An MSForms (all VBA) treeview

Pages in this article

  1. Features
  2. How To Use
  3. Examples

Features

Performance

Realistically a VBA form with all the controls we need for each node can’t be expected to match that of the compiled original. Even so we are surprised at what has been possible, albeit with considerable tuning and refining. Light testing suggests 1000 to 2000 nodes is quite feasible even in a typical five year old laptop, depending on the configuration of the treeview.

Cosmetics

The original has a wide variety of features and functionality. We have tried to replicate most of these, and added a few features of our own.

Checkboxes

The common controls treeview has a checkboxes option so you can check nodes. We ensured our treeview has that too.

A picture says more than a thousand words, so:

Checkboxes are possible and work
Checkboxes are possible and work!

Expand/collapse boxes or icons

By default we draw a rectangle with a plus or minus characters as text to mimic the expand/collapse boxes. You can also opt for expand/collapse icons. We have bundled XP and Win7 style expander icons in the demo form or you can customize your own as you wish:

 Expander icons are provided
Icons as expand/collapse buttons

Lines

The treeview from the common controls lib always shows lines to indicate the structure. With ours it is optional to show lines. The choice might be for aesthetic reasons, however a very large treeview will load significantly faster without lines so that might be an overriding consideration.

Choose whether or not to show lines
Choose whether or not to show lines

Icons

Again, let's just look at the picture!

Icons can be added
You can add icons too!

If you are familiar with the ImageList control we have incorporated a similar approach. You can store your images in a (hidden) frame and pass a reference to the frame, just as you would pass a reference to an ImageList to the common controls treeview. However your icon images could be stored anywhere. As long as you can retrieve a “StdPicture” handle for each icon and add it to a “collection” that’s all we need.

Label formatting

Label formatting is available to you with code as simple as:

cNode.Bold = True
cNode.BackColor = RGB(255, 255, 220)    ' pale yellow
cNode.ForeColor = RGB(180, 0, 0)    ' dark red

Make sure you do not use code like:

cNode.Control.BackColor = RGB(255, 255, 220)    ' pale yellow

When you are building the set of nodes, the controls of the treeview have not yet been created (this is done when .Refresh is called). Using the properties the node class itself exposes ensures those are applied when the nodes are created. Only once the treeview is fully initialised and refreshed you can access the properties of the cNode.Control.

In addition, the treeview will automatically pick up the default font settings of its parent (frame) control, so controlling appearance of the treeview starts as easy as setting properties of the frame you'll use to host the treeview. I'll explain this on the next page.

Functionality

We've already implemented quite some functionality. Here is what you can do "out of the box":

Function
Description

Collapsing and expanding

Clicking the expand/collapse buttons does as advertised: it expands or collapses the tree.

Navigation using mouse and keyboard

Navigation using mouse and keyboard works just like in the "old" treeview. Right and left arrow expand or collapse, up and down arrow move up or down the tree. Page-up and down just work.

Editing nodes

You can use F2 to edit a node. Double-clicking a node also puts it in edit mode. This option can be enabled or disabled by setting a property value.

Drag and drop

We're working on that.

Copying, moving (cutting) and pasting

Copy and move functions are built in, you can copy or move single nodes or entire branches. You can do that silently but we have also made it easy for you to trap user actions on the selected node, eg Ctrl-c, Ctrl-x, Ctrl-v. You can include your own code to validate the intended actions, eg only allow copy between similar levels, then instigate the copy or move.

Sort

An efficient sort routine has been devised especially for this treeview. Currently it only operates at a given node level, however options for sort order and text/binary are included.

Properties

There is quite a number of properties you can change. To give you an idea, some are listed below. There are more properties and methods, which are documented in the Excel download.

Property
Description
Remarks

Full Width

True: Node caption widths extend beyond the width of the treeview, makes for clear highlighting of the selected node and if backcolor's are applied.

False: label widths are 'autosized' to the text
If node icons are used an additional image control is created for use with 'full width'

A large tree using node icons will load faster if 'full width' is not applied.

Checkboxes

True: show checkboxes.
Press spacekey or click to change the value.

The check change event is trapped in the form, and the info label updated.

Allow Editing

User can manually edit the node.
Double click or press F2 on a selected node.

Key Enter to apply the edit or Esc to abort.

Show Lines

Whether or not to show lines may be a matter of aesthetics or needs depending on the tree.
Try experimenting with different indentation widths (see the spin button).

In a large tree start-up time will be considerably faster if lines are not shown.

Root button

True: the entire tree can be collapsed to the Root.
False: the tree can only be collapsed to the first level.

Expander icons

The expander buttons can be Labels with +/- characters toggled, or pairs of icons.
If icons are used the images must be available in the workbook, on a sheet or as in the demo hidden on the form.

The demo includes pseudo WinXP and Win7 Explorer type expander icons, but you can use any icons of your choice, or no icons.

Indentation

Default is 15 points

NodeHeight

The treeview already automatically adjusts its Node height to the Font size or checkbox size or Icon size, whichever is the larger.

It's probably best not to adjust Node height.

Font size

The font size and other default label properties are adopted from the parent frame.

Change at design time only.

RootNodes

The root nodes of the tree

Nodes

All Nodes of your tree

Methods

Method
Description
Remarks

ActivateNode

Activate a node based on the key.

Copy

Copies the node put on the 'clipboard'

Move

Move source node + children to destination node.

NodesClear

Equivalent to Treeview.Nodes.Clear.

NodeRemove

Remove Node, its children and grandchildren.

Refresh

Adds and displays all the controls for the Treeview for the first time. ALso displays and repositions node controls as required.

SetTreeExpansionLevel

Updates the expanded properties according to lLevel.
Called recursively.

TerminateTree

Removes the treeview from memory.
Must be called before your userform goes out of scope.

ScrollToView

Ensures the node passed to the method is scrolled into view.

Events

Currently we have made available a limited set of events:

Event
Descrription

Click event

Fires when you click any node.

Node check event

Fires when you click the checkbox of a node.

After label edit event

Fires after editing a label.

Key down event

Fires when you press a key. The demoform included in the sample project shows how to implement copy and paste.

Mouse events

All mouse events are pushed through using a single MouseEvents event.

Add your own events

It isn't hard to add your own events, e.g. it takes three steps to add a new event for a specific element of the tree:

  1. Add event to the clsNode class for the piece of the tree you need an event for, by using the dropdowns at the top of the code window. Make sure it then calls a Friend sub in the clsTreeView class.
  2. Add an Event declaration to the clsTreeView class (like shown above) and a friend sub that raises that event when called from clsNode.
  3. Add an event handler to your userform.

Next: using this treeview in your VBA project.

 


 


Comments

All comments about this page:


Comment by: Gill (28-2-2013 16:43:27) deeplink to this comment

Great work guys! :)
I'm not sure if this is a bug or a deliberate design feature, but I notice that the "NodesClear" method also sets the "TreeControl" object to Nothing.
This caused my project to crash several times until I worked out what was going on... I clear out and redraw the treeview whenever a change is made so it would be good if you could clear out all the nodes whilst leaving everyting else intact.
I realise it's not too difficult to work round, but like a lot of people I'm a disciple of PED and my code borrows heavily from the "PETRAS" application, so I'm sure others will have the same problem!


Comment by: Jan Karel Pieterse (1-3-2013 15:36:15) deeplink to this comment

Hi Gill,

Thanks for letting us know!
I agree that nodesclear should not be clearing the pointer to the treecontrol.

We'll update the code as soon as we can to overcome this.


Comment by: Salim (28-3-2013 09:15:51) deeplink to this comment

Jan Karel Pieterse,

Your TreeView Control is really quite amazing. You really deserve an award for this. I just hope we could one day create a custom GridFlex Control. I just realised on the TreeView Control that when a node is already active, clicking on it the second time does not trigger the click event. Just to let you know.

Thanks & warm regards,
Salim T. S.


Comment by: Jan Karel Pieterse (28-3-2013 15:26:38) deeplink to this comment

Hi Salim,

Thank you very much for your compliment!


Comment by: Greg Bakker (6-7-2013 17:35:48) deeplink to this comment

Salim mentioned that "when a node is already active, clicking on it the second time does not trigger the click event". I ran into this behavior also and did some digging to see if I could change the behavior. It looks like Jan/Ben anticipated that people may want this functionality. In the clsNode.mctlControl_Click() event procedure there is a comment that says that if it is preferred that the click event is always raised to the form (even if the node was previously active) simply comment or remove this If/EndIf check.

--Greg Bakker


Comment by: Eugene (28-8-2013 12:48:38) deeplink to this comment

Excellent work. Here is a recursive method that I found on a Microsoft site which shortens the code and works well in Access.


Option Compare Database
Private Sub Form_Load()
Const strTableQueryName = "Your_tbl _Name"
Dim db As DAO.Database, rst As DAO.Recordset
Set db = CurrentDb
Set rst = db.OpenRecordset(strTableQueryName, dbOpenDynaset, dbReadOnly)
AddBranch rst:=rst, strPointerField:="Parent", strIDField:="Id", strTextField:="Description"
End Sub
'Recursive Procedure to add branches
Sub AddBranch(rst As Recordset, strPointerField As String, strIDField As String, strTextField As String, Optional varReportToID As Variant)
Dim nodCurrent As Node, objTree As TreeView
Dim strCriteria As String, strText As String, strKey As String
Dim nodParent As Node, bk As String
Set objTree = Me!xTree.Object
If IsMissing(varReportToID) Then ' Root Branch.
strCriteria = strPointerField & " Is Null"
Else ' Search for records pointing to parent.
strCriteria = BuildCriteria(strPointerField, _
rst.Fields(strPointerField).Type, "=" & varReportToID)
Set nodParent = objTree.Nodes("a" & varReportToID)
End If
' Find the first emp to report to the boss node.
rst.FindFirst strCriteria
Do Until rst.NoMatch
' Create a string with LastName.
strText = rst(strTextField)
strKey = "a" & rst(strIDField)
If Not IsMissing(varReportToID) Then 'add new node to the parent
Set nodCurrent = objTree.Nodes.Add(nodParent, tvwChild, strKey, strText)
Else    ' Add new node to the root.
Set nodCurrent = objTree.Nodes.Add(, , strKey, strText)
End If
' Save your place in the recordset so we can pass by ref for speed.
bk = rst.Bookmark
' Add employees who report to this node.
AddBranch rst, strPointerField, strIDField, strTextField, rst(strIDField)
rst.Bookmark = bk     ' Return to last place and continue search.
rst.FindNext strCriteria ' Find next employee.
Loop
End Sub


