The following is intended to describe the basic factors relating to implementing the ActiveX “Treeview” component supplied with Visual FoxPro. In this case the information is applicable to VFP7 SP1.
This document does not include the “ImageList” control that can be used to add images to nodes, nor does it include the “Listview” control often seen used in conjunction with the Treeview control (Windows Explorer, for example, as the right side display pane).
Adding the control to a form
This is started in the same way as any other control is added. In this case the “ActiveX Control (OLEControl)” icon is clicked on the (standard) “Form Controls” toolbar. Then the mouse is moved to the area of the form where the top left corner of the control is to be positioned, downclicked, then dragged to where the bottom right corner of the control is to be.
Releasing the mouse at the appropriate point leads to a small delay and then presentation of the “Insert Object” dialogue (Figure 1 below). A listbox in the dialogue, titled “Control Type”, shows the available controls installed in the system. Hitting the “m” key positions the listbox to the first one with the name starting with “m”. In my case this was the “Macromedia Flash Factory Object”. Clicking on the scroll bar (downward) of the listbox eventually revealed the “Microsoft Treeview Control” – two in my case: “Microsoft Treeview Control 6.0 (SP4)” and “Microsoft Treeview Control, version 5.0 (SP2)”.
Figure 1
I initially selected the latest version (6.0 SP4) but found that later use of the F1 key to obtain its help produced nothing. Trying the other control instead (version 5.0 SP2) did result in a display of its help when I pressed the F1 key. My guess is that the “6.0 SP4” version came with the VFP8 public beta that I had downloaded and installed on the same machine. The F1 key does present help for the control but it is very poorly organized and there are missing properties and methods. I recommend the use, in addition to F1, of the Object Browser supplied with VFP7 and later versions.
Working with the Treeview control properties
Once the Treeview control is shown on the form design surface, clicking anywhere on it to make it the selected control and then hitting the F1 key will present a Help file for it. In addition, right-clicking on the (selected) Treeview control presents a popup menu with the selection “TreeCtrl Properties” as the last one. Clicking on that item presents the “TreeCtrl Properties” dialogue as shown in figure 4. An example of its use is shown below (figure 5), obtained by clicking on the “Checkboxes” checkbox in the dialogue and then clicking “Apply” and then clicking “OK”. Notice too that the dialogue has three tabs.
Figure 5
When you wish to manipulate the Treeview properties (above) programmatically you must use the “Object” property name within the name structure. Based on the figures here, and assuming the Treeview control has been placed on the form named “Form1”, the control would be (programmatically) disabled by coding: Form1.Olecontrol1.Object.Enabled = .F. I could not find this property of the VFP OLEControl mentioned in the property sheet or anywhere one might regularly refer to during development. However, it is mentioned in the section titled “Manipulation of Objects with Automation” under the topic “Using ActiveX Controls”, under the topic “Adding OLE Objects to Applications”, under the topic “Sharing Information and Adding OLE”, under the topic “Interoperability and the Internet”, under the topic “Extending Applications” in the “Using Visual FoxPro” part of the online Help. Phewww! The only way I found it was by using the Search option. Going through the “Content” I stopped clicking deeper in the topic tree once I came upon the topic “Interoperability and the Internet” because it seemed unrelated to the object (pun) of my need. It is noteworthy that the VFP documentation content itself is presented in a Treeview control.
Form1.Olecontrol1.Object.Enabled = .F.
Programming the Treeview control
In essence, the Treeview can be used to present any data that is hierarchical in nature. It can also be programmed to permit the user to make changes to the data presented, though of course if you do so you must also program to change the actual data in addition to accepting the user-typed revisions. For example, Windows Explorer allows you to rename a directory name presented in its left pane and in the background it also performs an actual rename of the directory.
Much of the data in our VFP applications is hierarchical. The Treeview can be used to give a user access to a list of top-level entities with the ability to "drill-down" to obtain details pertinent to a specific item. An example might be a root level list of 'assembly' part numbers, with component part numbers available on expanding the node. Sub-assemblies listed in that expansion could also be made expandable to show their component parts.
The Treeview control itself is just an empty box. To use it you must first fill it with the information you wish to present. Once you’ve done that, you likely want to react to actions that the user performs on the content. You may even want to store pertinent information after the user indicates that they have finished their work with the form or the record(s) displayed on the form.
Inherent in the Treeview control are two additional controls. The Nodes collection object is used to create and maintain the content of the Treeview while the Node object can be used to manipulate the display and/or content of a specific node within the Treeview content. The Nodes object can also be used to manipulate a specific node using its Item property.
The first thing that you need to do is to fill the Treeview with user-viewable content. To do this you will use the Add() method of the Nodes collection object. The syntax of the Add() method is (mostly from the Help file):
ONewNode = tvwRef.Nodes.Add([relative],[relationship],[key],text,[image],[selectedimage])
The arguments for the Add() method are defined below (it returns a reference to the added node):
Shown below is some sample code used to fill a very simple Treeview on a form. It is taken from the sub-classed Treeview, coded in a method named “PopulateTheTree()” invoked in the form’s Init(). The input is a table of two field records where “TypeName” will constitute a “top” (root) node and “SubType” will be a child of that “TypeName”. The records are sourced from a local view which specifies DISTINCT and sorts the records ascending by “TypeName” and “SubType”. The record format in this case assures that every top (root) node will have at least one child.
SCAN IF EMPTY(SubType) OR EMPTY(TypeName) && guard against an invalid record ELSE && we have content that is valid * When the "TypeName" changes, we need to create a new 'root' (parent) item IF TypeName <> m.LastType m.LastType = TypeName m.LastRootNumber = m.LastRootNumber + 1 m.LastRootKey = ALLTRIM(TypeName) ; + PADL(ALLTRIM(STR(m.LastRootNumber, 4, 0)), 4, "0") This.Nodes.Add( , ; && existing key string or index number tvwNext, ; && relationship to above m.LastRootKey, ; && key (string) TypeName, ; && text to be displayed , ; && image# in ImageList ) && SelectedImage# in ImageList ENDIF
* Now we add the "SubType" as a child node of the current parent This.Nodes.Add( m.LastRootKey, ; && existing key string or index number tvwChild, ; && relationship to above , ; && key (string) SubType, ; && text to be displayed , ; && image# in ImageList ) && SelectedImage# in ImageList ENDIF [EMPTY(SubType) OR EMPTY(TypeName)] ENDSCAN
Examples:
oNodeRef = tvwRef.Nodes.Item(index) oNodeRef = tvwRef.Nodes.Item(key-value)
=tvwRef.Nodes.Remove(index) =tvwRef.Nodes.Remove(key-value)
The Count property is useful if you have the need to step through the entire Nodes collection because it always reflects the exact count of nodes in the entire tree.
The Remove() method will remove a specific node, and if you have chosen to dynamically add child nodes as a parent node is expanded then this method can be used to remove those same nodes once the user has collapsed the parent node. Every node you Remove() decrements the Count property by one.
Once the Treeview has been populated (all nodes or just the top node level) the users can access and manipulate things in it. So the next thing you will do is to react to the user’s actions. While there are more (all Treeview PEMs are summarized, listed alphabetically at the end of this article), the most common events of the Treeview control that you will code for are:
In all cases above a node object (reference) is passed as the LOCAL parameter, named “node”.
The Node object has several properties and a single method. I have broken the properties down into two categories – regular and object (references). The properties are:
*These properties can be set as well as interrogated. 1The Checked property was not in the Help, but rather found using the Object Browser.
The sole method is EnsureVisible() and takes a node object reference as its parameter. It will expand Treeview node(s) and/or scroll the Treeview as necessary to ensure that the passed node is made visible to the user.
The documentation for the Selected property mentions another property named “MultiSelect” (of the Treeview?) but I could find nothing to confirm that it exists as a real property of the Treeview control and Treeview’s observed operation suggests that it is not an applicable property.
Using the Object Browser (VFP7 and later) to review ComctlLib (which is the Microsoft Windows Common Controls 5.0 (SP2)) and MSComctlLib (which is the Microsoft Windows Common Controls 6.0 (SP4)), the following differences were noted between the properties enumerated in the Help versus those listed. I don’t know that any of the named properties below can actually be used in a program (save “Checked”, which can for sure).
Found in both classes using the object browser:
As noted earlier, all four of the most used events of the Treeview control have a node object reference passed as the single parameter. This fact leads to some interesting capabilities as far as coding to refer directly to other related nodes is concerned. Some examples are shown below. Keep in mind that “node” is the LOCAL parameter name passed to the event.
IF node.Children > 0 && node has children * For illustrative purposes we will go backwards checkmarking each child PriorChildRef = node.Child.LastSibling && < -------- #1 FOR X = 1 TO node.Children PriorChildRef.Checked = .T. && checkmark this child PriorChildRef = PriorChildRef.Previous && < -------- #2 NEXT X ENDIF
ChildOfPassed = node.Child PriorChildRef = ChildOfPassed.LastSibling
ChildCount = ThisForm.tvwRef.Nodes.Item(1).Children FirstOfDaddys = ThisForm.tvwRef.Nodes.Item(“KEY007”).Parent.FirstSibling
Miscellaneous notes about the Treeview control
While I could find nothing definitive on the matter, there likely are limits inherent in the control itself.
One source put the limit of nodes in a Treeview at 32,000. If this is in fact the case, I assume that dynamically adding nodes as the user expands a parent node and removing those (added) children once the user collapses that parent can still make the control practical for most uses. Of course this would also be dependent on the user’s collapsing some of those expanded nodes from time to time.
I imagine, too that there is a limit regarding the length of any key value (string) that you can use. Can’t say if there might be a limit on the cumulative length of all key string values.
I could not find any limit stated for the text (label) property of a node but again I suspect that there is one.
I couldn’t locate anything defining/describing errors that might occur when using the Treeview control, nor do I know if such errors are trappable within VFP.
The Treeview is the control used in the Windows Explorer application’s left pane to display the volumes, directories and special files of the system (the right pane is presented using the “Listview” control). The Treeview is also used in the “Project Manager” window in VFP.
The Treeview control is often used by software installation routines to present selectable options for inclusion. In this case the “CheckBoxes” property is often TRUE.
Finally, it is worth keeping in mind that while the functionality of the Treeview control is well understood by systems people, the same cannot be said for your average clerical user. So you will be wise to explain its use in some detail in your application documentation and/or spend significant time going over its usage in user training sessions.
I hope that this article has helped you to implement your own Treeview controls with a little more ease than I experienced. There are more complex and powerful methods and events available in the control and I expect that you will be able to play with them once you have succeeded in implementing your first one.
Summary of all PEMs for the Treeview control
This summary does not include the Nodes collection object or the Node object because their PEMs are all documented in the body of the article.
The Treeview control itself has several properties, events and methods available. Their names and a summary of their functionality are listed below.
AfterLabelEdit Event
Occurs after a user edits the label* of the currently selected Node or ListItem object. I suspect that this event only occurs when the “LabelEdit” property is set .T. *I believe this refers to the “text” property of the node (refer to Nodes.Add()).
BeforeLabelEdit Event
Occurs when a user attempts to edit the label* of the currently selected ListItem or Node object. *I believe this refers to the “text” property of the node (refer to Nodes.Add()).I suspect that this event only occurs when the “LabelEdit” property is set .T.
Checkboxes Property
Returns or sets a value that determines if checkboxes appear left of each displayed node’s text.
Collapse Event
Generated when any Node object in a TreeView control is collapsed.
DropHighlight Property
Returns or sets a reference to a Node or ListItem object that is highlighted with the system highlight color when the cursor moves over it. [given this description, I have no idea what the “Drop” word is doing in its name]
Expand Event
Occurs when a Node object in a TreeView control is expanded. That is, when its child nodes become visible. FullRowSelect Property
Returns or sets a value that specifies if the entire row is selected. I suspect that this refers specifically to the selected highlight colour and that a .T. value causes the checkbox and/or image, when either is present, to also be highlighted.
GetVisibleCount Method
Returns the number of Node objects that fit in the internal area of a TreeView control.
HitTest Method
Returns a reference to the ListItem object or Node object located at the coordinates of x and y. Most often used with drag-and-drop operations to determine if a drop target item is available at the present location.
HotTracking Property
Returns a value that determines whether mouse-sensitive highlighting is enabled. [not sure what this means, actually, because when I have the property Unchecked in the “TreeCtrl Properties” dialogue the ‘tooltip’ still is displayed when I mouse over node text that extends near or beyond the right edge of the display]
ImageList Property
Specifies the name (object reference) of a separately instantiated (and filled) ImageList control. This property is not described in the documentation, other than appearing in the “TreeCtrl Properties” dialogue.
Indentation Property
Returns or sets the width of the indentation of objects in a control.
LabelEdit Property
Returns or sets a value that determines if a user can edit labels of ListItem or Node objects.
LineStyle Property
Returns or sets the style of lines displayed between Node objects.
NodeCheck Event
Occurs when the CheckBoxes property equals True and a Node object is checked or unchecked. The checkbox will have the stateas set by the operator upon entry to this event. [this event does not occur when a node is checked or unchecked programmatically] [there seems to be a bug that the node passed in the parameter cannot have its checkbox toggled to the opposite state (from its state on entry)]
NodeClick Event
Occurs when a Node object is clicked. [the node’s checkbox does not appear to be accessible through this event, at least as far as the node passed as the parameter is concerned]
Nodes Property
Returns a reference to a collection of TreeView control Node objects. PathSeparator Property
Returns or sets the delimiter character used for the path returned by the FullPath property.
Scroll Property
Returns or sets a value that specifies if scrollbars are displayed.
SelectedItem Property
Returns a reference to a selected ListItem or Node.
SingleSel Property
Returns or sets a value that specifies if an item is expanded when selected. [another case where the property name doesn’t seem to reflect its functionality]
Sorted Property
StartLabelEdit Method
Enables a user to edit a label. [not sure exactly how this fits in]
Style Property
Returns or sets the type of graphics (images, text, plus/minus, and lines) and text that appear for each Node object in a TreeView control.