The StatusBar class uses a derived version of this class, but thebulk of the drawing information is in the base class.. The Font and ForeColor prop-erties are set to the font information
Trang 14.3.2 A SSIGNING PANEL TEXT
With our panels defined, we simply set the Text property value for each panel tohave the text displayed by the application This only works for panels with their
Style property set to Text, of course We will look at our owner-drawn panel insection 4.4 Since our panels only have meaning after an image is loaded, we assigntheir values as part of the Click event handler for the Load button, as indicated bythe following steps
.NET Table 4.3 StatusBarPanel class
The StatusBarPanel class is a component that appears as a panel within a StatusBar trol This class is part of the System.Windows.Forms namespace, and inherits from the Sys- tem.ComponentModel.Component class A panel must be associated with a StatusBar
con-instance with its ShowPanels property set to true in order to appear on a form.
ToolTipText Gets or sets the tool tip for the panel.
Width Gets the current width or sets the default width
for the panel.
Public Methods
BeginInit Begins initialization of the panel when used
within a form or other component.
EndInit Ends initialization of the panel when used within
a form or other component.
S ET THE TEXT TO APPEAR IN THE PANELS
1 In the menuLoad_Click
method, set the ShowPanels
property to false while the image is loading.
private void menuLoad_Click (object sender, System.EventArgs e) {
try {
statusBar1.ShowPanels = false;
Trang 2Look again at the new try block.
try
{
statusBar1.ShowPanels = false;
statusBar1.Text = "Loading " + dlg.FileName;
pbxPhoto.Image = new Bitmap(dlg.OpenFile());
statusBar1.Text = "Loaded " + dlg.FileName;
Two items are worth noting in this code:
b The ShowPanels property is set to false while an image is loading so that the tusBar.Text property setting will appear, and set to true after the image is loadedand the panels are set
Sta-c The Format method used here is a static method provided by the String class forconstructing a string We could spend a chapter covering this and other features avail-able in C# strings generally and the NET System.String class specifically, butinstead will assume you can look this one up in the documentation In the code shownhere, the "{0:#} x {1:#}" string indicates that two parameters are required, both
of them integers
Build and run the application to see these panels in action Resize the window to seehow the panels react You will notice that the first panel resizes automatically alongwith the window, while the second two panels maintain their initial size This is con-sistent with the AutoSize settings we used for these objects
2 Initialize the sbpnlFileName
and sbpnlImageSize panels after the image is success- fully loaded.
statusBar1.Text = "Loading " + dlg.FileName;
pbxPhoto.Image = new Bitmap(dlg.OpenFile());
statusBar1.Text = "Loaded " + dlg.FileName;
this.sbpnlFileName.Text = dlg.FileName; this.sbpnlImageSize.Text
= String.Format("{0:#} x {1:#}", pbxPhoto.Image.Width, pbxPhoto.Image.Height);
3 Set the ShowPanels property
to true so the panel text will appear.
statusBar1.ShowPanels = true;
} }
S ET THE TEXT TO APPEAR IN THE PANELS (continued)
b
Disable the panels
c Create image size string
Trang 34.4 O WNER - DRAWN PANELS
So what about this owner-drawn panel? Text panels do not need to worry aboutdrawing their text onto the panel, as the NET Framework handles this internally.There are some cases where text just will not do, and these situations requiring man-ual drawing of the panel
Drawing of panels and other objects in NET are handled through use of the tem.Drawing namespace, sometimes referred to as GDI+ since it is based on anupdate to the graphical drawing interface provided by Microsoft Components such asmenus, status bars, and tabs that contain drawable components support a DrawItem
Sys-event that occurs when an item in the component should be drawn Controls derivedfrom the Control class provide a Paint event for this purpose Both types of drawingmake use of the Graphics class discussed in this section in order to draw the item.This section will examine how owner-drawn status bar panels are supported, anddraw the sbpnlImagePercent panel for our application A similar discussion wouldapply to owner-drawn menu items or other objects supporting the DrawItem event.The result of our changes is shown in figure 4.5
As you can see in the figure, when the image is displayed in Actual Size mode, thethird panel will show a numeric and visual representation of how much of the image
is displayed Before we draw this panel, let’s take a closer look at the DrawItem event
4.4.1 T HE D RAW I TEM EVENT
The DrawItem event is used by a number of classes to draw an item containedwithin some sort of larger collection For instance, the MenuItem, ListBox, and
ComboBox classes all include a DrawItem event for custom drawing of their tents These classes use the DrawItemEventArgs class to provide the data associ-ated with the event The StatusBar class uses a derived version of this class, but thebulk of the drawing information is in the base class An overview of this base class isprovided in NET Table 4.4
con-Figure 4.5 The third status bar panel here indicates that 30 percent of the image is visible in the window.
Trang 4For the StatusBar class, the StatusBarDrawItemEventArgs class derives fromthe DrawItemEventArgs class and is received by StatusBar.DrawItem eventhandlers The Panel property provided by this class is useful both for identifying thepanel and when the text assigned to the panel is needed.
When a DrawItem event handler is invoked, the default property values are whatyou might expect The Bounds property is set to the display rectangle of the panel todraw This rectangle is with respect to the rectangle for the containing status bar, sothe upper left corner of a panel’s bounds is not (0,0) The Font and ForeColor prop-erties are set to the font information for the StatusBar object; the Graphics prop-erty to an appropriate drawing object, the Index to the zero-based index number ofthe panel, and State is typically set to DrawItemState.None The DrawItem
event is called once for each panel drawn
.NET Table 4.4 DrawItemEventArgs class
The DrawItemEventArgs class is an event object used when handling DrawItem events in a number of classes This class is part of the System.Windows.Forms namespace, and inher- its from the System.EventArgs class Practically, this class is used to manually draw list box items, menu items, status bar panels and other objects.
The StatusBarDrawItemEventArgs class extends this class for use with StatusBar
objects This class includes a public Panel property to indicate which panel requires drawing.
Public Properties
Bounds Gets the Rectangle of the area to be drawn
with respect to the entire graphical area for the object.
Font Gets a suggested Font to use for any text
Typically, this is the parent’s Font property ForeColor Gets a suggested Color t o use for foreground
elements, such as text Typically, this is
Index Gets the index of the item to be painted The
exact meaning of this property depends on the object.
State Gets additional state information on the object,
using the DrawItemState enumeration Examples include whether the item is selected, enabled, has the focus, or is checked (for menus).
Trang 5.NET Table 4.5 System.Drawing namespace
The System.Drawing namespace provides access to basic graphics functionality provided by the graphical device interface (GDI+) The classes in this namespace are used when drawing
to any display device such as a screen or printer, and to represent drawing primitives such as rectangles and points.
Classes
Brush An abstract class representing an object used to fill the interior
of a graphical shape For example, the
Graphics.FillRectangle method uses a brush to fill a rectangular area on a drawing surface Classes derived from this class include the SolidBrush and TextureBrush classes Brushes A sealed class that provides Brush objects for all standard
colors For example, the Brushes.Red property can be used to fill shapes with a solid red color.
Font Represents a font that defines how text is drawn This includes
the font style and size as well as the font face.
Graphics Represents a GDI+ drawing surface Members are provided to
draw shapes, lines, images, and other objects onto the drawing surface.
Image An abstract class for image objects such as Bitmap Pen Represents an object used to draw lines and curves A pen can
draw a line in any color and specify various styles such as line widths, dash styles, and ending shapes (such as arrows) For example, the Graphics.DrawRectangle method uses a pen
to draw the outline of a rectangular area on a drawing surface Region Represents the interior of a graphics shape composed of
rectangles and paths.
SystemColors A sealed class that provides Color objects for the colors
configured in the local Windows operating system For example, the SystemColors.Control property returns the color configured for filling the surface of controls Similar classes also exist for Brush , Pen , and Icon objects based on the local system configuration.
Color Stores a color value A number of static colors are defined,
such as Color.Red , or a custom color can be created from an alpha component value and a set of RGB values.
Structures
Point A two-dimensional point as an integral x and y coordinate PointF A two-dimensional point as a floating point x and y coordinate Rectangle Stores the location and size of a rectangular region within a
two-dimensional area All coordinates are integral values Size Represents the size of a rectangular region as an integral width
and height.
SizeF Represents the size of a rectangular region as a floating point
width and height.
Trang 6A number of classes are available in the System.Drawing namespace for drawingstatus bar panels, menu items, and other objects An overview of this namespace isprovided in NET Table 4.5 Rather than provide detailed coverage of this namespace
in any one chapter of the book, we will visit members of this namespace as required
by our application In particular, we will use this namespace again in chapter 7 whendrawing on Form and Panel controls, and also in chapter 10 when discussingowner-drawn list boxes
4.4.2 D RAWING A PANEL
So let’s draw the panel in our application If you recall, we want this panel to showwhat percentage of the image is shown in the PictureBox control To do this, weneed to handle the DrawItem event We will build this code step by step The com-
plete code for the handler is shown following the table
Set the version number of the application to 4.4.
A DD D RAW I TEM H ANDLER
1 Handle the DrawItem event
for the StatusBar control in the MainForm.cs [Design]
2 In this handler, check that the
panel to draw is the
sbpnlImagePercent panel.
Note: This if statement is not strictly necessary Still, since the event relates to the entire status bar and not just this panel, this provides some robustness against future changes.
if (sbdevent.Panel == sbpnlImagePercent) {
// Calculate the percent of the image shown // Calculate the rectangle to fill
// Draw the rectangle in the panel // Draw the text on top of the rectangle }
}
Note: The four comments here are the four steps that
must be performed to draw the panel Each step is performed in the subsequent four steps of this table.
How-to
In the Properties window for the status bar, double-click the DrawItem entry.
Trang 7The complete code for this handler is shown as follows:
protected void statusBar1_DrawItem (object sender,
StatusBarDrawItemEventArgs sbdevent)
{
if (sbdevent.Panel == sbpnlImagePercent)
3 Calculate what percentage of
the image appears in the window.
// Calculate the percent of the image shown int percent = 100;
if (pbxPhoto.SizeMode != PictureBoxSizeMode.StretchImage) {
Rectangle dr = pbxPhoto.ClientRectangle; int imgWidth = pbxPhoto.Image.Width;
int imgHeight = pbxPhoto.Image.Height; percent = 100 * Math.Min(dr.Width, imgWidth)
* Math.Min(dr.Height, imgHeight) / (imgWidth * imgHeight);
prop-to a Brush object for all standard colors available in the framework.
6 Draw the percentage value in
the panel.
// Draw the text on top of the rectangle sbdevent.Graphics.DrawString(
percent.ToString() + "%", sbdevent.Font,
Brushes.White, sbdevent.Bounds);
Note: White is a good color choice if used with the
default desktop colors It may not be a good choice if custom desktop colors are used.
A DD D RAW I TEM H ANDLER (continued)
How-to
a If the SizeMode setting for the image is StretchIm- age , use 100% of the panel.
b Otherwise, divide the smaller of the display area and the image size by the total image area.
c For simplicity, use integer percent values.
How-to
Use the event’s Bounds
property and adjust its Width
based on the calculated percent.
How-to
Use the DrawString
method for the Graphics
object.
Trang 8// Calculate the percent of the image shown
int percent = 100;
if (pbxPhoto.SizeMode != PictureBoxSizeMode.StretchImage)
{
Rectangle dr = pbxPhoto.ClientRectangle;
int imgWidth = pbxPhoto.Image.Width;
int imgHeight = pbxPhoto.Image.Height;
percent = 100 * Math.Min(dr.Width, imgWidth)
* Math.Min(dr.Height, imgHeight) / (imgWidth * imgHeight);
}
// Calculate the rectangle to fill
Rectangle percentRect = sbdevent.Bounds;
percentRect.Width = sbdevent.Bounds.Width * percent / 100;
// Draw the rectangle in the panel
sbdevent.Graphics.FillRectangle(Brushes.SlateGray, percentRect); // Draw the text on top of the rectangle
we use the FillRectangle method, which requires a Brush object to use when
“painting” the rectangle In chapter 7, we will make additional use of this class See.NET Table 4.6 for an overview of some of the more interesting members of this class
It should be noted that the statusBar1_DrawItem handler is invoked eachtime a panel must be redrawn As a result, care should be taken in handlers such asthis to avoid expensive calculations or other operations that might adversely affect theperformance of the application For example, if we had generated a custom Brush
object while filling the rectangle here, such an operation would be performed eachtime the handler is invoked, potentially using an excessive amount of memory over thelife of the application Of course, our choice of the SlateGray color might not bethe best choice either, as it might interfere with colors the user has selected for theirdesktop A better option here might be to determine a color programmatically based
on the user’s desktop settings, and generate a single Brush object the first time theevent handler is invoked that is reused for the life of the application
You can compile and run this code so far if you like, but we do need to make onemore change When the PictureBox.SizeMode property is StretchImage, thecomplete image (100%) is always shown When SizeMode is set to Normal, theamount of image shown varies as the size of the client area changes As a result, whenthe user changes this setting, we need to make sure that our panel is redrawn by inval-idating the contents of the status bar
Trang 9.NET Table 4.6 Graphics class
The Graphics class is a drawing object that encapsulates a drawing surface , or more
specif-ically a graphical device interface (GDI+) drawing surface This class is part of the tem.Drawing namespace, and inherits from the System.MarshalByRefObject class
Sys-Drawing the outline of a shape typically requires a Pen object, while drawing a filled-in shape typically requires a Brush object.
This class contains a large number of members, but the list here should provide some idea of the supported functionality.
Public Static Properties
FromHdc Returns a Graphics instance from a given handle
to a device context.
FromHwnd Returns a Graphics instance from a given
window handle.
Public Properties
Clip Gets or sets as a Region object the portion of the
graphics area available for visible drawing DpiX Gets the horizontal resolution supported by the
object.
DpiY Gets the vertical resolution supported by the
object.
PageUnit Gets or sets the GraphicsUnit value specifying
the unit of measure for page coordinates SmoothingMode Gets or sets the SmoothingMode value indicating
how shapes are rendered with this object TextRenderingHint Gets or sets the TextRenderingHint value
indicating how text is rendered with this object.
circle) bounded by a given rectangle using a given
Pen DrawLine Draws a line using a given Pen DrawRectangle Draws the outline of a rectangle using a given Pen FillClosedCurve Fills the interior of a closed curve specified as an
array of points using a given Brush.
FillEllipse Fills the interior of an ellipse (which might be a
circle) bounded by a given rectangle using a given
Brush FillRectangle Fills the interior of a rectangle using a given
Brush MeasureString Returns the size a given string would occupy using
a given Font
Trang 10If you recall, our menus invoke the menuImage_ChildClick method to alter thedisplay mode by assigning a new SizeMode value.
Now the status bar will be redrawn whenever the SizeMode property is altered.Note that this change highlights another advantage of our decision in chapter 3 tohandle the Click of an Image submenu item with a shared handler If we decided to
add additional display modes in the future, this code will ensure that the status bar isredrawn correctly each time it changes
Compile and run your application to verify that the code works as expected play an image in both Stretch to Fit and Actual Size mode to see how the owner-drawnstatus bar panel behaves when the application is resized
This chapter introduced the StatusBar class and showed how both text and panelinformation are displayed in this control We looked at how to switch between thedisplay of text and panels in a status bar, and discussed how various properties can beused to alter the appearance and behavior of status bar panels
We also presented the base class of all Windows Forms controls by looking at the
Control class in some detail A discussion of owner-drawn panels and the use of the
DrawItem and Paint events led to a discussion of the System.Drawing namespace
in general, and the Graphics class in particular
The next chapter takes us out of the Windows Forms namespace briefly in order
to discuss reusable libraries
I NVALIDATE S TATUS B AR
7 Modify the
menuImage_Child-Click method to force a redraw
of the status bar.
protected void menuImage_ChildClick(object sender, System.EventArgs e)
{
if (sender is MenuItem) {
MenuItem mi = (MenuItem)sender;
nSelectedImageMode = mi.Index;
pbxPhoto.SizeMode = this.modeMenuArray[mi.Index];
pbxPhoto.Invalidate();
statusBar1.Invalidate();
} }
Trang 11•A title bar where the name and version number of the program are displayed.
•A menu bar where the user can access commands such as loading an image
•A main window that displays a single photo at a time (stretched and distorted,but displayed nonetheless)
•A status bar where information about the displayed photo appears
So now what? In this book, there are a number of features that still need to be ered Tool bars, dialog boxes, splitters, and printing, to name a few In order to do
Trang 12cov-this we will need more than a single photograph in our application If we can displayone, why not more than one Let’s display multiple photos We will call this, ofcourse, a photo album.
To keep this chapter somewhat manageable, we will not muck with our mainapplication window here We will focus instead on creating a photo album abstraction,and wait until chapter 6 to integrate it into our application Specifically, we will per-form the following tasks in this chapter:
•Create a PhotoAlbum class to represent a collection of photograph files
•Create a Photograph class to represent a single photograph
•Compile the PhotoAlbum and Photograph classes into an external library.Before we write any code for these classes, a short design discussion is in order
Within our application, we need to represent the album in a way that facilitates therequired actions, such as “add an image,” “move to the next photo,” and so forth Youmay immediately think of some sort of array, and this will be our approach This sec-tion will present a short design discussion as a way to introduce some terminology werequire and lay the groundwork for writing our code
Each photo is an image file located somewhere on disk While a simple list of filescould be stored in an array of strings, we should not be too hasty here Requirementschange, as do applications We may want to add additional features to our photoalbum later, so it makes sense to encapsulate our album in a class to make this possible.Classes in C# are very similar to classes in the C++ and Java languages We will create
a PhotoAlbum class to represent a single photo album, and provide a set of methodsthat external users of the class, such as our MyPhotos application, can use to retrieveand modify the contents of the album
What will our album contain? We already mentioned the idea of array file names.Since we would like to provide quick access to the images, we could also consider anarray of Bitmap objects Not a bad idea, except that a bitmap can be pretty large Afull color image such as a photograph uses 24 bits, or three bytes per pixel: one eachfor a red, blue, and green color Do the math and you’ll find that a 640×480 pixelimage takes up around 900K in memory, or almost 1 MB A system with 32 MB ofRAM will run out of memory fairly quickly, and even 128 or 256 MB systems willfeel the pinch Of course, virtual memory will allow us to use more than the availablephysical memory, but the performance will not make our users happy Instead of bit-maps, we will stick with the file names of our images, and create Bitmap objects asrequired To accommodate both types of information, and to extend this definition
in the future, we will create a Photograph class to encapsulate the concept of a singlephotograph Our album will contain zero or more photographs
One more feature here: once we build our PhotoAlbum and Photograph
classes, they could be useful in other programs that wish to use our concept of a photo
Trang 13album For example, a genealogy program for creating family trees might want to link
to a photo album of a specific person or family So we will place our new classes in alibrary that other programs can reuse In Windows parlance, such a library is called aDynamic Link Library, or DLL
5.1.1 I NTERFACES
As you might expect, the NET Framework provides a number of classes that canhelp us here These classes implement common data structures such as arrays, stacks,queues, and hash tables Before the ever-appropriate table summarizing such classes,this is a good place to introduce the idea of an interface
An interface is an abstraction of an abstraction, and should be familiar to
pro-grammers of COM or its UNIX ancestor, the distributed computing environment(DCE) While a class encapsulates a data structure and its operations, an interfaceencapsulates a type of data structure and its operations This is very similar to anabstract class, except that an interface does not provide any implementations for itsmembers, it just defines the properties, methods, and events that a class should imple-ment in order to support the interface In practice, an interface is a good way to encap-sulate a common idea for use by a number of possibly unrelated classes, while anabstract class is a good way to encapsulate a common idea for use by a number ofrelated classes
For example, the NET ICloneable interface defines a type of class that can becloned, or copied, from an existing class instance to a new one.1 This concept applies
to the Array, Brush, Font, String, and a number of other classes throughout the.NET Framework Languages such as C++ provide multiple inheritance for this type ofsupport In C++, ICloneable could be an abstract class and inherited where needed
In C# and Java, only single inheritance is supported, so this is not possible Instead,both languages provide interfaces as a way to encapsulate common functionality thatcan be used by a wide range of classes
For example, the Brush class supports the ICloneable interface We used thisabstract class in chapter 4 to create an owner-drawn status bar panel Brushobjectscan be cloned to create a new copy of an existing Brush You can create an instance
of a Brush, since it is a class, but you cannot create an instance of an ICloneable
except as a by-product of an existing class that happens to support this interface.The NET Framework provides interfaces for everything from enumeratingmembers of a set to transferring data between applications Some interfaces related toour current discussion on albums are listed in the following table
1 Generally speaking, cloning in NET always produces a deep copy of an object, as we saw for the menu
classes in chapter 3.
Trang 145.1.2 D ATA COLLECTION CLASSES
Looking over the interfaces in the table, the IList interface seems particularlyappropriate for the task at hand This allows elements to be added and removed fromthe collection, and supports array-style indexing Some of the data collection classes
in the NET Framework are shown in the following table Note, in particular, thoseclasses in the table that support the IList interface
Interfaces related to data collections
IEnumerable
Interface that supports the creation of
an enumerator class for iterating over the elements in a collection.
Usage
Supporting this interface allows the C#
foreach statement to be used with instances of a class or structure.
GetEnumerator method, which returns a class that supports the IEnumerator
Count property, to retrieve the number
of elements in the collection.
SyncRoot property, to retrieve an object for synchronizing multi-threaded access
Supporting this interface allows a class
or structure to be treated as an array
This permits objects to be used as targets of data bound controls, as discussed in chapter 17.
Item property, to support array-style indexing of elements using [brackets], much like a [] override in C++.
Add method, which adds a new element
Trang 15Since we do not have a database here, the DataView class is not appropriate If all wewanted was a collection of file names, the StringCollection class would work,but then our PhotoAlbum would not be very extensible This leaves us with a simplearray or the ArrayList or CollectionBase classes A simple fixed-size array isnot appropriate since we would like our album to grow dynamically So we are left tochoose between the ArrayList and CollectionBase classes.
Either class would work here, and both classes can be quite useful An overview
of the ArrayList class is shown in NET Table 5.1 Deriving our PhotoAlbum classfrom ArrayList would look like this:
// Deriving PhotoAlbum from ArrayList (not our approach)
public class PhotoAlbum : System.Collections.ArrayList
on object instances For example, the PhotoAlbum.Add method would have thefollowing signature:
Some NET classes related to data collections
Array The base class for all array objects
This class is abstract.
ICloneable, IList, tion, IEnumerable
ICollec-ArrayList A dynamically-sized array ICloneable, IList,
ICollection, IEnumerable
CollectionBase An abstract class for creating a
strongly typed collection.
IList, ICollection, ble
IEnumera-DataView A customized view of a database
table.
IList, ICollection, IEnumerable , and others Hashtable A collection of values stored based on
a hash code of the value, called a key.
ICloneable, ICollection,
IEnumerable , IDictionary, and
others Queue A FIFO queue; a first in, first out
collection of objects.
ICloneable, ICollection,
IEnumerable
SortedList A sorted collection of keys and values
accessible by both key and index.
Trang 16public int Add( object value );
So while this would be a very easy implementation, the methods in our PhotoAlbum
class would not be type-safe, and therefore not so robust
Let’s instead take a look at the CollectionBase class An overview of this class isshown in NET Table 5.2 This class is an abstract class, and requires derived classes
to implement the additional methods required to support the appropriate interfaces.This requires a little more work on our part, but creates a nicer interface that workswith Photograph objects directly
Before we create our implementation, note that an alternative implementationwould incorporate a private ArrayList object in a class derived directly from Sys- tem.Object This alternative would look something like the following:
// PhotoAlbum implementation with private ArrayList (not our approach) class PhotoAlbum
{
// internal (not inherited) ArrayList
.NET Table 5.1 ArrayList class
The ArrayList class is a collection of indexed objects where the number of objects can
change dynamically This class is part of the System.Collections namespace, and is very
similar to the Array class for fixed-length collections of objects The ArrayList class ports the ICloneable , IEnumerable , ICollection, and IList interfaces.
sup-Public Properties
Capacity Gets or sets the maximum number of objects the list can contain.Count Gets or sets the actual number of objects in the array.
Public Methods
Add Adds an object to the end of the array.
AddRange Adds the elements from an ICollection interface to the end of
the array.
Clear Removes all objects from the array.
Contains Determines if an object is in the array Comparison is done using
the Object.Equals method.
CopyTo Copies the ArrayList , or a portion of it, into a one-dimensional
Array object.
IndexOf Returns the zero-based index of the first occurrence of the given
object in the array, or – 1 if the object is not found Comparison is done using the Object.Equals method.
Remove Removes an object from the array.
RemoveAt Removes the object at a given index from the array.
Sort Sorts the array, using an IComparable interface to compare
objects.
TrimToSize Sets the capacity of the array to the actual number of objects in it.
Trang 17// Constructor and other wrappers
// Custom Add wrapper
public int Add(Photograph photo)
{
return _photoArray.Add(photo);
}
}
This would work just fine and be similar to our actual implementation derived from
CollectionBase Our implementation is more appropriate than this alternative,since the CollectionBase class is designed for just this purpose, and does in factprovide access to an ArrayList member through a protected property
.NET Table 5.2 CollectionBase class
The CollectionBase class is an abstract class for creating strongly typed collections A class is strongly typed if it only allows a specific type or types in its methods, rather than a
generic type such as an object Strongly typed classes allow the compiler to ensure that the proper objects are passed to methods in the class, and can prevent errors that would other- wise occur only at runtime.
The CollectionBase class is part of the System.Collections namespace It supports
the IEnumerable , ICollection, and IList interfaces A complete list of the public bers defined by this class is as follows Derived classes must implement the additional meth- ods to support the required interfaces.
mem-Public Properties
Count Gets or sets the actual number of objects in the array.
Public Methods
Clear Removes all objects from the array.
GetEnumerator Returns an enumerator that can iterate through the
elements in the collection using the IEnumerator
interface.
RemoveAt Removes the object at a given index from the array.
Protected Properties
InnerList Gets an ArrayList instance representing the collection
instance This can be used when implementing derived classes to modify the collection.
List Gets an IList instance representing the collection
instance This can be used when implementing derived classes to modify the collection.
Protected Methods
OnClear Performs additional custom processing before clearing the
contents of the collection This can be used by derived classes to perform any required actions before the collection is cleared.
OnInsert Performs additional custom processing before inserting an
element into a collection A number of other protected methods are provided, with a similar purpose.
Trang 185.2 C LASS LIBRARIES
Finally, we are ready to specify our album class We have decided to base this on lectionBase, and use our own Photograph object for the elements As we dis-cussed in the previous section, the CollectionBase class provides a limited set ofmethods, so it will be up to us to implement the appropriate class members to sup-port the required interfaces
Col-As a result, our PhotoAlbum class will look something like the following Sincethis is a photo album and we expect to display photos from it, we will also add somemethods to manage the current position within the album
public class PhotoAlbum : CollectionBase
{
// Default constructor
// The IEnumerable interface is provided by CollectionBase
// This allows the use of foreach with an album
// ICollection members
// IList members
// Position operations
// - Get/Set current position (as index).
// - Get photograph at current position.
// - Move to the next photograph.
// - Move to the previous photograph.
}
Some syntactic points here:
b As already mentioned, classes in C# support inheritance from a single class only, inthis case from the CollectionBase class, although multiple interfaces can be speci-fied This is the same as Java, and a break from C++ Also unlike the C++ language,C# classes do not support private or protected inheritance
c If you haven’t realized it by now, also note that there are no header files in C# LikeJava, the entire class is specified in a single file For C++ programmers, also note that
a semicolon (;) is not required after the class definition
The Photograph class will hold the original file name for the image, and the map object when necessary Its definition will look something like this:
public class Photograph
{
// Create a new instance from a file name.
// Properties:
// - get the file name for the Photograph
// - get the Bitmap for the Photograph
// Methods:
// - see if two Photographs are equal
}
b Inherit from CollectionBase class
c End of PhotoAlbum class
d Inherit from System.Object
Trang 19One additional point here:
d It is worth noting that all classes in C# implicitly inherit from the object class evenwhen it is not specified This ensures that all classes have a common ancestor So eventhough it is not shown, our Photograph class inherits from the base Sys- tem.Object class implicitly, which is equivalent to the C# object class
Now that we understand the framework for our classes, let’s perform the actualimplementation This section will create the class library in Visual Studio, discuss cre-ating such a library using the command line tools, and provide the initial implemen-tation of our PhotoAlbum and Photograph classes
5.2.1 C REATING THE CLASS LIBRARY
Enough preparation: time to create our library If you are not using Visual Studio.NET here, create your library as a separate directory and place the files discussedhere in it We’ll give you some hints for building this from the command line later inthe chapter
In this section we will create a new project as part of our MyPhotos solution Thisproject will build the new MyPhotoAlbum library We will create a top-levelnamespace called Manning for this project, and reference the new library from ourMyPhotos project
Set the version number of the application to 5.2.
C REATE A REUSABLE LIBRARY IN V ISUAL S TUDIO NET
Trang 20That’s all it takes The solution MyPhotos now contains two projects: a bum project to create a DLL library, and a MyPhotos project to create a WindowsForms application You will note that the new project has its own AssemblyInfo.cs file
MyPhotoAl-to support an independent version number for the library
2 Configure the new project
as a class library named
“MyPhotoAlbum.”
3 Click the OK button to
create the new project.
In the Solution Explorer window, the new project appears with
a default initial class named Class1 The main window displays the Class1.cs source file.
Note: The MyPhotos project is in bold to indicate that it
is the default project, or the startup project in Visual
Stu-dio NET terms.
C REATE A REUSABLE LIBRARY IN V ISUAL S TUDIO NET (continued)
How-to
a Select Visual C# Projects
as the Project Type.
b Select Class Library as the Template.
c Enter “MyPhotoAlbum”
for the name of the project.
Trang 21We do not want a class called Class1, so let’s rename it to PhotoAlbum We willalso adjust the version number of our new project to reflect the current section number.
Visual Studio automatically uses the project name as the namespace for all files in theproject Here, the PhotoAlbum class is in the MyPhotoAlbum namespace, so thatour class called PhotoAlbum will not interfere with anyone else who may have a classcalled PhotoAlbum By convention, namespaces should specify the company name,followed by the project name Since our library might be used outside of this book(hey, you never know!), we should follow this convention as well We will use thepublisher’s name Manning as our top-level namespace
R ENAME THE C LASS 1 CS CLASS FILE
Note: Your main window now displays two
Assembly-Info.cs tabs for the corresponding files in each project Make sure you keep track of which is which The dis- played file is always selected in the Solution Explorer win- dow, which identifies the project that contains the file To display the Solution Explorer window while editing a file, use the keyboard shortcut Ctrl+Alt+L.
5 Rename the Class1.cs file
name to PhotoAlbum.cs.
The Class1.cs tab in the main window is renamed as well.
6 Rename the Class1 class
name to PhotoAlbum
The PhotoAlbum.cs file should look like this:
using System;
namespace MyPhotoAlbum {
//
// TODO: Add Constructor Logic here //
} } }
Assembly-How-to
a Right-click on the Class1.cs file.
Trang 22Our library is now ready; all we need to do is add code One last task before we dothis is to make certain we can use our library from within the MyPhotos applicationproject For this to work, the MyPhotos project must include a reference to theMyPhotoAlbum class This corresponds to the /reference switch on the C# com-piler (csc.exe) that we saw in chapter 1, and is a bit like linking a library into yourprogram in C++ Since there are no header files in C#, a reference is all we need tostart using classes from the library in our project.
M
7 Modify the entire
MyPhotoAlbum namespace to exist within the Manning
namespace
How-to
Enter the bolded text into the PhotoAlbum.cs file When you type the final brace, Visual Studio will automatically reformat the lines as shown.
Note: We have not made a
sim-ilar change in the MyPhotos application since in this project the namespace is not likely to
be used outside of the tion itself.
applica-The PhotoAlbum.cs file should now look as follows: using System;
namespace Manning {
namespace MyPhotoAlbum {
//
// TODO: Add Constructor Logic here //
} } }
}
R EFERENCE M Y P HOTO A LBUM FROM THE M Y P HOTOS PROJECT
8 Display the Add Reference
dialog box for the MyPhotos project.
Alternately
Right-click on the References entry under the MyPhotos project in the Solution Explorer window, and select Add Reference.
How-to
a Click the MyPhotos project in the Solution Explorer window.
b Click on the Project menu.
c Select the Add Reference item.
Trang 23It is important to realize that our new reference refers to the assembly produced bythe MyPhotoAlbum project, and not the project itself Visual Studio automaticallyuses the correct path when compiling the MyPhotos project to pick up the mostrecent MyPhotoAlbum library from the corresponding project.
If you are not using Visual Studio NET to build your program, you will need
to establish the correct library location manually The command-line tools discussed
in chapter 1 are used for this purpose The next section provides a short discussion onthis topic
5.2.2 U SING THE COMMAND - LINE TOOLS
As we saw in chapter 1, you can build Windows Forms applications without usingVisual Studio NET The interactive environment makes a number of tasks easier, butalso uses memory and other system resources On a computer with limited resources,this can present some problems If you have a favorite editor and are comfortableworking with makefiles, you can create the examples in this book without usingVisual Studio NET
To create a class library such as MyPhotoAlbum.dll, create a MyPhotoAlbumdirectory for the library and place the required source files in it In this case you wouldcreate a PhotoAlbum.cs file to hold the PhotoAlbum class source code, and create otherfiles as required You can create an AssemblyInfo.cs file as well, or simply include theversion number and other assembly information at the top of your file as we did in chap-ter 1 The C# compiler (csc.exe) discussed in chapter 1 is used to produce both execut-ables and libraries The /target switch specifies the type of output file to produce
a Click the Projects tab.
b Click the MyPhotoAlbum project.
c Click the Select button.
d Click OK to add the selected project.
Trang 24The /out switch can be used to specify the output file name Both /out and /target
must appear before any source file names
For example, the following line will create a library assembly called bum.dll using a single source file PhotoAlbum.cs
MyPhotoAl-> csc /target:library /out:MyPhotoAlbum.dll PhotoAlbum.cs
/r:System.dll
To use this library with your MyPhotos application, you will need to include a /r erence when compiling the application For example, if your library was in a directorycalled C:\MyProjects\MyPhotoAlbum, then you would use the following switchwhen compiling the MyPhotos application:
/r:C:\MyProjects\MyPhotoAlbum
5.2.3 C REATING THE P HOTO A LBUM CLASS
No matter how you compile your library, we are now ready to implement the toAlbum class These next two sections take us through the initial implementation ofthis and the Photograph class If you find typing all this code a bit tedious (or are areally bad typist!), don’t be afraid to download the final code from the book’s web siteand simply read the accompanying text For the rest of us, let’s forge ahead
Pho-C# compiler output options (/target switch)
/target:exe Creates a console
application (.exe).
This is the default.
/target:library Creates a library file (.dll) The library generated is an assembly that can be
referenced by other NET applications
/target:module Creates a library module
(.dll).
This option does not produce an assembly manifest for the file Such a file cannot be loaded by the NET runtime until it is incorporated in an assembly manifest using the /addmodule switch This permits collections of files to become a single assembly
/target:winexe Creates a Windows
application (.exe).
When a Windows application is run in a console window, the console does not wait for the application to exit This is different than a console application, where the console does in fact wait.
I MPLEMENT P HOTO A LBUM CLASS
1 Display the PhotoAlbum.cs
file in the main window.
2 Add some class
Trang 25You may notice here that the MyPhotoAlbum project does not compile Try to do soand the compiler returns an error something like the following:
This is because CollectionBase is part of the System.Collections namespace
It turns out this namespace is part of the system library, so there is no need for anotherreference in our project We could fix the error by declaring the class as follows:
public PhotoAlbum : System.Collections.CollectionBase
{
Since we may use other objects or names from the System.Collections
namespace, we will instead simply indicate that our class will use this namespace atthe top of the file
Now the project should compile with no errors Before we implement any membersfor this class, let’s also take a look at the Photograph class
3 Define CollectionBase
as the base class.
public class PhotoAlbum : CollectionBase
{
4 Create an empty default
constructor.
public PhotoAlbum() {
// Nothing to do }
Note: It’s a good idea to add a short comment in
situa-tions like this to inform the poor guy or gal who ally supports your code that you created an empty constructor on purpose.
eventu-I MPLEMENT P HOTO A LBUM CLASS (continued)
Error The type or namespace name 'CollectionBase' could not be
found (are you missing a using directive or an assembly erence?)
ref-U SE S YSTEM C OLLECTIONS N AMESPACE
5 Add a using directive to
the PhotoAlbum.cs file for the System.Collections
namespace.
You should now have two using directives present:
using System;
using System.Collections;