Comment by: Antonio (17-9-2013 22:52:57) deeplink to this comment

Hi.

I would know if is possible to emulate a Selected property, or anycase, how can I highlight a node.

I've been working in a search procedure (outside your code), and if found, I nedd to highlight the founded node.

Many thanks for your great job.

Antonio.

Madrid (Spain)


Comment by: Jan Karel Pieterse (18-9-2013 10:11:09) deeplink to this comment

Hi Antonio,

You can simply set the ActiveNode, as in:

Set mcTree.ActiveNode = MyFoundNode


Comment by: David Engle (1-2-2014 03:10:29) deeplink to this comment

OMG! Thank you! I have been fighting with the treeview in A2007 for months trying to overcome the Windows 7 problem. Too many workstations to be able to spend that much time. I am adding you to the credits in the About window for the project. Thank You!


Comment by: Antoniu (28-3-2014 16:34:40) deeplink to this comment

Hello Jan

I am trying to add DblClick for mcTree. I add the event but when I dbl click on node
Private Sub mcTree_DblClick(cNode As clsNode) thas not fire up

(Those are my steps):

go in clsTreeView on TreeControl activate DblClick
Private Sub TreeControl_DblClick(ByVal Cancel As MSForms.ReturnBoolean)

End Sub

put on General - Declaration this line

Event DblClick(cNode As clsNode)

on userform I can see DblClick event

but it is not working

Please can you show me the correct steps? Thank you!


Comment by: Jan Karel Pieterse (28-3-2014 19:07:32) deeplink to this comment

Hi Antoniu,

You also have to raise the couble_click event using RaiseEvent. You can check how it is done by tracking fdown how we implemented the Click event.

Start from the sub "Private Sub mctlControl_Click()" in clsNode.

In there you will find this line of code:

moTree.NodeClick Control, Me


which calls a routine in the clsTree class:

Friend Sub NodeClick(ByRef oCtl As MSForms.Control, ByRef cNode As clsNode)


In that sub we raise the event so it is exposed to the userform implementing the tree class:

RaiseEvent Click(cNode)


Comment by: Shah (18-10-2014 00:41:33) deeplink to this comment

Hi,

I am trying to use the access version of the treeview. I see activatenode as a function in the documentation but not in the downloaded code.

I have set keys for all nodes in my tree. I just want to scroll to the one based on selection in other control. Is there other way to do this?

Thanks.


Comment by: Jan Karel Pieterse (18-10-2014 11:43:52) deeplink to this comment

Hi Shah,

You can just set the ActiveNode of the tree to the node you need.


Comment by: shah (18-10-2014 14:42:32) deeplink to this comment

Hi Jan,

I have created a tree with about 16000 nodes with unique keys based on the data that I have in a table. I have created the tree and displayed it in the form.

My desire is to scroll the tree to a specific node based on the key's value. I have not been able to figure out a way to find a node based on the key. So, the right side of the mctree.ActiveNode assignment is where I am having problem.

Hope this clarifies.

Thanks.


Comment by: TPhong (26-11-2014 10:20:22) deeplink to this comment

Hi there,

I just want to say thank you.
You did a really great job.

TPhong


Comment by: cris (5-2-2015 00:42:31) deeplink to this comment

Hi,

This is great.

I notice some strange behavior with the checkbox button :
if checked before the demo's launched, parent nodes are selected (besides childs). Anyway I could manage to produce the dump of all selected nodes or subnodes.


I'm looking for enhancement to display the nodes that comply with a text search (filter) : we could make successive search and keep "checked" the various checkboxes throughout the different searches. Is there any sample of this ?

Thx
Cris



Comment by: Jan Karel Pieterse (5-2-2015 06:53:07) deeplink to this comment

Hi Cris,

I'm afraid there is no sample for that. The checking of parent nodes depends on whether you have tristate set or not.


Comment by: Mathias (23-2-2015 18:02:32) deeplink to this comment

