- •Using Your Sybex Electronic Book
- •Acknowledgments
- •Contents at a Glance
- •Introduction
- •Who Should Read This Book?
- •How About the Advanced Topics?
- •The Structure of the Book
- •How to Reach the Author
- •The Integrated Development Environment
- •The Start Page
- •Project Types
- •Your First VB Application
- •Making the Application More Robust
- •Making the Application More User-Friendly
- •The IDE Components
- •The IDE Menu
- •The Toolbox Window
- •The Solution Explorer
- •The Properties Window
- •The Output Window
- •The Command Window
- •The Task List Window
- •Environment Options
- •A Few Common Properties
- •A Few Common Events
- •A Few Common Methods
- •Building a Console Application
- •Summary
- •Building a Loan Calculator
- •How the Loan Application Works
- •Designing the User Interface
- •Programming the Loan Application
- •Validating the Data
- •Building a Math Calculator
- •Designing the User Interface
- •Programming the MathCalculator App
- •Adding More Features
- •Exception Handling
- •Taking the LoanCalculator to the Web
- •Working with Multiple Forms
- •Working with Multiple Projects
- •Executable Files
- •Distributing an Application
- •VB.NET at Work: Creating a Windows Installer
- •Finishing the Windows Installer
- •Running the Windows Installer
- •Verifying the Installation
- •Summary
- •Variables
- •Declaring Variables
- •Types of Variables
- •Converting Variable Types
- •User-Defined Data Types
- •Examining Variable Types
- •Why Declare Variables?
- •A Variable’s Scope
- •The Lifetime of a Variable
- •Constants
- •Arrays
- •Declaring Arrays
- •Initializing Arrays
- •Array Limits
- •Multidimensional Arrays
- •Dynamic Arrays
- •Arrays of Arrays
- •Variables as Objects
- •So, What’s an Object?
- •Formatting Numbers
- •Formatting Dates
- •Flow-Control Statements
- •Test Structures
- •Loop Structures
- •Nested Control Structures
- •The Exit Statement
- •Summary
- •Modular Coding
- •Subroutines
- •Functions
- •Arguments
- •Argument-Passing Mechanisms
- •Event-Handler Arguments
- •Passing an Unknown Number of Arguments
- •Named Arguments
- •More Types of Function Return Values
- •Overloading Functions
- •Summary
- •The Appearance of Forms
- •Properties of the Form Control
- •Placing Controls on Forms
- •Setting the TabOrder
- •VB.NET at Work: The Contacts Project
- •Anchoring and Docking
- •Loading and Showing Forms
- •The Startup Form
- •Controlling One Form from within Another
- •Forms vs. Dialog Boxes
- •VB.NET at Work: The MultipleForms Project
- •Designing Menus
- •The Menu Editor
- •Manipulating Menus at Runtime
- •Building Dynamic Forms at Runtime
- •The Form.Controls Collection
- •VB.NET at Work: The DynamicForm Project
- •Creating Event Handlers at Runtime
- •Summary
- •The TextBox Control
- •Basic Properties
- •Text-Manipulation Properties
- •Text-Selection Properties
- •Text-Selection Methods
- •Undoing Edits
- •VB.NET at Work: The TextPad Project
- •Capturing Keystrokes
- •The ListBox, CheckedListBox, and ComboBox Controls
- •Basic Properties
- •The Items Collection
- •VB.NET at Work: The ListDemo Project
- •Searching
- •The ComboBox Control
- •The ScrollBar and TrackBar Controls
- •The ScrollBar Control
- •The TrackBar Control
- •Summary
- •The Common Dialog Controls
- •Using the Common Dialog Controls
- •The Color Dialog Box
- •The Font Dialog Box
- •The Open and Save As Dialog Boxes
- •The Print Dialog Box
- •The RichTextBox Control
- •The RTF Language
- •Methods
- •Advanced Editing Features
- •Cutting and Pasting
- •Searching in a RichTextBox Control
- •Formatting URLs
- •VB.NET at Work: The RTFPad Project
- •Summary
- •What Is a Class?
- •Building the Minimal Class
- •Adding Code to the Minimal Class
- •Property Procedures
- •Customizing Default Members
- •Custom Enumerations
- •Using the SimpleClass in Other Projects
- •Firing Events
- •Shared Properties
- •Parsing a Filename String
- •Reusing the StringTools Class
- •Encapsulation and Abstraction
- •Inheritance
- •Inheriting Existing Classes
- •Polymorphism
- •The Shape Class
- •Object Constructors and Destructors
- •Instance and Shared Methods
- •Who Can Inherit What?
- •Parent Class Keywords
- •Derived Class Keyword
- •Parent Class Member Keywords
- •Derived Class Member Keyword
- •MyBase and MyClass
- •Summary
- •On Designing Windows Controls
- •Enhancing Existing Controls
- •Building the FocusedTextBox Control
- •Building Compound Controls
- •VB.NET at Work: The ColorEdit Control
- •VB.NET at Work: The Label3D Control
- •Raising Events
- •Using the Custom Control in Other Projects
- •VB.NET at Work: The Alarm Control
- •Designing Irregularly Shaped Controls
- •Designing Owner-Drawn Menus
- •Designing Owner-Drawn ListBox Controls
- •Using ActiveX Controls
- •Summary
- •Programming Word
- •Objects That Represent Text
- •The Documents Collection and the Document Object
- •Spell-Checking Documents
- •Programming Excel
- •The Worksheets Collection and the Worksheet Object
- •The Range Object
- •Using Excel as a Math Parser
- •Programming Outlook
- •Retrieving Information
- •Recursive Scanning of the Contacts Folder
- •Summary
- •Advanced Array Topics
- •Sorting Arrays
- •Searching Arrays
- •Other Array Operations
- •Array Limitations
- •The ArrayList Collection
- •Creating an ArrayList
- •Adding and Removing Items
- •The HashTable Collection
- •VB.NET at Work: The WordFrequencies Project
- •The SortedList Class
- •The IEnumerator and IComparer Interfaces
- •Enumerating Collections
- •Custom Sorting
- •Custom Sorting of a SortedList
- •The Serialization Class
- •Serializing Individual Objects
- •Serializing a Collection
- •Deserializing Objects
- •Summary
- •Handling Strings and Characters
- •The Char Class
- •The String Class
- •The StringBuilder Class
- •VB.NET at Work: The StringReversal Project
- •VB.NET at Work: The CountWords Project
- •Handling Dates
- •The DateTime Class
- •The TimeSpan Class
- •VB.NET at Work: Timing Operations
- •Summary
- •Accessing Folders and Files
- •The Directory Class
- •The File Class
- •The DirectoryInfo Class
- •The FileInfo Class
- •The Path Class
- •VB.NET at Work: The CustomExplorer Project
- •Accessing Files
- •The FileStream Object
- •The StreamWriter Object
- •The StreamReader Object
- •Sending Data to a File
- •The BinaryWriter Object
- •The BinaryReader Object
- •VB.NET at Work: The RecordSave Project
- •The FileSystemWatcher Component
- •Properties
- •Events
- •VB.NET at Work: The FileSystemWatcher Project
- •Summary
- •Displaying Images
- •The Image Object
- •Exchanging Images through the Clipboard
- •Drawing with GDI+
- •The Basic Drawing Objects
- •Drawing Shapes
- •Drawing Methods
- •Gradients
- •Coordinate Transformations
- •Specifying Transformations
- •VB.NET at Work: Plotting Functions
- •Bitmaps
- •Specifying Colors
- •Defining Colors
- •Processing Bitmaps
- •Summary
- •The Printing Objects
- •PrintDocument
- •PrintDialog
- •PageSetupDialog
- •PrintPreviewDialog
- •PrintPreviewControl
- •Printer and Page Properties
- •Page Geometry
- •Printing Examples
- •Printing Tabular Data
- •Printing Plain Text
- •Printing Bitmaps
- •Using the PrintPreviewControl
- •Summary
- •Examining the Advanced Controls
- •How Tree Structures Work
- •The ImageList Control
- •The TreeView Control
- •Adding New Items at Design Time
- •Adding New Items at Runtime
- •Assigning Images to Nodes
- •Scanning the TreeView Control
- •The ListView Control
- •The Columns Collection
- •The ListItem Object
- •The Items Collection
- •The SubItems Collection
- •Summary
- •Types of Errors
- •Design-Time Errors
- •Runtime Errors
- •Logic Errors
- •Exceptions and Structured Exception Handling
- •Studying an Exception
- •Getting a Handle on this Exception
- •Finally (!)
- •Customizing Exception Handling
- •Throwing Your Own Exceptions
- •Debugging
- •Breakpoints
- •Stepping Through
- •The Local and Watch Windows
- •Summary
- •Basic Concepts
- •Recursion in Real Life
- •A Simple Example
- •Recursion by Mistake
- •Scanning Folders Recursively
- •Describing a Recursive Procedure
- •Translating the Description to Code
- •The Stack Mechanism
- •Stack Defined
- •Recursive Programming and the Stack
- •Passing Arguments through the Stack
- •Special Issues in Recursive Programming
- •Knowing When to Use Recursive Programming
- •Summary
- •MDI Applications: The Basics
- •Building an MDI Application
- •Built-In Capabilities of MDI Applications
- •Accessing Child Forms
- •Ending an MDI Application
- •A Scrollable PictureBox
- •Summary
- •What Is a Database?
- •Relational Databases
- •Exploring the Northwind Database
- •Exploring the Pubs Database
- •Understanding Relations
- •The Server Explorer
- •Working with Tables
- •Relationships, Indices, and Constraints
- •Structured Query Language
- •Executing SQL Statements
- •Selection Queries
- •Calculated Fields
- •SQL Joins
- •Action Queries
- •The Query Builder
- •The Query Builder Interface
- •SQL at Work: Calculating Sums
- •SQL at Work: Counting Rows
- •Limiting the Selection
- •Parameterized Queries
- •Calculated Columns
- •Specifying Left, Right, and Inner Joins
- •Stored Procedures
- •Summary
- •How About XML?
- •Creating a DataSet
- •The DataGrid Control
- •Data Binding
- •VB.NET at Work: The ViewEditCustomers Project
- •Binding Complex Controls
- •Programming the DataAdapter Object
- •The Command Objects
- •The Command and DataReader Objects
- •VB.NET at Work: The DataReader Project
- •VB.NET at Work: The StoredProcedure Project
- •Summary
- •The Structure of a DataSet
- •Navigating the Tables of a DataSet
- •Updating DataSets
- •The DataForm Wizard
- •Handling Identity Fields
- •Transactions
- •Performing Update Operations
- •Updating Tables Manually
- •Building and Using Custom DataSets
- •Summary
- •An HTML Primer
- •HTML Code Elements
- •Server-Client Interaction
- •The Structure of HTML Documents
- •URLs and Hyperlinks
- •The Basic HTML Tags
- •Inserting Graphics
- •Tables
- •Forms and Controls
- •Processing Requests on the Server
- •Building a Web Application
- •Interacting with a Web Application
- •Maintaining State
- •The Web Controls
- •The ASP.NET Objects
- •The Page Object
- •The Response Object
- •The Request Object
- •The Server Object
- •Using Cookies
- •Handling Multiple Forms in Web Applications
- •Summary
- •The Data-Bound Web Controls
- •Simple Data Binding
- •Binding to DataSets
- •Is It a Grid, or a Table?
- •Getting Orders on the Web
- •The Forms of the ProductSearch Application
- •Paging Large DataSets
- •Customizing the Appearance of the DataGrid Control
- •Programming the Select Button
- •Summary
- •How to Serve the Web
- •Building a Web Service
- •Consuming the Web Service
- •Maintaining State in Web Services
- •A Data-Driven Web Service
- •Consuming the Products Web Service in VB
- •Summary
754 Chapter 16 THE TREEVIEW AND LISTVIEW CONTROLS
Germany. PrevNode and NextNode allow you to iterate through the nodes of the current segment: they return the next and previous nodes on the current segment of the tree (the sibling nodes, as they’re called). See the section “Enumerating the Nodes Collection,” later in this chapter, for an example.
Assigning Images to Nodes
To display an image in front of a node’s caption, you must first initialize an ImageList control and populate it with all the images you plan to use with the TreeView control. The Node object exposes two image-related properties: ImageIndex and SelectedImageIndex. Both properties are the indices of an image in an ImageList control, which contains the images to be used with the control. To connect the ImageList control to the TreeView object (as well as the ListView object, which is discussed later in the chapter), you must assign the name of the ImageList control to the ImageList property of the TreeView control. Then you can specify images by their index in the ImageList control.
The ImageIndex property is the index of the image you want to display in front of the node’s caption. The SelectedImageIndex is the index of the image you want to display when the node is selected (expanded). Windows Explorer, for example, uses the icon of a closed folder for all collapsed nodes and the icon of an open folder for all expanded nodes. If you don’t specify a value for the SelectedImageIndex property, then the image specified with the ImageIndex property will be displayed. If you haven’t specified a value for this property either, then no image will be displayed for this node.
VB.NET at Work: The TreeViewDemo Project
It’s time to demonstrate the members discussed so far with an example. The project you’ll build in this section is the TreeViewDemo project, and you can find it in this chapter’s folder on the CD. The project’s main form is shown in Figure 16.8.
Figure 16.8
The TreeViewDemo project
The Add Categories button adds the three top-level nodes to the TreeView control with the statements shown in Listing 16.1. These are the control’s root nodes. The other two buttons add items under the root nodes.
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |
THE TREEVIEW CONTROL 755
Listing 16.1: The Add Categories Button
Protected Sub AddCategories_Click(ByVal sender As Object, _
ByVal e As System.EventArgs)
TreeView1.Nodes.Add(“Shapes”)
TreeView1.Nodes.Add(“Solids”)
TreeView1.Nodes.Add(“Colors”)
End Sub
When these statements are executed, three root nodes will be added to the list. After clicking the Add Categories button, your TreeView control looks like the one shown at the left.
To add a few nodes under the node Colors, you must retrieve the Colors Nodes collection and add child nodes to this collection, as shown in Listing 16.2.
Listing 16.2: The Add Colors Button
Protected Sub AddColors_Click(ByVal sender As Object, _ ByVal e As System.EventArgs)
Dim cnode As TreeNode cnode = TreeView1.Nodes(2) cnode.Nodes.Add(“Pink”) cnode.Nodes.Add(“Maroon”) cnode.Nodes.Add(“Teal”)
End Sub
When these statements are executed, three nodes will be added under the Colors node, but the Colors node won’t be expanded. Therefore, its child nodes won’t be visible. To see its child nodes, you must double-click the Colors node to expand it (or click the plus sign in front of it, if there is one). The same TreeView control with its Colors node expanded is shown to the left. Alternatively, you can add a statement that calls the Expand method of the cnode object, after adding the color nodes to the control:
cnode.Expand()
Run the project, click the first button (Add Categories) and then the second button (Add Colors). If you click the Add Colors button first, you’ll get a NullReferenceException, indicating that node can’t be inserted unless its parent node exists already. You can add a few statements in the TreeViewDemo project’s code to disable the buttons that generate similar runtime errors.
To add child nodes under the Shapes node, use the statements shown in Listing 16.3. This is the Shapes button’s Click event handler.
Listing 16.3: The Add Shapes Button
Protected Sub AddShapes_Click(ByVal sender As Object, _
ByVal e As System.EventArgs)
Dim snode As TreeNode
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |
756 Chapter 16 THE TREEVIEW AND LISTVIEW CONTROLS
snode = treeview1.Nodes(0) snode.Nodes.Add(“Square”) snode.Nodes.Add(“Triangle”) snode.Nodes.Add(“Circle”)
End Sub
Add a third Command button on the form, name it Add Shapes, and insert these lines in its Click event handler. If you run the project and click the three buttons in the order in which they appear on the Form, the TreeView control will be populated with Colors and Shapes. If you double-click the items Colors and Shapes, the TreeView control’s nodes will be expanded.
Notice that the code knows the order of the root node to which it’s adding child nodes. Your application should know the node under which it must add new child nodes. You could scan the entire tree to locate an item, but then again the node names are not unique, not even within a Nodes collection.
This approach doesn’t work with a sorted tree. If your TreeView control is sorted, you must create a hierarchy of nodes explicitly, with the following statements:
snode = TreeView1.Nodes.Add(“Shapes”) snode.Add(“Square”) snode.Add(“Circle”) snode.Add(“Triangle”)
These statements will work regardless of the control’s Sorted property setting. The three shapes will be added under the Shapes nodes, and their order will be determined automatically. Of course, you can always populate the control in any way you like and then turn on the Sorted property.
Let’s revise the code we’ve written so far to display all the nodes under a header called Items. In other words, we’ll add a new node that will act as the root node for existing nodes. It’s not a common operation, but it’s an interesting example of how to manipulate the nodes of a TreeView control.
First, we must add the root, a node that will contain all other nodes as children. Before we do so, however, we must copy into local variables all the first-level nodes. We’ll use these variables to add the current root nodes under the new (and single) root node. There are three root nodes currently in our control, so we need three local variables. The three variables are of the TreeNode type, and they’re set to the root nodes of the original tree. Then we must clear the entire tree, add the new root node (the Items node), and finally add all the copied nodes under the new root. The code behind the Move Tree button is shown in Listing 16.4.
Listing 16.4: Moving an Entire Tree
Protected Sub MoveTree_Click(ByVal sender As Object, _
ByVal e As System.EventArgs) Handles bttnMoveTree.Click Dim colorNode, shapeNode, solidNode As TreeNode
colorNode = TreeView1.Nodes(0) shapeNode = TreeView1.Nodes(1) solidNode = TreeView1.Nodes(2) TreeView1.Nodes.Clear()
TreeView1.Nodes.Add(“Items”)
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |
THE TREEVIEW CONTROL 757
TreeView1.Nodes(0).Nodes.Add(colorNode)
TreeView1.Nodes(0).Nodes.Add(shapeNode)
TreeView1.Nodes(0).Nodes.Add(solidNode)
End Sub
You can revise this code so that it uses an array of Node objects to store all the root nodes instead of their count. For a routine that will work with any tree, you must assume that the number of nodes is unknown, so the ArrayList would be a better choice. The following loop stores all the root nodes of the TreeView1 control to the TVList ArrayList:
Dim node As TreeNode
For Each node in TreeView1.Nodes
TVList.Add(node)
Next
Likewise, the following loop extracts the root nodes from the TVList object:
Dim node As TreeNode Dim itm As Object TreeView1.Nodes.Clear For Each itm In TVList
node = CType(itm, TreeNode) TreeView1.Nodes.Add(node)
Next
Enumerating the Nodes Collection
Each group of child nodes forms a Nodes collection, which exposes several methods. As you have seen in the last example, a Node object may include an entire tree under it. When we move a node, it takes with it the entire Nodes collection under it. The FirstNode property returns the first node in the collection, the LastNode property returns the last node in the collection, and the NextNode and PrevNode properties return the next and previous nodes in the collection, respectively. You can scan all the nodes in the CurrentNode collection with a loop, which starts with the first node and then moves to the next node with the help of the FirstNode and NextNode properties. The following loop prints the names of all continents on the GlobeTree control:
Dim node As TreeNode
node = GlobeTree.Nodes(0).Nodes(0).FirstNode While node <> Nothing
Console.WriteLine(node.text) node = node.NextNode
End While
The last property demonstrated by the TreeViewDemo project is the Sorted property, which sorts the child nodes of the node to which it’s applied. When you set the Sorted property of a node to True, every child node you attach to it will be inserted automatically in alphabetical order.
Note If you reset the Sorted property to False and add another node, it will be appended to the end of the existing (and sorted) nodes. This is how new child nodes are added to a parent node when its Sorted property is False.
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |
758 Chapter 16 THE TREEVIEW AND LISTVIEW CONTROLS
VB.NET at Work: The Globe Project
The Globe project, which you can find in this chapter’s folder on the CD, demonstrates many of the techniques we’ve discussed so far. It’s not the simplest example of a TreeView control, and its code is lengthy, but it will help you understand how to manipulate nodes at runtime. As you know by now, TreeView is not a simple control, so before ending this section I would like to show you a fairly advanced example that you can use as a starting point for your own custom applications. You’ll also see how to save the nodes of a TreeView control to a disk file and retrieve them later.
Figure 16.9
The Globe project
The Globe project consists of a single form, which is shown in Figure 16.9. The TreeView control at the left contains a tree structure with continents, countries, and cities, with a rather obvious structure. Each city belongs to a country, and each country belongs to a continent. The control is initially populated with the continents, which were added at design time. The countries and cities are added from within the form’s Load event handler. The continents were added at design time, but as you will see, there’s no particular reason not to add them to the control at runtime. It would have been actually simpler to add all the nodes at runtime, but I’ve decided to add a few nodes at design time just for demonstration purposes.
When a node is selected in the TreeView control, its text is displayed on the TextBox controls at the bottom of the form. When a continent name is selected, the continent’s name appears in the first TextBox, and the other two TextBoxes are empty. When a country is selected, its name appears in the second TextBox, and its continent appears in the first TextBox. Finally, when a city is selected, it appears in the third TextBox, along with its country and continent in the other two TextBoxes.
You can also use the TextBox controls to add new nodes. To add a new continent, just supply the name of the continent in the first TextBox and leave the other two empty. To add a new country, supply its name in the second TextBox and the name of the continent it belongs to in the first one. Finally, to add a city, supply a continent, country, and city name in the three TextBoxes.
Run the Globe application and expand the continents and countries to see the tree structure of the data stored in the control. Add new nodes to the control, and enumerate these nodes by clicking
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |
THE TREEVIEW CONTROL 759
the appropriate button on the right-hand side of the form. These buttons list the nodes at a given level (continents, countries, and cities). When you add new nodes, the code places them in their proper place in the list. If you specify a new city and a new country under an existing continent, then a new country node will be created under the specified continent, and a new city node will be inserted under the specified country.
Coding the Globe Project
Let’s take a look at the code of the Globe project. We’ll start by looking at the code that populates the TreeView control. The root node (GLOBE) and the continent names were added at design time through the TreeNode Editor. In many cases, it is convenient to add the first few nodes, or at least the root node, at design time.
After the continents are in place, the code adds the countries to each continent and the cities to each country. The code in the form’s Load event goes through all the continents already on the control and examines their Text property. Depending on the continent represented by the current node, it adds the corresponding countries and some city nodes under each country node.
If the current node is Africa, the first country to be added is Egypt. The Egypt node is added to the ContinentNode object. The new node is returned as a TreeNode object and is stored in the CountryNode object. Then the code uses this object to add nodes that correspond to cities under the Egypt node. The form’s Load event handler is quite lengthy, so I’m showing (Listing 16.5) only the code that adds the first country under each continent and the first city under each country.
Listing 16.5: Adding the Nodes of Africa
For Each ContinentNode In GlobeNode.Nodes
Select Case ContinentNode.Text
Case “Europe”
CountryNode = ContinentNode.Nodes.Add(“Germany”)
CountryNode.Nodes.Add(“Berlin”)
Case “Asia”
CountryNode = ContinentNode.Nodes.Add(“China”)
CountryNode.Nodes.Add(“Beijing”)
Case “Africa”
CountryNode = ContinentNode.Nodes.Add(“Egypt”)
CountryNode.Nodes.Add(“Cairo”)
CountryNode.Nodes.Add(“Alexandria”)
Case “Oceania”
CountryNode = ContinentNode.Nodes.Add(“Australia”)
CountryNode.Nodes.Add(“Sydney”)
Case “N. America”
CountryNode = ContinentNode.Nodes.Add(“USA”)
CountryNode.Nodes.Add(“New York”)
Case “S. America”
CountryNode = ContinentNode.Nodes.Add(“Argentina”)
End Select
Next
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |
760 Chapter 16 THE TREEVIEW AND LISTVIEW CONTROLS
The remaining countries and their cities are added with similar statements, which you can examine if you open the Globe project. Notice that the GlobeTree control could have been populated entirely at design time, but this wouldn’t be much of a demonstration. Let’s move on to a few more interesting aspects of programming the TreeView control.
Retrieving the Selected Node
The selected node is given by the property SelectedNode. Once you can retrieve the selected node, you can also retrieve its parent node and the entire path to the root node. The parent node of the current control is TreeView1.SelectedNode.Parent. If this node has a parent, you can retrieve it by calling the Parent property of the previous expression (TreeView1.SelectedNode.Parent.Parent). Or you can use the FullPath property to retrieve the selected node’s full path. The FullPath property of the Rome node is
GLOBE\Europe\Italy\Rome
The slashes separate the segments of the node’s path. You can specify any other character for this purpose by setting the control’s PathSeparator property.
To remove the selected node from the tree, call the Remove method:
TreeView1.SelectedNode.Remove
If the selected node is a parent control for other nodes, the Remove method will take with it all the nodes under the selected one. You can also use the IsSelected property of the Node object to find out whether a specific node is selected or not. The IsSelected property returns a True/False value, depending on the status of the node. A similar property, the IsExpanded property, allows you to find out whether a specific node is expanded or not.
One of the operations you’ll want to perform with the TreeView control is to capture the selection of a node. The TreeView control fires the AfterSelect event, which notifies your application of the selection of another node. If you need to know which node was previously selected, you must use the BeforeSelect event. The second argument of both events has two properties, the Node and Action properties, which let you find out the node that fired the event and the action that caused it. The e.Node property is a TreeViewNode object that represents the selected node. Use it in your code as you would use any other node of the control. The e.Action property is a member of the TreeViewAction enumeration (ByKeyboard, ByMouse, Collapse, Expand, Unknown). Use this property to find out the action that caused the event. The actions of expanding and collapsing a tree branch fire their own events, which are the BeforeExpand/AfterExpand and the BeforeCollapse/ AfterCollapse events, respectively.
VB6 VB.NET
The VB6 version of the TreeView control recognized the NodeClick event, which was fired every time the user selected another node in the control. The NodeClick event has been replaced by the AfterSelect event.
The Globe project retrieves the selected node and extracts the parts of the node’s path. The individual components of the path are displayed in the three TextBox controls at the bottom of the form. Listing 16.6 shows the event handler for the TreeView control’s AfterSelect event.
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |
THE TREEVIEW CONTROL 761
Listing 16.6: Processing the Selected Node
Private Sub GlobeTree_AfterSelect(ByVal sender As Object, _
ByVal e As System.Windows.Forms.TreeViewEventArgs) _ Handles GlobeTree.AfterSelect
If GlobeTree.SelectedNode Is Nothing Then Exit Sub Dim components() As String
txtContinent.Text = “” txtCountry.Text = “” txtCity.Text = “”
components = Split(GlobeTree.SelectedNode.FullPath.ToString, _ GlobeTree.PathSeparator)
Console.WriteLine(GlobeTree.SelectedNode.FullPath.ToString)
If components.Length > 1 Then txtContinent.Text = components(1) If components.Length > 2 Then txtCountry.Text = components(2) If components.Length > 3 Then txtCity.Text = components(3)
End Sub
The Split() function of VB extracts the parts of a string that are delimited by a special character. For the case of the TreeView control, this special character is given by the property PathSeparator, and the default value of this property is the character “\”. If any of the captions contain this character, you should change the default to a different character by setting the PathSeparator property to something else.
The code behind the Delete Current Node and Expand Current Node buttons is simple. To delete a node, call the selected node’s Remove method:
GlobeTree.SelectedNode.Remove
The other button expands the current node by calling the Expand method of the selected node:
GlobeTree.SelectedNode.Expand
The TreeNode object exposes the ExpandAll method, too, which expands not only the specified node but all the Nodes collections under it (its child nodes).
Processing Multiple Selected Nodes
The GlobeTree TreeView control has its CheckBoxes property set to True so that users can select multiple nodes. I’ve added this feature to demonstrate how you can retrieve the selected nodes and process them.
As you will notice by experimenting with the TreeView control, you can check a node that has subordinate nodes, but these nodes will not be affected. They will remain unchecked (or checked, if you have already checked them). In most cases, however, when we check a parent node, we actually intend to check all the nodes under it. When you check a country, for example, you’re in effect selecting not only the country but all the cities under it. The code of the Process Selected Nodes button assumes that when a parent node is checked, it must also check all the nodes under it.
Let’s look at the code that iterates through the control’s nodes and isolates the selected ones. It doesn’t really process them, it simply prints them on the ListBox control. However, you can call a
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |
762 Chapter 16 THE TREEVIEW AND LISTVIEW CONTROLS
function to process the selected nodes in any way you like. The code behind the Process Selected Nodes button starts with the continents. It creates a TreeNodeCollection with all the continents and then goes through the collection with a For Each…Next loop. At each step, it creates a new TreeNodeCollection, which contains all the subordinate nodes (the countries under the selected continent) and goes through the new collection. This loop is also interrupted at each step to retrieve the cities in the current country and process them with another loop. The following pseudo-code listing outlines the code:
Set up the Continents Collection
For Each continent In Continents
If continent is selected Then process it
Set up the Countries Collection
For Each country In Countries
If country is selected Then process it
Set up the Cities Collection
For Each city In Cities
If city is selected Then process it
Next
Next
Next
The code behind the Process Selected Nodes button implements the pseudo-code shown above and is shown in Listing 16.7.
Listing 16.7: Processing All Selected Nodes
Protected Sub bttnProcessSelected_Click(ByVal sender As Object, _
ByVal e As System.EventArgs)
Dim continent, country, city As TreeNode
Dim Continents, Countries, Cities As TreeNodeCollection
ListBox1.Items.Clear()
Continents = GlobeTree.Nodes(0).Nodes
For Each continent In Continents
If continent.Checked Then ListBox1.Items.Add(continent.FullPath)
Countries = continent.Nodes
For Each country In Countries
If country.Checked Or country.Parent.Checked Then _
ListBox1.Items.Add(“ |
“ & country.FullPath) |
|
Cities = country.Nodes |
|
|
For Each city In Cities |
|
|
If city.Checked Or city.Parent.Checked Or _ |
||
city.Parent.Parent.Checked Then _ |
|
|
ListBox1.Items.Add(“ |
“ & city.FullPath) |
|
Next |
|
|
Next
Next
End Sub
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |
THE TREEVIEW CONTROL 763
The code examines the Checked property of the current node, as well as the Checked property of its parent node. If either one is True, then the node is considered selected. You should try to add the appropriate code to select all subordinate nodes of a parent node when the parent node is selected (whether you deselect the subordinate nodes when the parent node is deselected is entirely up to you and the type of application you’re developing). The Nodes collection exposes the GetEnumerator method, which should be very familiar to you by now. You can revise the last listing so that it uses an enumerator in the place of each For Each…Next loop.
Adding New Nodes
The Add Node button lets the user add new nodes to the tree at runtime. The number and type of the node(s) added depend on the contents of the TextBox controls:
If only the first TextBox control contains text, then a new continent will be added.
If the first two TextBox controls contain text, then:
If a continent exists, a new country node is added under the specified continent.
If a continent doesn’t exist, a new continent node is added, and then a new country node is added under the continent’s node.
If all three TextBox controls contain text, the program adds a continent node (if needed), then a country node under the continent node (if needed), and finally, a city node under the country node.
Obviously, you can omit a city, or a city and country, but you can’t omit a continent name. Likewise, you can’t specify a city without a country or a country without a continent. The code will prompt you accordingly when it detects a condition that prevents it from adding the new node for any reason. If the node exists already, then the program selects the existing node and doesn’t issue any warnings. The Add Node button’s code is shown in Listing 16.8.
Listing 16.8: Adding Nodes at Runtime
Private Sub bttnAddNode_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles bttnAddNode.Click Dim nd As TreeNode
Dim Continents As TreeNode
If txtContinent.Text.Trim <> “” Then Continents = GlobeTree.Nodes(0)
Dim ContinentFound, CountryFound, CityFound As Boolean Dim ContinentNode, CountryNode, CityNode As TreeNode For Each nd In Continents.Nodes
If nd.Text.ToUpper = txtContinent.Text.ToUpper Then ContinentFound = True
Exit For End If
Next
If Not ContinentFound Then
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |
764 Chapter 16 THE TREEVIEW AND LISTVIEW CONTROLS
nd = Continents.Nodes.Add(txtContinent.Text) End If
ContinentNode = nd
If txtCountry.Text.Trim <> “” Then Dim Countries As TreeNode Countries = ContinentNode
If Not Countries Is Nothing Then For Each nd In Countries.Nodes
If nd.Text.ToUpper = txtCountry.Text.ToUpper Then CountryFound = True
Exit For End If
Next End If
If Not CountryFound Then
nd = ContinentNode.Nodes.Add(txtCountry.Text) End If
CountryNode = nd
If txtCity.Text.Trim <> “” Then Dim Cities As TreeNode Cities = CountryNode
If Not Cities Is Nothing Then For Each nd In Cities.Nodes
If nd.Text.ToUpper = txtCity.Text.ToUpper Then CityFound = True
Exit For End If
Next End If
If Not CityFound Then
nd = CountryNode.Nodes.Add(txtCity.Text) End If
CityNode = nd End If
End If End If
End Sub
The listing is quite lengthy, but it’s not hard to follow. First, it attempts to find a continent that matches the name in the first TextBox. If it succeeds, it need not add a new continent node. If not, then a new continent node must be added. To avoid simple data-entry errors, the code converts the continent names to uppercase before comparing them to the uppercase of each node’s name. The same happens with the countries and the cities. As a result, each node’s pathname is unique. You can’t have the same city name under the same country more than once. It is possible, however, to add the same city name to two different countries. The example is not quite realistic, as there are common city names in every country.
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |
THE TREEVIEW CONTROL 765
Listing Continents/Countries/Cities
The three buttons List Continents, List Countries, and List Cities populate the ListBox control with the names of the continents, countries, and cities, respectively. The code is straightforward and is based on the techniques discussed in previous sections. To print the names of the continents, it iterates through the children of the GLOBE node. Listing 16.9 shows the complete code of the List Continents button.
Listing 16.9: Retrieving the Continent Names
Private Sub bttnListContinents_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles bttnListContinents.Click Dim Nd As TreeNode, continentNode As TreeNode
Dim continent As Integer, continents As Integer ListBox1.Items.Clear()
Nd = GlobeTree.Nodes(0) continents = Nd.Nodes.Count continentNode = Nd.Nodes(0) For continent = 1 To continents
ListBox1.Items.Add(continentNode.Text) continentNode = continentNode.NextNode
Next End Sub
The code behind the List Countries names is equally straightforward, although longer. It must scan each continent, and within each continent, it must scan in a similar fashion the continent’s child nodes. To do this, you must set up two nested loops, the outer one to scan the continents and the inner one to scan the countries. The complete code for the List Countries button is shown in Listing 16.10. Notice that in this example, I’m using For…Next loops to iterate through the current level’s nodes, and I also use the NextNode method to retrieve the next node in the sequence.
Listing 16.10: Retrieving the Country Names
Private Sub bttnListCountries_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles bttnListCountries.Click Dim Nd, CountryNode, ContinentNode As TreeNode
Dim continent, continents, country, countries As Integer ListBox1.Items.Clear()
Nd = GlobeTree.Nodes.Item(0) continents = Nd.Nodes.Count ContinentNode = Nd.Nodes(0)
For continent = 1 To continents countries = ContinentNode.Nodes.Count CountryNode = ContinentNode.Nodes(0) For country = 1 To countries
ListBox1.Items.Add(CountryNode.Text)
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |