
- •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
5.4ROBUSTNESS ISSUES
While our classes are basically ready, there are some additional issues that will affect the robustness of our application in future chapters. This section will address a number of these issues in order to make our library a bit more sturdy. These topics apply more generally to any class library, so are probably worth considering while developing your own libraries as well.
This section will look at the following areas:
•Handling the potential exception when a bitmap is created.
•Ensuring that photographs are compared as expected.
•Cleaning up system resources used by our classes.
•Associating a file name with an album.
We will examine each issue separately.
5.4.1HANDLING AN INVALID BITMAP
We discussed the concept of exceptions in chapter 2. Here, there is a potential exception when we create our Bitmap object for the Image property. Look back at our definition of this property.
public Bitmap Image
{
get
{
if (_bitmap == null)
{
_bitmap = new Bitmap(_fileName);
}
return _bitmap;
}
}
If the file is an invalid bitmap, or cannot be loaded for some reason, this presents a real problem. On the one hand, this is an error, so perhaps we should return null or allow the exception to be thrown. On the other hand, the caller is expecting to display a Bitmap, and checking for null or an exception every time seems a bit cumbersome, not to mention the issue of what the caller should then display in lieu of a Bitmap object.
As an alternative approach, we will instead create a special bitmap to return whenever the file cannot be loaded. This provides a Bitmap that the caller can display in any situation, but still indicates that something is wrong. We will create a private static member of our Photograph class to hold this special image, and provide a new property to indicate if a valid image for the current Photograph exists.
Let’s see how this looks.
ROBUSTNESS ISSUES |
151 |
Set the version number of the MyPhotoAlbum library to 5.4.
|
|
HANDLE THE BITMAP EXCEPTION |
|
|
|
|
|
|
Action |
|
Result |
|
|
|
|
1 |
In the Photograph.cs file, |
|
private static Bitmap _invalidImageBitmap |
|
create a private static |
|
= null; |
|
member to hold a Bitmap |
|
|
|
object. |
|
|
|
|
|
|
2 |
Add a public property to |
|
public static Bitmap InvalidPhotoImage |
|
retrieve this bitmap. |
|
{ |
|
How-to |
|
get |
|
|
{ |
|
|
Create a 100×100 pixel |
|
if (_invalidImageBitmap == null) |
|
image that contains a red X |
|
{ |
|
|
// Create the "bad photo" bitmap |
|
|
to indicate it is invalid. |
|
|
|
|
Bitmap bm = new Bitmap(100, 100); |
|
|
|
|
Graphics g = Graphics.FromImage(bm); |
|
|
|
g.Clear(Color.WhiteSmoke); |
|
|
|
// Draw a red X |
|
|
|
Pen p = new Pen(Color.Red, 5); |
|
|
|
g.DrawLine(p, 0, 0, 100, 100); |
|
|
|
g.DrawLine(p, 100, 0, 0, 100); |
|
|
|
_invalidImageBitmap = bm; |
|
|
|
} |
|
|
|
return _invalidImageBitmap; |
|
|
|
} |
|
|
|
} |
|
|
|
|
3 |
Use this new property as |
|
public Bitmap Image |
|
the bitmap to return if the |
|
{ |
|
image file cannot be |
|
get |
|
|
{ |
|
|
loaded. |
|
|
|
|
if (_bitmap == null) |
|
|
|
|
{ |
|
|
|
try |
|
|
|
{ |
|
|
|
_bitmap = new Bitmap(_fileName); |
|
|
|
} |
|
|
|
catch |
|
|
|
{ |
|
|
|
_bitmap = InvalidPhotoImage; |
|
|
|
} |
|
|
|
} |
|
|
|
return _bitmap; |
|
|
|
} |
|
|
|
} |
|
|
|
|
4 |
Also add a new property |
|
public bool IsImageValid |
|
IsImageValid to identify |
|
{ |
|
if a valid image file is |
|
get |
|
|
{ |
|
|
present. |
|
|
|
|
return (_bitmap != InvalidPhotoImage); |
|
|
|
|
} |
|
|
|
} |
|
|
|
|
There is quite a bit of new code here (at least, by our standards in this chapter), so let’s take a look at some of the more important pieces. First, look at the
PhotoImage property.
152 |
CHAPTER 5 REUSABLE LIBRARIES |