I like your msforms treeview and i would like to have a treeview without a root because i want all items to be positioned directly under each other (without lines and without Expanders. Could someone help me by usin the code odf the demo-form (cmdgetdata) how this could be solved?

Thank you


Comment by: Jan Karel Pieterse (24-2-2015 06:22:12) deeplink to this comment

Hi Matthias,

Peter Thornton said:

Hi Mathias,
If I understand your question you only want a single column of nodes, if so
maybe a ListBox or ListView would be more appropriate. However you can add
each node as a root node but with no child nodes and that will create a
column of nodes, adapt Sub InitializeDemo1 in the demo


' demo code
With mcTree
' demo code
    .ShowLines = False ' Me.cbxShowlines.Value
    .Indentation = 0
    For k = 1 To 50    ' # roots
        Set cRoot = .AddRoot(sKey:="Root" & k, vCaption:="My Node" & k)
    ' add or change any cRoot properties as required here
    ' comment all the demo code from here until Next
     Next
        
     'create the node controls and display the tree
     .Refresh
End With


Adapt other properties for the treeview as required, eg .FullWidth. Each
(root) node could have a different icon.


Comment by: mathias (24-2-2015 12:20:04) deeplink to this comment

Hi Peter,
thank you for your prompt answer.

I use the folllwong code to get a treeview with no expanders.

        
'         Add a Root node with main and expanded icons and make it bold
    Set cRoot = .AddRoot("Root", "", "FolderClosed", "FolderOpen")
'
            
            
        Set dbs = CurrentDb
        ' Open recordset that returns the unique dates from tblBusinessUnit
        strSQL = "SELECT BusinessUnit_ID, BusinessUnit FROM tblBusinessUnit"
        
    
        
        Set rst1 = dbs.OpenRecordset(strSQL, dbOpenForwardOnly)
        ' Loop through the Business Units
        Do While Not rst1.EOF
    
            ' Add node
            strKey = "B " & rst1!BusinessUnit_ID
            strCaption = "Business unit: " & rst1!BusinessUnit
            Set cNode1 = cRoot.AddChild(sKey:=strKey, vCaption:=strCaption)
            cNode1.Bold = True
            cNode1.Expanded = True


but i don't want a root Checkbox to be shown in the view. (can i load up a Picture here? Just to Show what i mean.)

Got any idea?

Thank you.


Comment by: Jan Karel Pieterse (24-2-2015 12:55:35) deeplink to this comment

Hi Mathias,

Unfortunately I do not allow any attachments. You could upload one to skydrive and share a link as an alternative however.


Comment by: Mathias (24-2-2015 18:21:25) deeplink to this comment

next try:

i managed to get a single column of nodes almost loke i wanted to.

I did it like this:

        strSQL = "SELECT BusinessUnit_ID, BusinessUnit FROM tblBusinessUnit"
                    
        Set rst1 = dbs.OpenRecordset(strSQL, dbOpenForwardOnly)
        ' Loop through the Business Units
        Do While Not rst1.EOF
            ' Add root
            strKey = "B " & rst1!BusinessUnit_ID
            strCaption = "Business unit: " & rst1!BusinessUnit
            Set cRoot = .AddRoot(sKey:=strKey, vCaption:=strCaption)
                
            
            ' Open recordset that returns the Main Categories for each BusinessUnit
            strSQL = "SELECT Zwischen_ID, MainCategories FROM tblZwischenBusiness WHERE BusinessUnit_ID=" & _
            rst1!BusinessUnit_ID & " ORDER BY MainCategories_ID"
            Set rst2 = dbs.OpenRecordset(strSQL, dbOpenForwardOnly)
            ' Loop through the maincategories
            Do While Not rst2.EOF
                 'Add Node
                strKey = "M" & rst2!Zwischen_ID
                strCaption = "Main Categorie: " & rst2!Maincategories
                Set cNode2 = .AddRoot(sKey:=strKey, vCaption:=strCaption)
                            
                ' Open recordset that returns the students for each class
                strSQL = "SELECT ZwischenSub_ID, Subcategories FROM tblZwischenSub WHERE Zwischen_ID=" & _
                rst2!Zwischen_ID & " ORDER BY ZwischenSub_ID"
                Set rst3 = dbs.OpenRecordset(strSQL, dbOpenForwardOnly)
                 'Loop through the subcategories
                Do While Not rst3.EOF
                cNode2.Expanded = True


Comment by: Peter Thornton (25-2-2015 15:19:32) deeplink to this comment

Mathias, it's hard to recreate your code but it looks like you are adding a single column of root nodes as I suggested. Keep in mind Expanders and ShowLines are true by default, though not Checkboxes. If your cNode2 does not have any child nodes there is no need to do cNode2.Expanded = True (actually Expanded = True is default)

You say you've got it working almost as you want, in what way is it not quite as you want.


Comment by: Mathias (26-2-2015 10:05:00) deeplink to this comment

Hi Peter,

in my code example: how can i get a node being underlined?.

Cnode2.underline=true


or
cnode2.font.underline=true


does not work.

Thanks

Mathias


Comment by: Jan Karel Pieterse (26-2-2015 10:08:18) deeplink to this comment

Mathias:

Please end a piece of code with [/vb] instead of [vb]


Comment by: Jan Karel Pieterse (26-2-2015 10:10:00) deeplink to this comment

Mathias:

Please end a piece of code with square bracket /vb square bracket (so include the slash)


Comment by: Peter Thornton (26-2-2015 12:43:10) deeplink to this comment

Mathias, we didn't include a Node.Underline property but you can add it yourself. Search both classes for "Bold": if you don't use Bold simply Replace most* of the "Bold" with "Underline", otherwise copy* the Bold code* in both classes and adapt to Underline.

*note Bold is also used in Expanders, don't change or copy that near where you see "-" and "+" (only in clsTreeView).

Add comments so you can easily adapt the next version.


Comment by: Ian (4-4-2015 10:42:46) deeplink to this comment

How do I return a collection of a nodes children. The nodes.children property seems to return nothing


Comment by: Jan Karel Pieterse (5-4-2015 15:06:05) deeplink to this comment

Hi Ian,

You need the ChildNodes collection.


Comment by: Paco (6-5-2015 15:40:32) deeplink to this comment

This is an amazing piece of ingenuity! Elegant, unpretentious, very clever. Thanks for sharing.

Would you release drag & drop functionality anytime soon?

Any chance you could consider multi-select?

Kindest regards!


Comment by: Jan Karel Pieterse (6-5-2015 16:20:47) deeplink to this comment

Hi Paco,

Thank you!

Multi-select. Hmm. You can by enabling check boxes. You can check as many boxes as you like.


Comment by: Elissa (15-5-2015 02:33:01) deeplink to this comment

Hi.
Looks fantastic!
2 Questions:
1. Can it read from tables and alter the icon based on the data in the table?
2. Can it then be clickable, so a form pulls up based on which record is clicked?
Thanks!


Comment by: Peter Thornton (18-5-2015 11:35:49) deeplink to this comment

Hi Elissa,
You can trap events to respond to the newly selected node, then pass details of the node to your own routine that reads your table to return details about the applicable icon.

Since build 026 icons can be updated to nodes during run-time, see Treeview.ImageAdd and Node.ImageUpdate in the documentation (in the Excel demo). Also see the examples in the demo form to respond to mouse and keyboard events.


Comment by: Tom (15-9-2015 11:37:09) deeplink to this comment

Hi gentleman,

I'm trying to unload / change icons during runtime, but this does not seem to work?

Let's say I create a treeview, based on the with Icons example
Initially the treeview nodes don't have Images (only root has default Image)

Then I'm processing some data and based on some assertion
In case not ok: cNode.ImageMain = "nok"
In case ok: cNode.ImageMain = "ok"
mcTree.Refresh to rebuild
This seems to work

Now the issue 1:
When 'resetting' I'd like to have No icons, so I run for root.Children a loop: cNode.ImageMain = ""
mcTree.Refresh
But the Images still there

Now the issue 1:
When 'changing' I'd like to have all to "nok" icons, so I run for root.Children a loop: cNode.ImageMain = "nok"
mcTree.Refresh
But the Images still there (the old asserted "ok")

So, how can I update without assining a new collection to the treeview?

Thanks for your help!

Tom


Comment by: Peter Thornton (22-9-2015 12:57:28) deeplink to this comment

Tom, sorry for late reply only just seen your comment.

The option to change icons during runtime was added in build 026 with ImageAdd and ImageUpdate (see the documentation). However we didn't consider anyone would want to remove icons from individual nodes altogether!

Contact me privately and I'll send you a small fix which hopefully will meet your needs.


Comment by: Jon von der heyden (21-10-2015 11:03:07) deeplink to this comment

Using this for the first time and loving it! That is all. :)


