Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Manning - Windows Forms Programming With CSharp.pdf
Скачиваний:
72
Добавлен:
24.05.2014
Размер:
14.98 Mб
Скачать

Of these, the first Album 1 node contains a single TreeNode object representing the Photo 1 node.

The point here is to see firsthand how TreeNode objects are created and appear on the form. In the next section we will create nodes for our actual albums and photographs programmatically. The TreeNode Editor we used here is useful for creating a fixed set of nodes, or for creating the top-level nodes for a tree. For example, in a program with a large number of application settings, you might organize these settings into a hierarchy and display them in a tree view. The user could then interact with the tree to modify the application’s settings. In this case, the TreeView.CheckBoxes property might be useful to enable or disable each setting via a check box.

15.3DYNAMIC TREE NODES

In this section we will look at programmatically creating and modifying the set of tree nodes associated with a tree view control. We have already decided to display a toplevel “Default Albums” node, under which the albums in the default album directory will be displayed. Within each album the set of photos in the album will appear. The result should look something like figure 15.7. This section will focus on populating the tree view with the appropriate set of tree nodes. Section 15.4 will examine how to coordinate the contents of our TreeView and ListView controls.

To make this change, we will first create some index constants for use when accessing our image list. Once this is done, we will look at how to create the album nodes and photograph nodes in code.

Figure 15.7

A TreeView automatically displays horizontal and vertical scroll bars as required. Note that the contents of the TreeView and ListView controls are not yet synchronized.

15.3.1ASSIGNING INDEX CONSTANTS

Before we talk about how to create this tree, recall that we created constants for the image indices in chapter 14. Figure 15.7 shows the closed book icon for each unselected album, and the open book icon for the selected “leeds” album. Let’s create

DYNAMIC TREE NODES

497

some constants for the remaining images in our ImageList objects so that we can use them in this section.

This is done with the following step.

Set the version number of the MyAlbumExplorer application to 15.3.

ASSIGN IMAGE INDEX CONSTANTS

 

Action

Result

 

 

 

1

In the MainForm.cs code

private const int PhotoIndex = 0;

 

window, update the image index

private const int AlbumIndex = 1;

 

constants to account for

private const int ErrorIndex = 2;

 

private const int SelectedPhotoIndex = 3;

 

unselected and selected items.

 

private const int SelectedAlbumIndex = 4;

 

 

private const int AlbumDirectoryIndex = 5;

 

 

 

With these constants in place, we are ready to discuss adding the actual albums to our tree.

15.3.2CREATING THE ALBUM NODES

The proper way to add nodes to a tree depends somewhat on the size of a tree. For a small set of nodes, it makes sense to add the entire hierarchy of nodes at one time, and then allow the control to manage the nodes as they are expanded and collapsed by the user. For a large hierarchy, adding a huge number of nodes can use up a lot of memory. Imagine if the Windows Explorer program created a TreeNode object for every directory and file on your computer. This would be a lot of nodes on most computers.

Instead, applications typically add only the nodes a user initially requires, and then insert additional nodes based on the user’s actions. This saves both time and memory, since less work is required to initialize the tree when the application starts, and memory is only allocated as new nodes are added to the tree.

In our application, the number of albums present could be few or many. We could create the entire hierarchy all at once as shown in listing 15.1. This requires that we open each album file and iterate through every photograph in every album. Since this could be expensive for a large number of albums, we will not use this method nor discuss this code in any detail. Hopefully, it is instructive to see how the entire hierarchy might be created in a single method.

Listing 15.1 Create the entire set of tree nodes required (not our approach)

private void InitTreeData()

{

treeViewMain.BeginUpdate();

treeViewMain.Nodes.Clear();

// Create the top-level node

TreeNode defaultRoot = new TreeNode("Default Albums",

AlbumDirectoryIndex, AlbumDirectoryIndex);

treeViewMain.Nodes.Add(defaultRoot);

498

CHAPTER 15 TREE VIEWS

// Create a node for each album file foreach (string s in Directory.GetFiles(

PhotoAlbum.DefaultDir, "*.abm"))