public static Bitmap InvalidImageBitmap
{
get
{
if (_invalidImageBitmap == null)
{
// Create the "bad photo" bitmap Bitmap bm = new Bitmap(100, 100);
Graphics g = Graphics.FromImage(bm); g.Clear(Color.WhiteSmoke);
b Create the new bitmap
c Construct Graphics object
Pen p = new Pen(Color.Red, 5); |
|
d Draw a red X |
|||
|
|||||
g.DrawLine(p, |
0, 0, |
100, 100); |
|
|
|
g.DrawLine(p, |
100, 0, |
0, 100); |
|
|
|
_invalidImageBitmap |
= |
bm; |
|
|
}
return _invalidImageBitmap;
}
}
The get implementation shown here creates and initializes the _invalidImageBitmap object the first time the property is invoked.
b First, a new Bitmap of size 100×100 pixels is constructed.
cNext, a Graphics object is generated to treat the Bitmap as a drawing surface using the static Graphics.FromImage method.
dFinally, a new red Pen is constructed with a width of five pixels and two lines are drawn corner to corner on the bitmap image to create a big red X. The Pen class is part of the System.Drawing namespace discussed in chapter 4. We could have used the Pen object returned by the Red property of the Pens class. This pen has a width of one pixel, so we opted to create our own pen instead.
Since the _invalidImageBitmap member variable is static, this code is executed the first time the property is called, and the image is then re-used as needed for all
PhotoAlbum objects in the application. In the Photograph.Image property, an exception raised while creating the bitmap is caught and the field is set to our invalid image.
try
{
_bitmap = new Bitmap(_fileName);
}
catch
{
_bitmap = InvalidImageBitmap;
}
Notice how an exception class is not specified in the catch clause. This ensures that all exceptions will be caught regardless of their origin.
ROBUSTNESS ISSUES |
153 |
Finally, a new IsImageValid property compares the photo’s bitmap to the static invalid image variable to see if they are equal. If they are, then the original photo is not a valid photograph.
public bool IsImageValid
{
get
{
return (_bitmap != InvalidPhotoImage);
}
}
Interestingly enough, if neither the _bitmap nor the _invalidImageBitmap variables has been initialized, then this comparison will generate both Bitmap objects in order to compare them.
This handles any possible exception our code might encounter when creating a bitmap from a given file name. One other subtle but very important change we need to make is how Photograph objects are compared. We will take this up next.
5.4.2OVERRIDING METHODS IN THE OBJECT CLASS
As we have repeatedly indicated, all classes in C# implicitly inherit from the object class, which is the same as System.Object class. In this section we look at the Object class in some detail, and override some of the methods inherited from this class in our Photograph class.
You may wonder why there is both an object and an Object, and the answer is both simple and confusing. The object class is part of the C# language definition, and all types, be they built-in or specific to your program, ultimately inherit from object.
Separate from the language definition is the .NET Framework, containing classes
and namespaces used to generate programs and services of every kind. Within the
.NET Framework is the System.Object class. In Microsoft’s C# compiler, the System.Object class is equivalent to the C# object class. So object and Object are different but functionally equivalent. In this book, we have used and will continue to use both classes interchangeably, with a preference toward the language-specific object. An overview of the Object class is shown in .NET Table 5.3.
Note that a similar discussion applies to the classes string and System.String as well.
Look closely at the Equals method in the table. In our Photograph class, we would like two Photographs to be equal if they represent the same file. So far, however, this will not be the case. Since Photograph is a reference type, two objects will be equal only if they refer to the same physical storage on the heap. It doesn’t matter if both objects internally represent the same image file. If they are different references, they are not equal. This behavior should come as no surprise to the seasoned Java coders among us, but might seem a little strange to programmers accustomed to C++ or Visual Basic behavior.
154 |
CHAPTER 5 REUSABLE LIBRARIES |