Comment by: Jan Karel Pieterse (21-10-2015 15:18:05) deeplink to this comment

Hi Jon,

Glad you like it!


Comment by: Frank (20-1-2016 14:13:37) deeplink to this comment

Hi there - thank you for this great component. With this I could replace the annoying activex treeview.

I have one question - I would like to open a data record according to which Root/node was clicked. As in root I have jobs and in the nodes I have services contained in the jobs I must know what was clicked (root or node) and maybe read the key from the root/node. Is that possible? Thanks for help!


Comment by: Jan Karel Pieterse (20-1-2016 15:05:44) deeplink to this comment

Hi Frank,

If you implement the mcTree_Click event, the argument you get passed to you is the node you clicked:

Private Sub mcTree_Click(cNode As clsNode)
    Dim cChild as clsNode
    If Not cNode.ChildNodes Is Nothing Then
        For Each cChild In cNode.ChildNodes
            'Now do something with the childnode.
        Next
    End If
End Sub


Comment by: swapnil bhalerao (30-1-2017 12:14:31) deeplink to this comment

Hello
I am working on similar tree view but I want multiple checkboxes for each node (at least three) each representing cell of three different columns, also if I checked 3-4 nodes it must show value from an associated cell from the columns for which particular checkbox has been checked.

C1 C2 C3 are Checkboxes for each node if I checked C1.1(Node1) & c1.2(Node2) also C1.3(Node3) (First checkbox of three different nodes) it should show the sum value ( First checkbox C1 is associated with column 1) after cmd button has been hit.


Comment by: Peter Thornton (31-1-2017 17:55:07) deeplink to this comment

Hi bhalerao,
It would be possible to add multiple checkboxes for but far to much to show how here. Try and follow how icons and checkboxes are added, and increase the offsets to add to Nodes' Left property. Event handling in the node class, and referred to the main treeview class can be added similar to the existing single checkbox.

I don't really follow the second part of your question, but once your event handlers are in place you can include code in the main form's event handlers to apply the combinations you require.