{

String baseName = Path.GetFileNameWithoutExtension(s); TreeNode albumNode = new TreeNode(baseName) defaultRoot.Nodes.Add(albumNode);

// Open the album

PhotoAlbum album = OpenAlbum(s); if (album == null)

{

// Bad album, so adjust the image index settings albumNode.ImageIndex = ErrorIndex; albumNode.SelectedImageIndex = ErrorIndex; continue;

}

// Create a node for each photo in this album foreach (Photograph p in album)

{

string text = album.GetDisplayText(p); TreeNode photoNode = new TreeNode(text, PhotoIndex, SelectedPhotoIndex);

albumNode.Nodes.Add(photoNode);

}

album.Dispose();

}

treeViewMain.EndUpdate();

}

Instead, we will take an “as-needed” approach to our tree nodes. Initially we will create only the album nodes, and then add the photographs for an album only when the user expands that album’s node.

To begin this process, we need to modify our OnLoad method to create the initial tree structure.

MODIFY THE ONLOAD METHOD

 

Action

Result

 

 

 

1

In the MainForm.cs code

protected override void OnLoad(EventArgs e)

 

window, update the OnLoad

{

 

method to initialize the tree view

. . .

 

 

 

control before the form is

// Initialize the tree and list controls

 

displayed.

InitTreeData();

 

 

LoadAlbumData(PhotoAlbum.DefaultDir);

 

 

}

 

 

 

DYNAMIC TREE NODES

499

The InitTreeData method will clear the nodes we created with the TreeNode Editor and add the top-level default node and set of albums from the default album directory. This table continues the steps from the previous table.

ADD THE INITTREEDATA METHOD

 

Action

Result

 

 

 

2

Add an InitTreeData method to the

private void InitTreeData()

 

MainForm.cs code window.

{

 

 

 

3

To implement this method, first clear

treeViewMain.BeginUpdate();

 

any existing nodes in the tree view

treeViewMain.Nodes.Clear();

 

control.

 

 

Note: The BeginUpdate method

 

 

should be used when adding multi-

 

 

ple nodes to a TreeView control so

 

 

that it will not repaint its window

 

 

while the new nodes are added.

 

 

 

 

4

Create the top-level node for the tree.

// Create the top-level node

 

How-to

TreeNode defaultRoot

 

= new TreeNode("Default Albums",

 

a. Use the label “Default Albums.”

AlbumDirectoryIndex,

 

b. Set the image indices to use the

AlbumDirectoryIndex);

 

defaultRoot.Tag = PhotoAlbum.DefaultDir;

 

album directory icon.

treeViewMain.Nodes.Add(defaultRoot);

 

c. Add the node as a root of the tree.

treeViewMain.SelectedNode = defaultRoot;

 

 

 

d. Select this node by default.

 

 

 

 

5

Create a node for each album file in

foreach (string s in Directory.GetFiles(

 

the default album directory.

PhotoAlbum.DefaultDir, "*.abm"))

 

 

{

 

 

 

6

Create a new TreeNode for this

// Create a node for this album

 

album using the base file name as the

String baseName = Path.

 

label text for the node.

GetFileNameWithoutExtension(s);

 

TreeNode albumNode

 

 

 

 

= new TreeNode(baseName,

 

 

 

7

Create a child node in each album

new TreeNode[] {

 

node with the label text “child.”

new TreeNode("child")

 

 

});

 

 

 

8

Set the Tag property for the node to

albumNode.Tag = s;

 

contain the album file path.

Note: We will use the Tag property to identify

 

 

 

 

the album related to a specified node when

 

 

handling events for the TreeView control.

 

 

 

9

Add the new node to the collection of

defaultRoot.Nodes.Add(albumNode);

 

nodes under the default root node.

}

 

 

 

10

Allow the TreeView to repaint by

treeViewMain.EndUpdate();

 

calling the EndUpdate method.

}

 

 

 

500

CHAPTER 15 TREE VIEWS

This code uses a few tricks to ensure that our application will perform as expected. When a new album node is created, a single child node is added to ensure that the TreeView control will allow the node to be expanded.

