- •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
8.2THE FORM.CLOSE METHOD
In this section we pick up the thread of our previous discussion on the CloseCurrentAlbum method by discussing the Close and Dispose methods. You may think this is a little off-topic from dialog boxes, but in fact it is quite relevant. One of the key issues for C# programming in .NET is when to call the Dispose method to clean up window handlers and other nonmemory resources. This section will discuss this topic as it relates to dialog boxes, and introduce the Closing event as a way to intercept a user’s request to close a form.
8.2.1The relationship between Close and Dispose
Before we return to the topic of calling CloseCurrentAlbum when our application exits, let’s look at the relationship between Close and Dispose in .NET. It’s actually quite simple: they are the same. For all classes in the .NET Framework, a call to Close is equivalent to calling the Dispose method, and a call to Dispose is equiv-
alent to calling the Close method. The term “close” traditionally applies to objects like files and windows, and .NET has preserved this terminology. When you are finished with a form or a file, it seems silly to require a call to both Close and Dispose, so it makes sense to merge these two concepts together. The .NET design team could have chosen to use a common name for all classes, but programmers naturally expect to close objects such as forms and files, and closing objects like arrays or drawing objects seems a bit odd. Instead, the designers chose to use both methods and define them to be equivalent.
For Form objects, the behavior of the form itself varies depending on whether the object is displayed as a modal or modeless window. For a modeless window, displayed with the Form.Show method, the nonmemory resources are automatically cleaned up when the form is closed. This makes life much easier for us programmers, since we do not have to remember anything in this case. You cannot use a modeless Form after it is closed since all of its resources are gone. The Hide method should be used if you simply want to remove a Form from the desktop and display it later via the Show method. We will see this in chapter 13 when we use a tool bar button to hide the modeless dialog created in section 8.4 of this chapter.
For modal windows, displayed with the Form.ShowDialog method, there is a problem in that the dialog is typically accessed after the window disappears. As a result, a modal dialog must call Dispose explicitly to release its nonmemory resources. Typically, a modal dialog is created and destroyed in the same block of code. For example:
{
MyModalDialog dlg = new MyModalDialog();
// Initialize any dlg settings
if (dlg.ShowDialog() == DialogResult.OK)
{
// Use dlg settings to do something
THE FORM.CLOSE METHOD |
233 |
}
dlg.Dispose()
}
In this code, if the resources for the dlg variable disappeared after the ShowDialog method returned, you could not access any of its settings. For this reason, .NET only calls the Hide method after a user responds to a modal dialog, so that the dialog settings may still be accessed. This can be a little confusing since we still say the user closes the dialog, even though the dialog’s Close method is not actually called.
Fortunately, modal dialog boxes tend to have deterministic scope, meaning that you can predict when the dialog will be created and destroyed. The application waits until the user responds to a modal dialog, so it’s clear where the Dispose method must be called. We have already seen this method used with OpenFileDialog and SaveFileDialog objects in chapter 6, both of which are modal dialogs.
The C# language provides a using statement to call Dispose on our behalf in deterministic situations such as this. We have seen how the using directive defines an alias or shortcut for an object or members of a namespace. The using statement defines the scope in which a given object should exist. The syntax is as follows:
using (object)
{
// Do something with object
}
At the end of the block of code associated with the statement, the identified object is automatically disposed. For example, the previous code for the
object can be written as follows to cause Dispose to be called automatically at the end of the block:
{
using (MyModalDialog dlg = new MyModalDialog)
{
// Initialize any dlg settings
if (dlg.ShowDialog() == DialogResult.OK)
{
// Use dlg settings to do something
}
}
}
As another example, here is how our menuSaveAs_Click handler looks with this statement. The changes from our current implementation are shown in bold.
private void menuSaveAs_Click(object sender, System.EventArgs e)
{
using (SaveFileDialog dlg = new SaveFileDialog())
{
234 |
CHAPTER 8 DIALOG BOXES |
dlg.Title = "Save Album"; dlg.DefaultExt = "abm";
dlg.Filter = "abm files (*.abm)|*.abm"; dlg.InitialDirectory = PhotoAlbum.DefaultDir; dlg.RestoreDirectory = true;
if (dlg.ShowDialog() == DialogResult.OK)
{
// Record the new album name _album.FileName = dlg.FileName;
// Use Save handler to store the album menuSave_Click(sender, e);
//Update title bar to include new name SetTitleBar();
}
}
}
In general, any object that supports the IDisposable interface can be used with the using statement in this manner. In particular, you will recall that we supported this interface in our PhotoAlbum and Photograph classes in chapter 5, so we could use this statement with our album and photo objects.
For the remainder of the book, we will generally employ the using statement in our examples to dispose of nonmemory resources rather than calling the Dispose method explicitly.
8.2.2INTERCEPTING THE FORM.CLOSE METHOD
Let’s get back to our application and the CloseCurrentAlbum method. Since our application is a modeless dialog, Close will be called when the application exits. In fact, we call the Close method explicitly in the Click handler for our Exit menu.
We could certainly use the CloseCurrentAlbum method in our Click event handler. While this would work for the Exit menu, it does not work for the case where
the application exits via the Alt+F4 keyboard shortcut or the Close option on the system menu.2
To handle both situations, the Form class provides a Closing event that occurs whenever the form is about to close. The protected OnClosing method is invoked whenever the Close method is called, and it in turn raises the Closing event by invoking any registered event handlers. The signature for this method is as follows:
protected virtual void OnClosing(CancelEventArgs ce);
2 The system menu, as you may know, is the menu of operating system commands that appears when you click the control box icon in the upper left corner of a window. You can also right-click an application’s title bar or its entry in the task bar to display this menu.
THE FORM.CLOSE METHOD |
235 |
As you can see, this method receives a CancelEventArgs object. This class defines a Cancel property to help determine whether the application will actually exit. If this property is set to true by an override of the OnClosing method or a Closing event handler, then the close operation is cancelled and the application will continue to run. The Cancel property has a default value of false, so that the close operation is not cancelled and the application will exit.
We will override the OnClosing method in our MainForm class to make sure the CloseCurrentAlbum method is called regardless of how the application exits.
Set the version number of the MyPhotos application to 8.2.
|
OVERRIDE THE ONCLOSING METHOD |
|
|
|
|
|
Action |
Result |
|
|
|
1 |
Override the OnClosing |
protected override void OnClosing |
|
method in the |
(CancelEventArgs ce) |
|
MainForm.cs source |
{ |
|
|
|
|
window. |
|
|
|
|
2 |
Within this method, call the |
if (this.CloseCurrentAlbum() == false) |
|
CloseCurrentAlbum |
|
|
method to see if the |
|
|
current album should be |
|
|
saved. |
|
|
|
|
3 |
If the user clicked the |
ce.Cancel = true; |
|
Cancel button, then cancel |
Note: This cancels the Close operation so that the appli- |
|
the close operation. |
|
|
cation does not exit. |
|
|
|
|
|
|
|
4 |
Otherwise, allow the |
else |
|
application to close. |
ce.Cancel = false; |
|
|
Note: Since false is the default value, these lines are |
|
|
not strictly required. They are here simply to illustrate the |
|
|
setting when the application is permitted to exit. |
|
|
|
5 |
Remember to call |
base.OnClosing(ce); |
|
OnClosing in the base |
} |
|
class. |
Note: This call ensures that logic internal to the Form |
|
|
class is performed, and ensures that any Closing event |
|
|
handlers for the form are called before the application |
|
|
exits. Of course, any registered handler can prevent the |
|
|
application from exiting by setting ce.Cancel to true. |
|
|
|
Compile and run the application to see this method in action. Add a few photos and try to exit the application using the Exit menu, the Alt+F4 key, and the Close option from the system menu. In all cases, you should be queried by the
Album method with the question dialog for saving the current album. If you select the Cancel button the application will not, in fact, exit.
236 |
CHAPTER 8 DIALOG BOXES |