Comment by: Pablo (15-2-2017 21:49:17) deeplink to this comment

Hi, This is an awesome component, but I'm experiencing something really odd, when I try to add event handlers to a node click event I got the event is raised even when a key is pressed (e.g.: Enter, Arrow Up, etc)
Do you have any suggestions on how to solve this?

P/S: I'm working on an Access 2013 Database. I see the same behavior on your sample app as well.


Comment by: Peter Thornton (16-2-2017 13:37:11) deeplink to this comment

Hi Pablo,
Indeed, the navigation keys will indirectly raise the click event, which means you can trap a node has been activated whether my mouse or keyboard. This 'by design' and consistent with equivalent app's.

If you want disable keyboard navigation comment the relevant key actions in TreeControl_KeyUp and _KeyDown.

If you want to allow keyboard navigation but not raise any click events, you would need to adapt quite a bit more. But are you really sure you want to do that!


Comment by: Pablo (17-2-2017 21:33:43) deeplink to this comment

Hi Peter,
Thank you for your quick answer, indeed I'd like to use keys for navigation and only raise a click event only when pressed "Enter" rather than on every keystroke, I'm trying to use the TV as a menu, where you can click or press enter to open the desired form. I tried also trapping the _KeyUp event but for some (odd) reason the keycode for "Enter" is the same as the left-arrow.
I'd be grateful if you can point out where to make the changes you mentioned in this line: "allow keyboard navigation but not raise any click events"

Thanks!


Comment by: Peter Thornton (18-2-2017 11:06:05) deeplink to this comment

Hi Pablo, OK I think I follow.

In TreeControl_KeyDown replace 3 instances of "Caption", 1 with 0
In the same event near the top, after "If KeyCode = vbKeyReturn" comment the If/Else/End if and replace with:

If Not moActiveNode Is Nothing Then
    RaiseEvent Click(moActiveNode) ' Enter will raise the click event
End If

Or, trap vbKeyReturn in the mcTree_KeyDown or KeyUp event in the main form.

FYI Enter in other treeviews expands or collapses a branch, like the left/right arrow keys, and explains the "odd" reason Enter morphed into an arrow!


Comment by: Ray Buckley (19-10-2017 12:31:03) deeplink to this comment

Hello,
Are you supporting drag drop functionality yet
Thanks
Ray


Comment by: Peter Thornton (19-10-2017 17:16:41) deeplink to this comment

Hi Ray,

Drag & drop is supported the 'pro' (see above) but it's not viable in the free version, at least not in the way we'd want to do it.


Comment by: Robin Ball (14-3-2018 16:34:35) deeplink to this comment

I am working on a Bill of Materials system and have a working recursive SQL server script that populates a single table with all components and sub builds and their components and their level number in the hierarchy.

Your free Treeview control looks excellent and I could organise my data easily enough to replicate your multi-table approach but in doing so will lose the benefits of my recursive code. I could code to choose how many levels to display for any specific parent item and its multi-level children to an arbitrary number of levels (say 12) which is currently deeper than real world data.

2 questions

Is there a depth limit to the number of levels in the hierarchy ?

Does the pro version allow for recursion and an unknown number of levels ?


Comment by: Jan Karel Pieterse (14-3-2018 17:33:16) deeplink to this comment

Hi Robin,

Theoretically there are no depth limits. But I would limit use of our free treeview to a max of about 1,000 nodes to get good performance. The pro version can handle thousands of nodes without issue.
Recursively adding nodes should be easy enough but is left as an excersize to the reader :-)
There is a post here that has some code, perhaps that helps:

https://jkp-ads.com/Articles/treeview00.asp?AllComments=True#19232


Comment by: Bert (14-5-2018 14:11:43) deeplink to this comment

Hi,

This is a very nice treeview control.

I do run in one issue though. I'm adding childnodes by selecting the parent node and then using the .Addchild method. That works well until it can't find the parent node. Then I get an VBA error 5:Invalid procedure call or argument.
In this case I just want to ignore this, but how can I do this? I tried several things like:


Set cNode = .Nodes(rst!Parent)
if Not cNode is Nothing then
cChild = cNode.AddChild(sKey:=strKey, vCaption:=strCaption)
end if

But no matter what I try, I keep getting error 5 on line:

Set cNode = .Nodes(rst!Parent)

How can I check if a node exists before I try to use it?


Comment by: Jan Karel Pieterse (14-5-2018 15:46:57) deeplink to this comment

The nodes are only accessible through their index, not through their name so you would have to enumerate the nodes and then check their Name to find the right node. Air code:

Function GetNode(sName As String)
    Dim oNode as Node
    For Each oNode in mcTree.Nodes
        If oNode.Caption = sName Then 'Or use .Key
            Set GetNode = oNode
            Exit for
        End If
    Next
End Function


Comment by: Jim Dettman (28-1-2019 15:25:02) deeplink to this comment

The features page shows a ActivateNode method, yet I cannot locate this in the Access or Excel versions....is this a feature of the "Pro" version?

Thanks,
Jim.


Comment by: Peter Thornton (29-1-2019 09:49:45) deeplink to this comment

Hi Jim,

