Tuesday, March 11, 2008

My Treeview Project | Episode Three: Changing How the Treeview Looks

Here's what we'll end up with
after this episode.

In Episode 1: The Hello World! Treeview we built the simplest of treeviews, just to introduce some of the concepts. In Episode 2: Northwind Categories and Products we created a treeview that displayed products in categories based on data from the Northwind sample database.

In this episode we'll take the treeview from epsiode 2 and add some tweaks that affect how it looks. This will let us look at a number of useful techniques and concepts. If you haven't done episode 2 you should go back to it now and build the database because you'll be working from there in this episode.

Starting with the code from episode 2, the first tweak is to the code for the Form Open Event. Here's the new version:

Private Sub Form_Open(Cancel As Integer)
  SetupTreeview
  CreateCategoryNodes
  CreateProductNodes
End Sub

The line SetUpTreeviw is new. It calls a new subroutine to set up a bunch of attributes that control how the treeview looks. Here's the code for that subroutine, which you have to add to your form's code module:

Private Sub SetupTreeview()
  With Me.xProductTreeview
    .Style = tvwTreelinesPlusMinusText
    .LineStyle = tvwRootLines
    .Indentation = 240
    .Appearance = ccFlat
    .HideSelection = False
    .BorderStyle = ccFixedSingle
    .HotTracking = True
    .FullRowSelect = True
    .Checkboxes = False
    .SingleSel = False
    .Sorted = False
    .Scroll = True
    .LabelEdit = tvwManual
    .Font.Name = "Verdana"
    .Font.Size = 9
  End With
End Sub

This routine sets the display properties for the treeview. These can also be set interactively by right-clicking on the treeview in design view and selecting TreeCtlObject->Properties. I prefer to do this in code. First because I can maintain consistency from treeview to treeview. And second so that if I ever need to delete the control from the form and recreate it the code will take care of setting the new control up the way I had the old one. I've linked the reference pages for each of the attributes so you can see what they do. If you prefer to have your treeview look different you can adjust these options.

When we first created the products and categories treeview all we saw was the list of categories. We had to expand the categories to see any products. Sometimes that's not what we want. Sometimes we want some of the nodes to start out expanded. Here's our new category node code that takes care of that.

Private Sub CreateCategoryNodes()
  Dim rst As DAO.Recordset ' recordset for category data

  ' open the recordset for categories
  Set rst = CurrentDb.TableDefs!Categories.OpenRecordset

  ' loop through the rows in the recordset
  rst.MoveFirst
  Do Until rst.EOF
    With Me.xProductTreeview.Nodes.Add(Text:=rst!CategoryName, _
        Key:="Cat=" & CStr(rst!CategoryID))
      .Expanded = True
    End With

    rst.MoveNext
  Loop
End Sub

Look at the lines in bold.

We've enclosed the arguments to the Add method in parentheses. Add is really a function that returns the node that was added. In previous episodes we didn't use the return value- by leaving off the parentheses we used it like a subroutine instead of a function and ignored any result. This time, by using the parentheses and putting With in front of it we create a block of code where anything starting with a . will refer back to the new node we just created.

Withing that With block we've added code that sets the new node's Expanded property to True. That's all it takes to expand the node. When the tree is finished any nodes under that category node will be visible. The user can still collapse it if the like, but it starts out expanded.

The next changes are to the subroutine that adds products to the category nodes. Here's the new version:

Private Sub CreateProductNodes()
  Dim rst As DAO.Recordset ' recordset for product data
  
  ' open the recordset for products
  Set rst = CurrentDb.TableDefs!Products.OpenRecordset
  
  ' loop through the rows in the recordset
  rst.MoveFirst
  Do Until rst.EOF
    With Me.xProductTreeview.Nodes.Add(Relationship:=tvwChild, _
        Relative:="Cat=" & CStr(rst!CategoryID), _
        Text:=rst!ProductName, Key:="Prod=" & CStr(rst!ProductID))
      If rst!Discontinued Then
        .ForeColor = vbGrayText
      End If

    End With
    rst.MoveNext
  Loop
End Sub

The change here is that discontinued products will be a different colour. The lines in bold make that happen. Note the use of vbGrayText, a System Color Constant that tells Windows to use a color from the users desktop colors, rather than hardcoding a color that might not work for the user. Just like when we created the category nodes we have a with block to allow us to set attributes for the node we just created.

In this episode we covered:

  • setting properties of the treeview that control ts appearance
  • using a with block to set properties of a node after it's created
  • setting the expanded and forecolor properties of a node

In episode 4 we'll add buttons that interact with the treeview. Stay tuned! Check out all the posts in MyTreeviewProject.

12 comments:

Anonymous said...

Thanks!! Looking forward to episode 4

Anonymous said...

Hey, can you give me some direction about create an Unicode TreeView? I want to add Japanese to my Discussion TreeView but it doesn't display right. Thanks!

Stephen said...

DF: I haven't looked at Unicode text at all. Maybe post at www.utteraccess.com and see if anyone there has figured out how to do it!

Anonymous said...

I had an idea but I must use free .ocx file, it contain many control but only TreeView is needed in my business and the coding is different from your tutor. By the way, can you tell me how to register an ocx file automatically? Thanks

eigersoftware said...

Good article. In an Access-application that I made (Match, tracker for UEFA Euro 2008 soccer), I also used the treeview-component for the menu, but at some computers of customers it didn’t work, even after registring the oxc (dll). Any suggestions?
NB: For people interesested in Match, you can download a test-version from my website, see:

http://eigersoftware.tripod.com/match.htm

and I also made a small movie of Match (converted Powerpoint-presentation), see:

http://www.youtube.com/watch?v=WVRnhhIkg3A

Regards,

Marten

SNAKEBOB said...

It's really hard to find out this kind of favor instruction for Treeview control in Access in Korea.
I guess I just found out great source from here.

Thanks very much for the information.

Unknown said...

Stephen,

Did you ever do the episode 6 tutorial? I think a lot of us could benefit from it.

Jack

Stephen said...

Jack-

I'm still hoping to find time for more articles in this series. My original vision was 10 right off and then more. As you've observed, I;m stuck at five and haven't posted anything new in some time. March break is coming soon- so maybe I'll be able to make time then.

Unknown said...

Thanks for getting back. There's a lot of people looking for info on Treeview... and ImageList. Your approach to teaching is logical and appreciated.

Is it possible to have a child node's text as a hyperlink?
I can see using Application.Follow...
but the Node_Click event is for Root node as far as I can see. I'm not sure how to get an event for a child node.

I have a list of names (name at root), Province is child, City is child of Province, URL is child of Name also. How to make URL a Hyperlink? and what event to FollowHyprlink??

Jack

Unknown said...

Stephen, Regarding last post about Hyperlink text. I have sorted it out.

I looked a lesson 3, and derieved method to determine which nodes was clicked, and can FollowHyperlink as needed.

Paulo Oliveira said...

This was very helpful.
But i have one problem that i having trouble solving it, so let's see if you can help (a little bit more).

I'm triyng to use the setuptreeview sub to control 2 treeviews but it's not working:

Private Sub SetupTreeview(obj As MSComctlLib.TreeView)

With Me.obj
.Nodes.Clear
End With
End Sub

If anyone could tell me what's wrong with this i really appreciated.

Walter Gantenbein said...

Paulo

The Code ist correct.

Try the following call:

Private Sub Form_Open(Cancel As Integer)

Dim MyTreeview As MSComctlLib.TreeView


Set MyTreeview = Me!xProductTreeview.Object
SetupTreeview MyTreeview

End Sub

Regards,
Walter