- •brief contents
- •about this book
- •The Windows Forms namespace
- •Part 1: Hello Windows Forms
- •Part 2: Basic Windows Forms
- •Part 3: Advanced Windows Forms
- •Who should read this book?
- •Conventions
- •Action
- •Result
- •Source code downloads
- •Author online
- •acknowledgments
- •about .NET
- •Casting the .NET
- •Windows Forms overview
- •about the cover illustration
- •Hello Windows Forms
- •1.1 Programming in C#
- •1.1.1 Namespaces and classes
- •1.1.2 Constructors and methods
- •1.1.3 C# types
- •1.1.4 The entry point
- •1.1.5 The Application class
- •1.1.6 Program execution
- •1.2 Adding controls
- •1.2.1 Shortcuts and fully qualified names
- •1.2.2 Fields and properties
- •1.2.3 The Controls property
- •1.3 Loading files
- •1.3.1 Events
- •1.3.2 The OpenFileDialog class
- •1.3.3 Bitmap images
- •1.4 Resizing forms
- •1.4.1 Desktop layout properties
- •1.4.2 The Anchor property
- •1.4.3 The Dock property
- •1.5 Recap
- •2.1 Programming with Visual Studio .NET
- •2.1.1 Creating a project
- •Action
- •Result
- •2.1.2 Executing a program
- •Action
- •Result
- •2.1.3 Viewing the source code
- •View the code generated by Visual Studio .NET
- •Action
- •Result
- •2.2 Adding controls
- •2.2.1 The AssemblyInfo file
- •Action
- •Results
- •2.2.2 Renaming a form
- •Action
- •Result
- •2.2.3 The Toolbox window
- •Action
- •Result
- •2.3 Loading files
- •2.3.1 Event handlers in Visual Studio .NET
- •Action
- •Result
- •2.3.2 Exception handling
- •Action
- •Result
- •Action
- •Results and Comments
- •2.4 Resizing forms
- •2.4.1 Assign the Anchor property
- •Action
- •Result
- •2.4.2 Assign the MinimumSize property
- •Action
- •Result
- •2.5 Recap
- •Basic Windows Forms
- •Menus
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •3.3 Click events
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •3.5 Context menus
- •Action
- •Result
- •Action
- •Result
- •3.6 Recap
- •Status bars
- •4.1 The Control class
- •4.2 The StatusBar class
- •Action
- •Result
- •Action
- •Result
- •4.3.1 Adding panels to a status bar
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •4.5 Recap
- •Reusable libraries
- •5.1 C# classes and interfaces
- •5.2 Class libraries
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •5.3 Interfaces revisited
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •5.4 Robustness issues
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Common file dialogs
- •Action
- •Results
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •6.3 Paint events
- •Action
- •Result
- •Action
- •Result
- •6.4 Context menus revisited
- •Action
- •Result
- •Action
- •Result
- •6.5 Files and paths
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •6.6 Save file dialogs
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •6.7 Open file dialogs
- •Action
- •Result
- •Action
- •Result
- •6.8 Recap
- •Drawing and scrolling
- •7.1 Form class hierarchy
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •7.4 Panels
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Dialog boxes
- •8.1 Message boxes
- •Action
- •Result
- •Action
- •Result
- •8.1.4 Creating A YesNoCancel dialog
- •Action
- •Result
- •Action
- •Result
- •8.2 The Form.Close method
- •8.2.1 The relationship between Close and Dispose
- •Action
- •Result
- •8.3 Modal dialog boxes
- •Action
- •Result
- •Action
- •Result
- •8.3.2 Preserving caption values
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Basic controls
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •9.1.2 Creating a derived form
- •Action
- •Result
- •9.2 Labels and text boxes
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •9.3.6 Adding AlbumEditDlg to our main form
- •Action
- •Result
- •Action
- •Result
- •9.4 Recap
- •List controls
- •10.1 List boxes
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •10.2 Multiselection list boxes
- •10.2.1 Enabling multiple selection
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •10.3 Combo boxes
- •Action
- •Result
- •Action
- •Result
- •10.4 Combo box edits
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •10.5 Owner-drawn lists
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •More controls
- •Action
- •Result
- •Action
- •Result
- •11.2 Tab pages
- •Action
- •Result
- •Action
- •Result
- •11.3.1 Dates and times
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •11.5 Recap
- •A .NET assortment
- •12.1 Keyboard events
- •Action
- •Result
- •Action
- •Result
- •12.2 Mouse events
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •12.3 Image buttons
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •12.4 Icons
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •12.5 Recap
- •Toolbars and tips
- •13.1 Toolbars
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •13.4.2 Creating tool tips
- •Action
- •Result
- •Action
- •Result
- •Advanced Windows Forms
- •List views
- •14.2 The ListView class
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •14.2.3 Populating a ListView
- •Action
- •Result
- •Action
- •14.3 ListView columns
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •14.6 Recap
- •Tree views
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •15.3 Dynamic tree nodes
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •15.4 Node selection
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •15.5 Fun with tree views
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Multiple document interfaces
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •16.3 Merged menus
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •16.4 MDI children
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •16.5 MDI child window management
- •Action
- •Result
- •Action
- •Result
- •16.6 Recap
- •Data binding
- •17.1 Data grids
- •Action
- •Result
- •Action
- •Result
- •17.2 Data grid customization
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Odds and ends .NET
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •18.2 Timers
- •Action
- •Result
- •Action
- •Result
- •18.3 Drag and drop
- •Action
- •Result
- •Action
- •Result
- •18.4 ActiveX controls
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •Action
- •Result
- •18.5 Recap
- •C# primer
- •A.1 C# programs
- •A.1.1 Assemblies
- •A.1.2 Namespaces
- •A.2 Types
- •A.2.1 Classes
- •A.2.2 Structures
- •A.2.3 Interfaces
- •A.2.4 Enumerations
- •A.2.5 Delegates
- •A.3 Language elements
- •A.3.1 Built-in types
- •A.3.2 Operators
- •A.3.3 Keywords
- •A.4 Special features
- •A.4.1 Exceptions
- •A.4.2 Arrays
- •A.4.3 Main
- •A.4.4 Boxing
- •A.4.5 Documentation
- •.NET namespaces
- •B.1 System.Collections
- •B.2 System.ComponentModel
- •B.3 System.Data
- •B.4 System.Drawing
- •B.5 System.Globalization
- •B.6 System.IO
- •B.7 System.Net
- •B.8 System.Reflection
- •B.9 System.Resources
- •B.10 System.Security
- •B.11 System.Threading
- •B.12 System.Web
- •B.13 System.Windows.Forms
- •B.14 System.XML
- •Visual index
- •C.1 Objects
- •C.2 Marshal by reference objects
- •C.3 Components
- •C.4 Common dialogs
- •C.7 Event data
- •C.8 Enumerations
- •For more information
- •bibliography
- •Symbols
- •Index
.NET Table 5.3 Object class
The Object class is the base class for all objects in C#, including the built-in types such as int and bool, and is part of the System namespace. The System.Object class is equivalent to the C# language object class in the .NET Framework.
Public Static |
Equals |
Determines if two objects are equal. |
|
ReferenceEquals |
Determines if two objects both refer to the same |
||
Methods |
|||
|
|
object instance. |
|
|
|
|
|
|
Equals |
Determines whether a given object is the same as |
|
|
|
this object. Performs bitwise equality for value |
|
|
|
types, and object equality for reference types. |
|
|
GetHashCode |
Returns an integer suitable for use as a hash code |
|
|
|
for the object. Objects which are equal (based on |
|
|
|
the Equals method) return the same value, so you |
|
Public Methods |
|
should override this method if you override Equals. |
|
|
|
||
|
GetType |
Returns the Type object representing the C# |
|
|
|
language metadata associated with the object. |
|
|
ToString |
Returns a string that represents the current |
|
|
|
object. By default, the name of the object’s type is |
|
|
|
returned, so classes should normally override this |
|
|
|
method to return a more useful value. |
|
|
|
|
In order to ensure that Photographs compare as expected, we must override the Equals method. Our override will return true if the two photos refer to the same file.
|
|
OVERRIDE EQUALS METHOD |
|
|
|
|
Action |
Result |
|
|
|
1 |
In the Photograph.cs file, |
public override bool Equals(object obj) |
|
provide an override of the |
{ |
|
Equals method that |
if (obj is Photograph) |
|
{ |
|
|
compares file names. |
|
|
Photograph p = (Photograph)obj; |
|
|
|
return (_fileName.ToLower(). |
|
|
Equals(p.FileName.ToLower())); |
|
|
} |
|
|
return false; |
|
|
} |
|
|
|
Some features of this code are worth noting in more detail:
1In C#, the override keyword is required to override a virtual method. Using the virtual keyword here would cause a compile error, since the method name is already declared in the base class. The override keyword indicates that the
Equals method here serves the same purpose as the inherited member and replaces this base member. To define a new meaning for an inherited member and hide the original definition, the new modifier is used instead of the override keyword.
ROBUSTNESS ISSUES |
155 |
public override bool Equals(object obj)
{
2Since we must handle any object here, we only perform our comparison if the given object is a Photograph. We use the is keyword for this purpose, even though this results in the performance of two cast operations—one for the is keyword, and one for the actual cast.
if (obj is Photograph)
{
Photograph p = (Photograph)obj;
3The String.Equals method performs a case-sensitive comparison of strings. That is, “book” and “book” are equal, but “book” and “Book” are not. To ignore capitalization in our file name strings, we use the ToLower method to make sure the compared strings are all lower case.
return (_fileName.ToLower().Equals(p.FileName.ToLower()));
}
4 Note how false will always be returned if the given object is not a Photograph.
return false;
}
It is also worth noting here that the String class overrides the Equals method to perform a value-based case-sensitive comparison of its contents, even though it is a reference type. This ensures that two String objects are identical as long as they contain the same set of characters in the same order.
We should also override the GetHashCode and ToString methods in our Photograph class. The default GetHashCode implementation for the Object class returns different hash values for different references, while the default ToString implementation returns the name of the type, in this case the string "Photograph". Neither of these implementations really works for our purposes.
This is especially true for the GetHashCode method. This method should return an identical value for identical, or equal, objects. The default implementation for reference types works fine when two physically different objects are never equal. In our case, since two different photographs can now be equal, this means that two Photograph objects that refer to the same file name might return different hash values.3 This would make it rather difficult to look up Photograph objects in a hash table. As a
3This discussion assumes you understand hashing and hash tables. Briefly, a standard hash table uses a key, or hash code, as an index into a table. Unlike an array, this key does not have to be unique for each object, since each entry in the table refers to a linked list of objects that hash to the same key. A hash table enjoys the benefits of a linked list in that items can be quickly inserted and removed, and the benefits of an array since items can be quickly located. Of course, it all depends on a table appropriate for the number of expected items, and a good hash code algorithm that produces an equal distribution of values for the stored data across the entire table.
156 |
CHAPTER 5 REUSABLE LIBRARIES |
rule, you should always (yes, always!) override GetHashCode if you are overriding the Equals method. In our case, the comparison in Equals is based on the file name string, so we can use the String.GetHashCode method in a similar fashion.
OVERRIDE THE GETHASHCODE METHOD
|
Action |
Result |
|
|
|
2 |
Override the GetHashCode method. |
public override int GetHashCode() |
|
How-to |
{ |
|
return this.FileName.GetHashCode(); |
|
|
Use the String.GetHashCode |
} |
|
method on the contained file name. |
|
|
|
|
Finally, we may as well override the ToString method here as well. The default implementation will return the string "Photograph" every time, which is not very illuminating. A better implementation for our purposes would return the file name associated with the photograph, which is what we will do here.
OVERRIDE THE TOSTRING METHOD
|
Action |
Result |
|
|
|
3 |
Override the ToString method to |
public override string ToString() |
|
return the contained file name. |
{ |
|
|
return this.FileName; |
|
|
} |
|
|
|
Compile the code to verify that you and I have not made any errors. These overrides of the base Object methods will come in useful in future chapters. Since they are found in every object, Windows Forms controls make use of these methods whenever an object must be compared with another object or a corresponding string displayed in a window. In particular, we will see in chapter 10 how list controls utilize the ToString method by default when displaying an object in a list. As a result, providing a reasonable ToString implementation for your classes is always a good idea.
The changes in this section ensure that the base object methods are properly implemented for our Photograph class. Another change we should make is to ensure that any system resources used by our classes are cleaned up as required.
5.4.3DISPOSING OF RESOURCES
Our PhotoAlbum and Photograph classes are now fairly well-defined. We can create photographs from image files, add and remove photos to albums, and iterate through the contents of an album. A topic we haven’t touched on is the issue of cleaning up a photo or album when we are finished.
You might be wondering why we even care. Isn’t this the purpose of garbage collection? When we are finished with an album, the garbage collector will clean it up eventually, so we do not need to worry about it.
ROBUSTNESS ISSUES |
157 |
This is true to a point. The problem is that we have no idea when the garbage collector will run. It could be immediately, it could be hours later, or it could even be in conjunction with the program exiting. This is fine for the memory used by our objects, but might present a problem for the system resources in use. For example, the creation of a Bitmap object requires that a file be opened and loaded into memory. This requires file and other system resources. Since such resources can be limited, it is a good idea to release them when you are finished.
The preferred method for doing this is through a Dispose method as part of the IDisposable interface. This interface is summarized in .NET Table 5.4. Since the Component class supports the IDisposable interface and is the basis for most classes in the System.Windows.Forms namespace, most objects in the Windows Forms namespace provide a Dispose method for just this purpose.
.NET Table 5.4 IDisposable interface
The IDisposable interface indicates that an object can be disposed of. Typically, instances of objects that support this interface should always call the Dispose method to free any nonmemory resources before the last reference to the object is discarded. This interface is part of the System namespace.
Public Methods |
Dispose |
Releases any resources used by the object. |
|
|
|
Let’s support this interface in our classes. In many cases, it is an error to reference a disposed object. In our case, we would like to be able to clear and reuse a
bum instance, so we will leave the album object in a usable state after the Dispose method has been called.
SUPPORT THE IDISPOSABLE INTERFACE
|
Action |
Result |
|
|
|
1 |
In the Photograph.cs source file, |
public class Photograph : IDisposable |
|
indicate that this class will support |
{ |
|
the IDisposable interface. |
. . . |
|
|
|
|
|
|
2 |
Implement the Dispose method. |
public void Dispose() |
|
How-to |
{ |
|
if (_bitmap != null |
|
|
Dispose of the contained bitmap |
&& _bitmap != InvalidPhotoImage) |
|
only if it exists and is not our static |
{ |
|
InvalidPhotoImage bitmap. |
_bitmap.Dispose(); |
|
} |
|
|
|
|
|
|
_bitmap = null; |
|
|
} |
|
|
. . . |
|
|
} |
|
|
|
3 |
Similarly, support the IDisposable |
public class PhotoAlbum : |
|
interface in the PhotoAlbum.cs file. |
CollectionBase, IDisposable |
|
|
{ |
|
|
. . . |
|
|
|
158 |
CHAPTER 5 REUSABLE LIBRARIES |