Indeed the 'Pro' has many additional features but the ActivateNode method is also in the 'free' Access and Excel versions. If you look in the top right drop-down in the clsTreeView module it's the first method listed.


Comment by: Jim Dettman (29-1-2019 12:42:59) deeplink to this comment

Thanks for responding so quickly, but unless I have the wrong download (I have build 026, which is a .MDB file), I don't see any ActivateNode method. There is an ActiveNode property listed first, and the first method shown for the class is AddNodeToCol.

I'm also the second person that could not find this. A question was posted on Experts-Exchange from someone trying to use the control:

https://www.experts-exchange.com/questions/29133803/jkp-ads-com-treeview-How-to-select-node-in-treeview-with-Key-value.html

However after posting my last comment here, I found a similar question on the page here along with the required code, so we are set.    

But you might want to check your current build. There is no need to publish this comment unless you wish to do so.

Thanks,
Jim


Comment by: Jan Karel Pieterse (2-2-2019 16:14:33) deeplink to this comment

Hi Jim,

The docs are wrong, simply use the Activenode property as it is read/write so you can change the activenode simply by using code like:

Set mcTree.ActiveNode = cNode


Comment by: Akira HOSOKAWA (31-7-2019 10:39:00) deeplink to this comment

This TreeView control works perfectly on my VBA project.

Is there a way to get an image data (via clipboard or as an image file of JPG or PNG) of a whole tree structure which is displayed on the TreeView control?

Thanks in advance.


Comment by: Peter Thornton (31-7-2019 15:30:00) deeplink to this comment

Hi Akira,

It's easy to copy the form with Alt - Print-Screen, but I don't know how to capture the non-visible area scrolled off the form (or off the screen) which I guess is what you need.

Have you tried the 'Dump Data' example on the demo form which copies the Treeview data and structure to a worksheet. If you format cells as text you can resize column widths so nodes at different levels overlap slightly, more like the treeview.


Comment by: Andrew de beer (30-9-2019 11:19:00) deeplink to this comment

Is there a "word wrap" property for a treeview node?


Comment by: Peter Thornton (30-9-2019 16:37:00) deeplink to this comment

Hi Andrew,

There is isn't a "word wrap" property, complicated as it can be arbitrary when and where 'virtual' line breaks are added.

Nodes can handle multi-line (line breaks). However you would also need to size 'nodeheight' to at least the height as the node with most lines (handled automatically in the pro treeview).


Comment by: David (1-12-2020 20:28:00) deeplink to this comment

Is there an example of populating the tree using recursion?
I have a self referencing table.
Thank you.
David


Comment by: Jan Karel Pieterse (2-12-2020 11:33:00) deeplink to this comment

Hi David,

Perhaps this comments gets you started?
https://jkp-ads.com/Articles/treeview00.asp?AllComments=True#19232


Comment by: Casey Little (12-1-2021 21:26:00) deeplink to this comment

Very well done. I have just built a test database implementing this treeview, and it is excellent. I hate having to deal with the always-breaking MS Treeview control. Have you given up on Drag/Drop? Do you have any recommendations, articles, reference materials that can help me to implement drag/drop myself?


Comment by: Peter Thornton (13-1-2021 10:33:00) deeplink to this comment

We certainly haven't given up with D&D, it's included in in the 'pro' treeview and it works nicely. However with the free version it's difficult to make it reliable in all scenarios, particularly with larger treeviews.

As an alternative to D&D consider using a right click context menu with copy/cut/paste items, and/or with the keyboard Ctrl+C/X/V.


Comment by: Pete (20-2-2021 11:26:00) deeplink to this comment

I have implemented a drag-and-drop functionality in my Access app, using this as a starting point. It works well when both source and target are visible. I would like to add automatic scrolling, for when I'm dragging the source to something that is outside the frame at the moment when the source is selected. I'm having trouble detecting when the cursor approaches the top or bottom edge. I've tried using the Y coordinate of the MouseMove event, along with the Visindex property, but no luck so far. The Y property tells me where I am in relation to the starting position, but I have been unable to determine where the starting position is inside the control. The Visindex tells me how many positions I am down in the tree, but not the pysical position inside the scrolled control. Can you advise?


Comment by: Peter Thornton (20-2-2021 14:55:00) deeplink to this comment

Hi Pete,

Although it is possible to get the frame to scroll as you describe drag and drop is prone to various problems in the free version, particularly with larger treeviews. If drag & drop is important for you D&D is implemented in the 'pro' treeview, along with many other features.


Comment by: Pete (20-2-2021 18:40:00) deeplink to this comment

Thank you, but I really don't need other features. In fact, I cut out quite a bit of the functionality that you already have, partly for simplicity, partly for speed. This app uses a subset of what you built originally, but I would like to add the automatic scrolling. The drag-and-drop that I wrote works nicely, even with a large tree, but it can't go beyond what is displayed. You write that is is possible to do the scrolling, and I know that is possible - the ScrollToView routine does it quite nicely. My issue is detecting WHERE the cursor is in the control at the moment I initially grab an element - the 'altitude' inside the box. Once I have that, I can compute everything else - the proximity of the border, and how much to scroll the display. Can you tell me how to get that one piece of information?