TreeNode albumNode = new TreeNode(baseName,

new TreeNode[] { new TreeNode("child") });

Without this child, the control would presume that our node has no children, and would not display a plus sign next to the album to permit the user to expand the node. We will make use of this in the next section, where we implement the expansion of an album node. This line simply ensures that the user can initiate this step.

We also assign the Tag property for each node to contain the file path corresponding to the node. For the root node, this path is the default album directory. For each album, this path is the fully qualified album file name.

. . .

defaultRoot.Tag = PhotoAlbum.DefaultDir;

foreach (string s in Directory.GetFiles(. . .)

{

. . .

albumNode.Tag = s; defaultRoot.Nodes.Add(albumNode);

}

This setting will permit us to identify the object corresponding to a given node while processing a user action on behalf of the tree view. Like other Tag properties we have seen for .NET, this property can be set to any object instance.

Notice as well that we use the default image index and selected image index for all album files. Since we do not open the corresponding PhotoAlbum during our initialization step, we have no way to know which albums can be opened and which will generate an error. We start by assuming that all albums can be opened, and will update the image index values if we discover any problems.

It is also worth noting that using the Tag property as we do in the previous code is not always a practical solution. Another common tactic, especially in more complex applications, is to derive a new class from the TreeNode class, and use this new class to populate the tree. This alternate approach can encapsulate node-specific functionality in the derived class, and can improve the maintenance and readability of the resulting code.

Compile and run your application to verify that the albums appear in the tree. Our next topic is the insertion of photographs when the user expands an album node.

15.3.3CREATING THE PHOTOGRAPH NODES

So far our TreeView control displays the albums from the default album directory during start-up. We created a default child node within each album to permit the user to expand these nodes. The next step is to handle this expansion and replace the default child node with the set of photos in the album.

DYNAMIC TREE NODES

501

There are a number of ways tree nodes can be expanded and collapsed. These include the following:

From the mouse. The user can double-click on a tree node to toggle between expand and collapse operations. When the ShowPlusMinus property is true, a click on a plus ‘+’ sign will expand the node while a click on a minus

‘–’ sign will collapse a node.

From the keyboard. The user can press the right arrow key to expand the selected node in the tree, and the left arrow key to collapse the selected node.

From code. The TreeNode class includes an Expand method to expand the node, a Collapse method to collapse the node, and a Toggle method to switch the node to the opposite of its current state. The TreeView class includes the ExpandAll and CollapseAll methods to expand or collapse all nodes in the tree.

In addition, the TreeNode.EnsureVisible method will expand nodes as required to have the node appear within the containing TreeView control.

Regardless of how a node is expanded or collapsed, the BeforeExpand, AfterExpand, BeforeCollapse, and AfterCollapse events occur in the TreeView class for each node as it alters its state. The before events receive a TreeViewCancelEventArgs class instance as their event parameter, while the after events receive a TreeViewEventArgs class instance. The TreeViewCancelEventArgs class is summarized in .NET Table 15.4. The TreeViewEventArgs class provides the same two Action and Node properties shown in the table, but inherits from the System.EventArgs class rather than the CancelEventArgs class. The CancelEventArgs class is discussed in chapter 8.

.NET Table 15.4 TreeViewCancelEventArgs class

The TreeViewCancelEventArgs class is a CancelEventArgs object that contains event data for events in the TreeView class that occur before an operation takes place. The event handler receiving this class has the opportunity to cancel the operation by setting the inherited Cancel property to true. This class is part of the System.Windows.Forms namespace, and inherits from the System.ComponentModel.CancelEventArgs class.

 

Action

Gets the TreeViewAction enumeration member

Public Properties

 

representing the action that caused this event to occur.

Node

Gets the TreeNode object that is the target of the current

 

 

 

operation.

 

 

 

Let’s get back to our application and make use of some of these constructs. We would like to insert a set of nodes for the photos in an album whenever the album node is expanded. We can do this by handling the BeforeExpand event for our tree.

502

CHAPTER 15 TREE VIEWS