- •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
Album method. The Nodes collection is cleared and the expand operation is cancelled.
2If the album is opened and found to be empty, then the Nodes collection is cleared and the expand operation is cancelled.
3If the album is opened and found to be nonempty, then the existing Nodes collection is replaced with a collection of TreeNode objects based on the photographs in the album.
Note that we once again use the Tag property to hold the file path, this time for the Photograph object’s file name. This will come in useful when we look at node selection in the next section.
Compile and run the program to exercise our new event handler. Try to reproduce each of these three possibilities to see the result. Also note how the icon for the photograph nodes differs when the node is selected.
TRY IT! Handle the AfterCollapse event for the tree to clear the collection contained in an album node. This event handler should again use the Tag property for the node to determine if the node represents an album. When an album node is collapsed, call the Clear method on its Nodes collection and recreate the default “child” node so the album can be expanded later on.
Of course, a more complex tree hierarchy will require nodes at various levels of the tree to expand and collapse depending on their requirements. The code we created here is for a three-level tree, but can be extended to support more complicated structures. Once again it is worth mentioning that the use of the Tag property works well in our application since there are only three types of objects. For a more complex tree view, consider creating one or more new classes based on the TreeNode class.
So far we have not worried about synchronizing the contents of our ListView and TreeView controls. In the next section we finally take up this topic while discussing the selection of tree nodes.
15.4NODE SELECTION
A node in a tree view is selected whenever the user clicks on the node with the mouse. In our application, the tree nodes correspond to albums and photographs that can be displayed in the ListView area of the form. Whenever a user selects a node, the contents of that node should be displayed in the list view.
Such behavior is typical of applications that employ a TreeView control. The nodes in the tree contain or refer to other data that is or can be displayed on the form. Whenever a new tree node is selected, the data displayed must be updated as well. For example, in Windows Explorer, the tree view contains directories,1 while the list view contains files contained in these directories. When the user selects a directory entry from the tree view, the contents of that directory are displayed in the list view of the window. The reverse is also true. When the user double-clicks on a directory in the
NODE SELECTION |
505 |
list view, that directory is shown in the tree view and its contents are displayed in the list view.
In this section we will look at how to implement this behavior in our MyAlbumExplorer application. This will link up our TreeView and ListView controls so they work together and present a consistent interface to the user.
These changes come in two flavors. First there are changes to ensure that the ListView is properly updated when the TreeView changes. Next there are changes to ensure that the TreeView is properly updated when the ListView changes. All of these updates will be driven by the selection of a tree view node using the
edNode property of the TreeView control. Figure 15.8 shows our application with an album selected in the tree view and the corresponding collection of photographs displayed in the list view.
Figure 15.8 In this figure, the TreeView and ListView controls are finally coordinated.
We will begin these changes by updating our form when a node is selected in our
TreeView control.
15.4.1SUPPORTING NODE SELECTION
As we saw for the expand and collapse operations, there are two events associated with node selection. The BeforeSelect event occurs before the node is selected in the control, and receives a TreeViewCancelEventArgs instance containing the event data. The AfterSelect event occurs after the node has been selected, and receives a TreeViewEventArgs instance.
1It also contains disks, the desktop, the control panel, and other objects. For the purposes of our example, we can pretend that it contains only directories.
506 |
CHAPTER 15 TREE VIEWS |
The BeforeSelect event is useful when you may wish to cancel a selection based on the state or other settings related to a given node. Since we have no need to do this here, we will use the AfterSelect event to update the ListView control based on the selected node. The following table summarizes the types of nodes in our tree, how to identify each type, and what the ListView control should contain for each type.
Contents of ListView for each type of TreeNode
TreeNode Type |
How to identify this type |
What to show in the ListView |
|
|
|
Top-level node |
The parent node is null. |
The collection of albums. |
Album node |
The associated file has an album |
The collection of photos in this album. |
|
file extension. |
|
Photograph |
The node is not a top-level or an |
Nothing for now. Later we will draw the actual |
node |
album node. |
photograph associated with this node. |
|
|
|
We can use this information to implement our event handler. The steps required are described by the following table.
Set the version number of the MyAlbumExplorer application to 15.4.
IMPLEMENT A HANDLER FOR THE AFTERSELECT EVENT
|
Action |
Result |
|
|
|
1 |
In the MainForm.cs [Design] window, |
private void treeViewMain_AfterSelect |
|
add an AfterSelect event handler for |
(object sender, System.Windows. |
|
the TreeView control. |
Forms.TreeViewEventArgs e) |
|
{ |
|
|
|
|
|
|
|
2 |
Obtain the file name associated with the |
TreeNode node = e.Node; |
|
selected node. |
string fileName = node.Tag as string; |
|
|
|
3 |
If the file name string is null, throw an |
if (fileName == null) |
|
exception. |
throw new ApplicationException |
|
Note: This should not happen, and indi- |
("selected tree node has " |
|
+ "invalid tag"); |
|
|
cates that something is wrong. |
|
|
|
|
4 |
If the node is a top-level node, display |
if (node.Parent == null) |
|
the albums associated with this node in |
{ |
|
the list view. |
// Top-level node |
|
LoadAlbumData(fileName); |
|
|
|
|
|
|
} |
|
|
|
5 |
If the node is an album node, display the |
else if (Path.GetExtension(fileName) |
|
photographs associated with the album |
== ".abm") |
|
in the list view. |
{ |
|
// Album node selected |
|
|
|
|
|
|
PhotoAlbum album |
|
|
= OpenTreeAlbum(node); |
|
|
LoadPhotoData(album); |
|
|
} |
|
|
|
6 |
Otherwise, the node must be a |
else // must be a photograph |
|
photograph node. |
{ |
|
|
// Just clear the list for now. |
|
|
listViewMain.Clear(); |
|
|
} |
|
|
} |
|
|
|
NODE SELECTION |
507 |
As you can see, we take advantage of the LoadAlbumData and LoadPhotoData methods implemented in chapter 14. By encapsulating our load functionality in a method, we are able to reuse the methods here with no changes. Both of these methods are based on a file path from which to load the data, and we make use of this fact here to specify the appropriate data associated with the selected tree node. For a toplevel node this is an album directory. For an album node this is an album file which is loaded as a new PhotoAlbum object. For a photograph node, this is the image file, although we do not make use of this fact here.
Astute readers will realize that there is some inefficiency here since we have separated the logic for updating our two views. For instance, when an album node is expanded and selected, we open the album to load the collection of photographs in the treeViewMain_BeforeExpand method, and then open the album again to update the contents of the ListView control from the treeViewMain_AfterSelect method. This is the result, in part, of how we have separated our discussion of the two controls. In a production program, you would likely want to merge these efforts to ensure that an album is only opened one time for each update.
One situation we will fix is the behavior of the OnLoad method. The method performs the following tasks:
1 The InitTreeData method is called, which does the following:
aCreates and selects the top level node,
bCreates tree nodes for each album in the default album directory.
2As a result of selecting the top-level node, the treeViewMain_After-Select event handler is called, which does the following:
a Calls LoadAlbumData to populate the ListView control.
3Back in the OnLoad method, the LoadAlbumData method is called to initialize the ListView control.
Clearly the second call to LoadAlbumData is no longer required, so we can remove it from our program.
UPDATE THE ONLOAD METHOD
|
Action |
Result |
|
|
|
7 |
Modify the OnLoad method to |
protected override void OnLoad(EventArgs e) |
|
only initialize the TreeView |
{ |
|
control. |
. . . |
|
|
|
|
|
// Initialize the contents of the form |
|
|
InitTreeData(); |
|
|
} |
|
|
|
508 |
CHAPTER 15 TREE VIEWS |
This completes the update of the list view as the contents of the tree view are modified. You can compile and run the application to verify that the ListView contents changes as different nodes are selected.
Our next topic is to update the contents of the TreeView control based on user interactions with the list view items.
15.4.2REVISITING THE LIST VIEW
The contents of our ListView control can be modified directly by the user through the control itself and through the menu bar items. There are three actions a user can perform to alter the list contents:
1Select the Albums menu item under the View menu. This invokes a Click event for the menu, and our menuAlbums_Click event handler.
2Select the Photos menu item under the View menu, which can only be done when an item representing an album is selected in the list view. This invokes a Click event for the menu, and our menuPhotos_Click event handler.
3Double-click on an item in order to activate it. This invokes the ItemActivate event, and our listViewMain_ItemActivate event handler.
We will handle each of these actions by selecting the appropriate node in our
View control. This permits the tree view to “be in charge” of ensuring that all controls on the form display the proper information based on the currently selected node. This is a good general mechanism that can be employed in any application.
Let’s take a moment to consider what the behavior should be for each of these actions. These are summarized in the following table.
Result of user actions modifying the ListView control
Action |
Result in TreeView |
Result in ListView |
|
|
|
Select the Albums |
The top-level Default Albums node |
The collection of albums from the |
menu item. |
should be selected. |
default album directory should be |
|
|
displayed. |
Select the Photos |
The tree node corresponding to the |
The collection of photographs for the |
menu item. |
current album should be selected. |
current album should be displayed. |
Double-click on an item |
The tree node corresponding to the |
The contents of the item should be |
in the list view. |
activated item should be visible and |
displayed. |
|
selected. |
|
|
|
|
As you can see, all three actions should result in the selection of a node in the tree. This will cause the AfterSelect event to occur, which will invoke our treeViewMain_AfterSelect event handler. This handler will, in turn, cause the proper set of items to appear in the ListView control, as described in the previous table.
As a result, we simply need to modify the behavior for these three actions to select the proper tree node, and our existing code will do the rest. We will begin with our Albums menu item.
NODE SELECTION |
509 |