Comment by: Peter Thornton (20-2-2021 20:42:00) deeplink to this comment

Hi Pete,

First a some misconceptions:
1 You say what you have so far is no problem in a large tree, but that's probably because so far you've only tested D&D in a static frame, showing perhaps only a small part of the tree.
2 I'd be surprised if removing features, as you say partly for speed, noticeably improves speed. Performance in the free version reduces exponentially the larger the tree, this is because each node involves several controls, 'features' make very little difference in this respect.
3 What's involved to scroll the frame with D&D in response to the mouse is not a matter of simply adapting ScrollToView.

Scrolling the frame for D&D purposes involves a fair amount of code, and some unexpected catches to handle. It's not viable to post here as a few lines!


Comment by: Pete (20-2-2021 21:50:00) deeplink to this comment

1. Perhaps we have different ideas of what is large. This tree has a bit over 60,000 elements, and most of the lag time is loading the data from an ODBC server. I get around that by loading only parts, and loading expanded elements on demand. I have tested it with far more elements than can be shown in a the frame, and the D&D works fine, as long as the elements to be dragged from and to are in frame. When they are out of frame, it doesn't work at all, because of the scrolling issue, which is what I'm trying to fix.

2. Removing features DID improve speed. I didn't conduct any timing tests, so I can't tell you by how much, and it was interspersed with limiting the amount of data pulled in over the network, but it did help. And it made it much simpler to cut out things that I wasn't using, so that troubleshooting subsequent tuning efforts was lots easier.

3. Well, I don't know about that. I see no reason why it wouldn't work, but maybe there are things I don't know about. In any case, I'm not asking you to do all the work for me, only if you know how to get the height inside the control at the moment an element is clicked. I tried various MouseMove events in the controls, and none of it worked, which is why I wrote you. That one thing is all I'm after at the moment, not for you to write and post an entire scrolling routine. Do you know of a way to get that number?


Comment by: Peter Thornton (21-2-2021 16:55:00) deeplink to this comment

I’d say 60k nodes is a large tree, and way beyond design for the ‘free’ version! (hence the ‘pro’). Unless your connection is particularly slow by far the most time involved with such a tree is creating the node controls, exponentially more with bigger numbers, whereas getting data over the connection will normally remain linear.

Though note controls are only built ‘on-demand’ first time required to display, so apply Expanded = False for most parent nodes when populating. Although that can dramatically help load time, once fully created such a tree will be slow to respond and difficult to navigate.

I can’t think which properties that if removed would make a significant difference to load time. Though not using properties that require additional controls would help, such as not showing lines or icons.

I’m confused as to which approach you are using for D&D. I first assumed you had rolled your own, as the MSForms events won’t work between the Treeview and Access controls but the frame should automatically scroll if you were. Yet you say you can't return coordinates in the move events, they should work fine whereas they won’t with the MSForms while dragging.

Whichever approach you're using you’re using you could get the coordinates with a mouse-hook, though probably other ways. It’s difficult to give you the simple answer you’re looking for, though even once you get it scrolling there are other catches to contend with. If you want to continue this further it might be easier if you contact me off-line.


Comment by: Mark C (27-9-2021 10:02:00) deeplink to this comment

Trying to implement it with my access project. It would be great to have double click event to expand subchild as default when editing is not allowed. :)


Comment by: Peter Thornton (27-9-2021 10:33:00) deeplink to this comment

Hi Mark,

You can adapt the code to include the double click event if you follow the comments starting under if you follow the comments starting under 'Event Click', near the top of clsTreeview. Then it's fairly straightforward to include the code to toggle expand/collapse in the event, either in your project or within the treeview classes.

FYI the pro version includes an optional property 'DblClickExpand' which does exactly what you ask, also the full range of events and much else.


Comment by: Mark C (8-10-2021 02:17:00) deeplink to this comment

Hi Peter,

Thank you for the instruction. :)

I notice there might be a bug with modal popup form.
With demo access file you provided,
1. creat frm01, set as pop up form, and modal mode as true
2. add a button on frm01, simply use it add code "DoCmd.OpenForm "frmDemo"
3. Open frm01 and click the button to open frmDemo; close frmDemo, and the whole access program will be minimized. It should just close frmDemo and leave frm01 on screen.
4. To advoid it, set frm01 modal mode as false; Or just delete subTreeView control from frmdemo.
I dont' know what is causing the problem. Could you have a check?

Thanks,


Comment by: Peter Thornton (8-10-2021 12:09:00) deeplink to this comment

Hi Mark,

I can reproduce what you describe, I'm not sure why that occurs either.

However this does not seem to occur with the 'pro' treeview, so it might be related to how the 'userform' is handled which is much improved in the pro version.

You are the first person in 8 years to notice this:)


Have a question, comment or suggestion? Then please use this form.

If your question is not directly related to this web page, but rather a more general "How do I do this" Excel question, then I advise you to ask your question here: www.eileenslounge.com.




To post VBA code in your comment, use [VB] tags, like this: [VB]Code goes here[/VB].