dControls Library

Author : Dan Howard (sproket@total.net)
History: 1.0 December 12, 1998
Initial Release
Additional Programmers:
Peter Rorlick
Gary White
Bowen Moursund
Jim Sare
Ken Mayer
Richard Sandieson

Table of Contents

What's In Here

You should have received as part of the dControls.zip file the following files. Open each one to read about the specifics of each.
 
dForm.cfm Custom base form class
treeview.wfm A form for modifying a dTreeView control
treeview.pop An overridden popup for the dTreeView
dControls.htm This file
fontget.cc A font getter class
ini.cc A very slightly modified version Bowen Moursund's Ini class
ini2.cc An extended Ini class with a few additional behaviors
aspin.cc A new array spin box
colorget.cc A color getter class
fDialog.cc A set of file dialog related classes
dControls.cc The base set of custom controls
treedrop.cc A sort of combobox/treeview hybrid control
dContainers.wfm Sample form showing the containers in this library
dControls.wfm Sample form showing the dControls in this library
dTreeView.wfm Sample form showing the dTreeView in action
fDialog.wfm Sample form showing file dialog classes 
TreeDrop.wfm Sample form showing TreeDropper in action

 

Introduction

This class library had pretty humble beginnings. What started out as a simple set of base controls that I had been using and wanting to publish turned into a rather large set of classes with a fair amount of new technology.

It took me quite a bit longer to finish this than I had expected. As I was working I kept coming up with new things to add. There are several things which I have not included because I just thought of them in the last day or so. I'll play around with them and update this library when they are tested.
 

dForm

This base form class has a few new abilities.

* I added a Repaint Method based on Jim Sare's repaint routines. Simply call form.Repaint() to force the form to redraw itself. Surprisingly fast.

* The form's onGotFocus event is used to attach a reference of the form to _app.ActiveForm. Very useful for debugging from the command window and for MDI apps. Not really my idea. I don't know who thought of it first but it's very useful.

* Sub classing from this form gives you a new Init method. Thanks to Peter Rorlick for the idea. If you define an Init with any control on this form, it will fire "before" the form opens. Much better than using the form's or the control's OnOpen which can cause unneeded screen flicker. As far as any sub classed form is concerned, Init is a replacement for both Open and ReadModal. There are plenty of examples of it's use in this library.

* The method Iterate is available. This I found to be a very useful method. It's purpose is to iterate through all controls on a form (including controls in containers and notebooks) and fire a code block on each. DForm uses this to handle the Init methods:

Class::Iterate({|o|; iif(type("o.Init") # "U", o.Init(),false) })
This looks at every control and - if there is an Init method - it fires it. This is very powerful and "this" will be passed correctly. You can also specify a starting object if you want to simply iterate through - lets say - a notepad object. You could use:
form.Iterate({|o|; msgbox(o.name) } ,form.NotePad1)
I liked this method so much I included it in the dTreeView object as well. This idea could also be used in a custom array, rowset, string or any other type of object that uses loops.
 

DControls

Some of the controls have some visual behavior. Initially I wanted to have no visual properties for these controls whatsoever but I left them in anyway.  I like Arial 8 font because it's the font used with MS Office 97. You can simply remove the visual code if you use your own color/font scheme.

Most of the visual code was inspired by the work of Peter Rorlick. He's got a flair for making forms look great. Thanks Pete.

If a control is not listed here it's because there are no additional behaviors for them except for those noted here.

All of the standard controls have the following additional methods.
 

FullName

Returns a fully qualified name of the object. Instead of ENTRYFIELD1 it will return: FORM.MYCONTAINER.ENTRYFIELD1. Useful for macros etc.
 

SetValue

This is based on Peter Rorlick's and my idea of using custom field objects for properties. This method will create one for you. e.g..
this.SetValue("MyFieldProperty",10)
The second parameter is the initial value you want to assign to the field. Fields used in this manner are quite powerful and flexible. You can assign things to fields that could not be stored in a table - like arrays or even other objects.

Try this at the command window.

f = New Field()
f.Value := {"one","two","three"}
?f.Type ---> Array
Nifty!

Fields as properties can be used as accessors for more complex objects. Since fields have beforeGetValue, canChange, onChange  and onGotValue, you can control what goes in and what comes out of your objects. You can even fire events when the property changes! There are several examples of this in this library as well as a more detailed explanation of their use elsewhere in this document.
 

GetValue

This method gets the value of a field property. Not really all that useful. Normally I simply use the Value of the property itself.
e.g.. ? this.MyFieldProperty.Value
I include it for consistency with the Set/Get concept.
 

dEntryField

Simple little thing really. I highlight it with a yellow color and change it's border style when it gets focus.
 

dListBox

It gets a nice yellow background when it has focus.
 

dCombobox

Same yellow background as the dListBox.

dTreeView

* I added an Iterate method which works identically to the form's Iterate method. It performs a code block on all the children in the dTreeView.

e.g.. To expand all treeitems:

this.Iterate({|o|;o.Expanded := true})
* The dTreeView has a Fill method. You pass an array and the dTreeView will be filled with treeitems of the same structure. Ken Mayer wrote a similar FillFromArray method in his treeview from then dUFLP. I came up with the idea independently but when I heard of Ken's version I sneaked a peak at his code and improved mine as a result. Thanks Ken.

* Save/Restore have been added. The save method will create a file with a .TV extention and write all of the information in the treeview to it. The file name will be a long name consisting of the form classname + container names + dTreeView name. The TV file is styled after an INI file and is editable with NotePad.  It's not the most elegant code I've ever written but it works. Apparently this will be implemented in the next in-line anyway.

* OnRightMouseDown has been added to implement a new popup for this object. I added an "Edit Tree" option which calls another form allowing you to further customize the treeview. I've not used the treeview for interactive purposes but I've seen some posts from users on the newsgroups who have done so. This adds a little more user interaction to the stock treeview.
This is using an interesting technique. Instead of bothering with the form's popup property I simply create a custom popup and programmatically open it with the popup's Open method. Gary White was the first person I've seen use this technique so I'm crediting him for it. This is an excellent idea as you can use this to easily add popups to virtually any control. There are a couple of issues you should be aware of. Popup positions seem to only accept the "char" metric. Also there's a bug with the enabled property. The menu items are always enabled no matter what the setting is. :(

* I replaced the very oddly named "disablePopup" property with the slightly more normal name "popupEnable". It's more consistant with the editor object's popup property name.

dEditor

Same yellow background as the dListBox.

dGrid

Same yellow background as the dListBox.  It becomes a dropshadow when it gets focus. I also dynamically create a rectangle to cover up the ugly bleeding that Vdb grids are famous for.  Thanks to Richard Sandieson for the idea.
 

dContainer

The dContainer creates a field property called Setter which I use as an accessor property for any complex controls I create. See the DContaners section for details. This class also has an Init method which hides the dContainer's border at run time.

dObject

This is an improved base object to the stock one.

* It's subclassed from the DataModRef object so it can be placed on the component pallet. Makes an excellent place holder for generic non visual code that you want to associate with a form. I've only begun to explore the potential of this technique.

* This contains the FullName, SetValue and GetValue methods described at the top of this section.
 

dField

A subclass of the stock field object. This one is used primarily for creating custom field properties of objects. It adds a custom "Owner" property since I can't use the "Parent" property. The SetValue method uses this class.
 

PropertyRowset

This is essentially a left-over from my experimentation's. The only limitation to the field properties was that you couldn't datalink them. Well actually you can but they're read-only when you do so. I experimented with using the SetValue method to attach the field properties to a phony rowset. This is what discovered:

PLUSES
* You can datalink your custom field properties to controls.

MINUSES
* They will become more strictly typed. The Length property will be respected and they will be automatically padded. (debatable minus)

* Trying to code with datalinks is much more confusing. It's harder to see how/where/why field events are firing.

* It definitely adds a good chunk of overhead to what should be a simple process.
 

DContainers

The following containers all use the field property technology. The base dControl class creates a Setter property for this purpose. The idea behind this is to allow you to treat a container as a true single unit. This will greatly simplify the use of complex custom containers on your forms.

Consider the ColorGetter object: The ColorGetter's purpose is to allow the user to get a color. They can type in a color name in the entryfield and the pushbutton will change to that color or they can click on the pushbutton and select a color which will update both the pushbutton and the entryfield. To use this control without the setter property would mean that you would have to override both the entryfield's OnChange and the pushbutton's OnClick in your forms. With the setter property you only have to deal with one event: Setter_OnChange.

Using the setter also let's you assign to the entire container. Run the DContainers.WFM form and type this in the command window:

_app.ActiveForm.ColorGetter1.Setter.Value := "Red"
OR
_app.ActiveForm.ColorGetter1.Setter.Value := "0x0000FF"
Gotchas:

FontGetter

This class is a wrapper around the GetFont dialog box. It allows the user to enter or select fonts. You can either type in a font string i.e. "Arial,10,B" or select a font.  The getfont.cc file also includes a Font class which handles conversions between font strings and object font properties.

ColorGetter

This class is a wrapper around the GetColor dialog box. It allows the user to enter or select a color. You can either type in a color name i.e. Blue" or select a color. This class creates a ColorGet.INI file as well so you can input hex values and associated color names to make it more user friendly.

FileGetter

This class allows the user to enter or select a file. I don't bother doing validation on whether the file exists or not. You could add that code yourself easily.

FilePutter

Pretty much the same as the FileGetter except it calls PutFile instead of GetFile.

DirGetter

This class allows the user to enter or select a directory. I don't bother doing validation on whether the directory exists or not. You could add that code yourself easily.

ImageGetter

This is a subclass of the FileGetter class. It has an additional preview image object attached which updates whenever the image is changed. This class does to validation.

IconGetter

This ugly little thing is used by the TreeViewForm for selecting icons for treeitems.

ArraySpinBox

This class gives you a spinbox with an array of strings as a datasource. It's interesting in that when you assign to it you assign the numerical index to the array but the value returned is the character string. Run DContainers.WFM and try:
_app.ActiveForm.ArraySpinbox1.Setter.Value := 2
?_app.ActiveForm.ArraySpinbox1.Setter.Value --> "Two"
Property morphing in action!

TreeDropper

This is a combobox type of object which uses a treeview instead of a drop down list.  Lets you make fancy comboboxes. This control was inspired by the one in the Inspector. It could use some improvements. Let me know what you think.

New Technologies To Play With

Below are a few additional things worth looking at. You may want to experiment further with them.

Collection Objects

I came up with a better technique for saving property values to be restored later. In the past if I wanted to lets say - save the
Top property of a pushbutton I would do this:
form.PushButton1.OldTop = form.PushButton1.Top
then later I would reverse it. I never liked doing this because it creates all kinds of extra properties. I always worried that someone else using this technique could cause a conflicts with my objects. So what I do now is create a custom property called SavedSettings as a collection object.
Class MyEntryField(f) of entryfield(f)
   this.SavedSetings = New Object()
endclass
Then I simply add to the object things that I want to save for later.  A collection is simply an object used to store stuff into. Like a bag - you throw stuff into it and then take out the things as you need them. I'm working on a true collection object which I'll include in the next release. This technique makes your objects more encapsulated than using the "Old" way.
 

Other Possibilities With Iteration

Using the form's iteration method you can create new event handlers for controls. Consider the Init method. It's kind of like a "BeforeFormOpens" event for each control. You could - for instance - use this technique to create controls which "know" when the form is resized. You've probably noticed that many controls don't have this feature. The form does however. You could write a  form_onSize event of the form which would iterate through all the controls and if there is a "OnFormResize" method defined it would fire it. You can make it so that controls will automatically reposition themselves when the form resizes!

I'll leave that to you to experiment with. If you get this idea working send it to me and I'll include it in the next release of this library.
 

Using PROTECT Within Containers

You'll notice when you look at the source code of the dContainers that I create custom controls for use with them. For example in my FontGetter class I use custom FG_Entryfield, FG_PushButton, etc.  Why? Well you'll notice that in those class definitions I protect the specific properties that I want to use with my containers. You'll see that FG_Pushbutton's onClick is protected. I do this because these are the properties that my containers depend on and there is no need for you (the user) to see them. This makes the containers much more encapsulated - more bullet proof if you like.

More About Popups

I wonder if it's possible to override the Editor's popup the same way I did with the treeview's popup?

This is the basic logic of creating popups.

   function onRightMouseDown(flags, col, row)
      if type("form.MyPopup") = "U"         
         set procedure to My.POP additive
         form.MyPopUp = New MyPopip(form,"MyPop")
         // Works only with the CHAR metric
         form.MyPopup.top = this.Top + row + 1.3
         form.MyPopup.left = this.Left + col + 11.3
         form.MyPopup.Open()
      endif
   return

Summary

Well, quite a few new things to experiment with. Some of these topics may seem advanced if you are either new to Visual dBASE or OOP. If you have any questions about this document let me know. I'm happy to be able to improve this as needed.

Support Information

If you encounter bugs or other problems with the code, don't bug the contributing programmers. Their code was the basis for some of my code but I've probably modified it extensively. Contact me Dan Howard (sproket@total.net).

I write everything using Char metrics. If you use another metric you'll probably have some problems with object positioning etc. Would it be nice to have a built in converter? Anyone want to write one?
 

Disclaimer

This software is donated to the public domain. You may modify this code in anyway you wish however the author reserves the right to whatever official version is published. If you have  further ideas/bug reports/additions then contact the author and your name will be added to the contributors if your idea is implemented.

The author accepts no responsibility for the use or consequences of the use of this software.