The
TreeDropper Control
A
custom class written for Visual dBASE 7.01
by Dan Howard
- Date: March, 1999
What
is the TreeDropper control?
The TreeDropper is a new control
that is similar in functionality to the stock combobox found in dBASE.
This one though will drop down a treeview instead of the simple list. This
takes advantage of two of the most powerful classes in dBASE: the container
class and the treeview class.
Features
What’s
included in the package
-
DForm.cfm –
A custom form class that the TreeDropper needs.
-
TreeDrop.cc –
The source for the TreeDropper control.
-
TreeDrop.wfm –
A sample form to demonstrate this control.
-
down.bmp -
A bitmap for the drop button.
-
back.png -
The background for the sample form.
-
arrow.ico,
darrow.ico,
dot.ico,
udarrow.ico - Icons used
in the sample form. Sent to me by George Burt. Thanks George!
Installation
Simply extract all of the files from
bu01tree.zip into a directory,
open up dBASE, locate the directory you unzipped the files to and double-click
on TreeDrop.wfm in
the Navigator to see the sample form.
How
to use the TreeDropper on your own forms
The first thing you should do is open
up Dform.cfm in
the Source editor and review the added methods in it. This base form class
handles an important feature of the TreeDropper and any other control you
may ever use. DForm uses iteration to look for a special method called
INIT() in either the form
or any control on the form. If it finds one, it runs it before the
form opens. This gives you the advantage that all initialization of the
form itself or any control on the form can be done while the form is off
screen. This prevents a lot of flickering when you open the form. The DForm
class can be implemented as a base class for your own forms or you can
simply cut the code out and paste it in you own base form class. DForm
has a few other goodies as well!
To ensure that the TreeDropper
is always loaded when dBASE starts up, open up a form in the form designer,
right-click on the component pallet and choose Setup Custom Components
from the menu. You’ll see a dialog box which lists all of the custom class
files which you currently use. Click the Add button
and locate the TreeDrop.cc file
then click OK. Now this class will be added to your
vdb.ini file and will
be automatically loaded next time you fire up dBASE.
Now go to the Custom tab
on the Component pallet, find the TreeDropper object and drag in onto the
form’s surface. What you’ll see is something similar to what is shown in
the image below. Notice that the
button is missing? This is because I create this button dynamically
before the form opens and position it at the right edge of the entryfield.
This saves you the trouble of having to reposition it yourself in the designer.
You also don’t have to worry about the widths of the entryfield or the
treeview itself. They will resize themselves to the width of the container.
Notice also the thin line under the
entryfield. This is the treeview itself. I make it small so as not to take
up too much space on your form. This treeview will be resized and repositioned
at run-time depending on the width of the TreeDropper control itself. It’s
available in the designer so you can customize the treeview any way you
want.
If you want to add treeitems to
the TreeDropper in the form designer, use your mouse to expand the container
downward and then expand the treeview. You can use this treeview the same
as any standard one in the Designer.
You can add, edit and delete items
all you want and the information will be saved in your form. There are
other ways of filling the treeview with items but I’ll get to that later.
The TreeDropper has a few custom
properties which you can use as well. Since dBASE does not yet support
custom property streaming they have to be defined in either the
Init() or
onOpen() methods of your
form.
They are:
-
DropDownHeight:
This property specifies the actual height of the treeview when it drops
down (in char metrics).
It defaults to 7.
-
ShowParents:
This property affects how the result of the user’s selection appears in
the entryfield. When set to true,
the current selection’s text and each of its parent’s texts will appear
in the entryfield. When set to false
it shows only the current
selection’s text. It defaults to
false.
-
Separator:
This property is used in conjunction with the
ShowParents property.
It’s a string which will show as a separator between each treeitem’s text
in the entryfield. It defaults to “.”.
-
Selected:
This is a reference to the currently selected treeitem object.
-
IsOpen:
This property tells you whether a particular TreeDropper is open or not.
Do
not assign to this property. It’s for read-only purposes.
-
OnChange:
If defined, this will fire when the user changes their selection on the
TreeDropper.
See the TreeDropForm (TreeDrop.WFM)
to see how these are implemented.
Some
ideas for its use
-
This control could be one component
in an attractive help system.
-
You could use this to replace a regular
combobox. How about a list of states or provinces with their flags as images?
-
It could be used to show parents and
relations in genealogy.
-
If you’re brave you might consider
the concept of making this control data-aware. Very doable.
Nuts
& Bolts
This class implements some interesting
techniques. One of the challenges posed by this control is it’s z-order.
If it is dropped below pushbutton for instance, the treeview will show
up below it. Not exactly what we want. My solution is to use the Win API
SetParent() function.
This function will attach an object to another object. It takes two
hWND properties as parameters.
See the TreeDropper code to see how it works. There is a limitation
to this: a combobox’s list will drop outside the form if necessary
— the TreeDropper’s list won’t. I haven’t quite figured out how
to do this yet so if you come up with a way, let me know.
I like the ability to create and
move objects before the form opens. This allows you to define two
types of functionality: design-time and run-time. It also simplifies the
use of containers in the form designer as you don’t have to deal with repositioning
all of the container’s components. You can define your own rules
on how the container’s components will look at run-time.
I create a form property called
ActiveTreeDropper in this class. It is a pointer to the currently active
TreeDropper control. I use it to ensure that only one TreeDropper is open
at a time.
I also added several new methods
to the treeview which you could move into your own base treeview class.
-
Fill():
This method will fill the treeview from an array. The array can be a simple
array, an array containing other arrays or an array of TreeItemInfo objects.
The TreeItemInfo objects are simple objects to hold information about a
treeitem like its image properties etc. You could create a table
to store this information and then use it to fill the treeview.
-
Iterate():
This method will run a code block against each item in the treeview. You
can see how to use this in the sample form. The TreeDropper uses
it to make sure that every treeitem is expanded before it opens up.
-
AddItem():
This method simplifies adding new treeitems to the treeview for you. Simply
call this method with a string parameter and the treeitem will be created
with that text. The 2nd parameter is the parent under which you want to
create the treeitem. It defaults to the treeview itself.
Limitations
Since the lists are objects and not
simple strings you should probably not use this control for very large
lists. A few hundred will be ok but more than that and you could
run out of memory.
I didn’t spend much time testing
the positioning logic with different fonts. I normally use
Arial 8 pt for my forms
and controls. You might find that the drop button doesn’t position itself
correctly if you standardize on a different font. Improvements in this
area would be greatly appreciated. Also this only works with
char metrics. It would
need more tweaking to handle others.
The
DropDownHeight property
doesn’t really do what I want. This should specify how many items will
appear in the treeview regardless of the font or font sizes used. Currently
it simply sets the treeview’s height to it’s value.
Finally I would definitely not
call this a production control. It does the basics but there is a
lot of extra functionality that could be added to it. Let me know of any
ideas you have for this control and I’ll implement them for the next version.
Conclusion
You can see that the container class
gives us a whole new avenue for custom control creation. With the
container we can create composite objects. You can create virtually
any type of control that you can dream up. If you’re looking for inspiration
for your own controls, have a look at the software sitting on your desktop
now. Think about how other controls in different applications
operate. How does the user interact with them? How do they interact with
each other? When you see something you like, sit down, plan
it out and then code away. The possibilities are endless!
Known
Issues:
In Visual dBASE 7.01 there is a bug
with the releaseAllChildren() method.
If you add treeitems in the Designer and then try to release them in your
code they will still be visible in the inspector.
Workaround: Simply
use the treeview either as a static treeview or a dynamic one. Don’t mix
them. In other words — if you know that the contents of your treeviews
are going to change at some time, don’t add items to them in the designer
— use the Fill() method instead.
To download the TreeDropper
Control code, click here
(it’s a 34Kb zipped file)
Dan
Howard has been an independent software developer for almost 10 years using
Clipper for DOS and Visual dBASE for Windows. He can be contacted
at: sproket_dBulletin_@total.net (take out “_dBulletin_”).