A Native TreeView (Part II)

Version: 1.00.10 - Last Update: Tuesday the 29th, September 2009 - 11:20:00

Previous ChapterComplex Controls Home (TOC)Next Chapter

FoxPro Rocks! This is all about how to create complex controls using native VFP only!


In part one we talked about the vision creating a VFP based TreeView without using any “external” C++ but pure native VFP code. Today we will look at some of the basics we will incorporate in the final solution. As usual VFP offers more than one way to achieve our aim, but it turned out to be suited best to use a VFP Grid-based approach to set up something that looks and behaves like a native MS TreeView ActiveX control.

Why should we use a Grid?

Well, first of all, VFP’s Grid is a data-aware (table-bound) control. This gives us the chance to utilize VFP’s data engine to speed up a lot of things internally and (as a very nice side effect) keep the memory footprint of such a control as small as possible. Secondly, a Grid takes away the need for implementing vertical positioning (the nodes) ourselves. We get scrolling for free; we are able to hook in on all mouse- and keyboard events and, last but not least, are able to control (almost) any other Grid’s behavior programmatically. The only thing left is, how to create the nodes and make them to show up like a TreeView (or a popup menu first) when listed in our Grid’s column. Well, that sounds like a pretty trivial task, but don't let yourself be fooled!

Why NOT to use a Grid? There are no reasons at all – I never saw neither a native Grid-based solution, nor any other – now less than ever! :-)

What should I know about VFP Grids to follow this track? Well, to put in bluntly: everything. The rest you will find out here :-))) Okay, no - just kidding! But there really are some strange Grid behaviors one should be aware of. So let’s talk about some of these oddities first.

One of the really cool things you can do within a VFP Grid is, you can instantiate almost any kind of object in its columns. As you might have read on Craig Boyds Blog, the key to realize stunning effects within a Grid is to use a VFP container “first” instead of the (default) textbox, or any other plain-vanilla VFP input controls you would normally instantiate.

Into this parent container, let’s call it the NODE container from now on (or simply THE NODE), we can put as much child controls as we want. The only difficulty to face is that we have to find some event to hook on to refresh our NODE child objects inside. The solution (the cool trick) to this is to hook on the container’s BackStyle property by adding a BackStyle_Access() method to the container class. Within this access method we can implement code that gets fired every time VFP queries this value.

Let’s get started with some alpha-testing environment setup to check out some of the above. Follow the steps listed below:

Create a very simple table and fill in some values:

CREATE TABLE test FREE (pCaption C(20))
FOR lnLoop = 1 TO 99
    INSERT INTO test (pCaption);
        VALUES ("Test " + PADL(m.lnLoop,2,"0"))

We will extend this table later in the game. At the moment we only need a handful of records to fill our alpha-test grid.

Create a Grid class and add an instance of it to a VFP Form (SCX). If you run the Form without altering any Grid properties you will end up like shown below.

Figure #1: Running a Grid with Defaults

Now let’s create our first NODE container class and a caption class (VFP label based). Set the following label properties:

  • AutoSize = .T.

  • BackStyle = 0

  • Caption = (none)

Add the Label- and the Node container classes to your test class library like shown below.

Figure #2: Adding first Node (plus Caption Item)

Finally let’s assemble the parts. First of all set the Grid’s ColumnCount property to 1. This will add a default class based “column1” to your grid. Set the following Column1 properties:

Sparse = .F. (this is mandatory!)
ControlSource = “pCaption”

You can set the following Grid properties, too:

RecordSource = “Test”

Note that setting Control- and Record-Sources isn’t necessary in our case! If you want to ensure the correct cursor is selected when your grid instantiates, setup your Test-Form’s data-environment accordingly.

Before adding the new NODE container to our Grid’s Column#1, we have to add the “magic” BackStyle_Access() functionality. Open the Node container class in the VFP’s class designer and add the “backstyle_access” method manually. Now go and add the following lines to your new BackStyle_Access() method:

? "BackStyle_Access RecNo# =" + TRANSFORM(RECNO())

Open the container’s Refresh() method and add the following lines:

LPARAMETERS tlBackStyle AS Boolean
THIS.oCaption.Caption = ALLTRIM(pCaption)

Finally add an instance of your caption label to the container and name it “oCaption”.

Set the Node container properties like this:

BackStyle = 0
BorderWidth = 0

This will eliminate the display of the container’s border.

Now it’s time to add the Node container to our Grid’s column#1 class. You should know how to do that. Note that there still is the default textbox1 object in our column. So first delete that, before adding the node container. After having done this you may want to think about some meaningful renaming of your objects. Figure #3 shows the containment and naming on my computer.

Figure #3: Object hierarchy and naming

Before running the Test-Form you should set the Form’s “AllowOutput” property to .F. otherwise the first line we put in our Node container’s BackStyle_Access() method wouldn’t echo the monitored values to VFP’s Screen.

Some final comments about the BackStyle_Access() method code.

First of all: Never forget to return the BackStyle value at the end of your hook. A missing “RETURN THIS.BackStyle” will return the Boolean .T. by default, which in turn will cause VFP to start complaining (this is pretty wired to debug!)

If you’re wondering why I didn’t code the Label’s caption refresh in the BackStyle_Access method directly, maybe you remember this rule of thumb: “Never implement behaviour inside VFP’s events, but always let events call methods!”

When “decorating” our container’s BackStyle property with the access method we are creating some kind of new VFP-internal event. This “access event” gets triggered each time VFP internally queries the Node container’s BackStyle value. So it sounds to be a good idea to let this event trigger our implementation, which (at this early development stage) we’ve decided to put into the already existing Refresh() method (to save a little time).

The other reason for doing so is, that _Access() methods get flagged PROTECTED by VFP; maybe we want to call/raise our implemented functionality from the outside later in the game. This is why to move all implementation into discrete public (callable) methods.

Calling the Node’s Refresh() method passing some parameter enables us to check where the call was coming from. Okay, with that I anticipated some findings we will talk about later :-)

Finally RUN your Test-Form

Don’t feel frustrated by the poor performance. That’s what we wanted when adding some very slow Screen-output. Start clicking around and carefully watch the echoed record numbers! You will find the first “oddity” we will fix in part three.

Previous ChapterComplex Controls Home (TOC)Next Chapter

1 comment:

  1. This is starting to look coool. Cant wait for part III.