Working with the Form Controls
in dBASE

Last Modified: January, 2004
Ken Mayer, Senior SQA Engineer
dBASE, Inc.

Example files available in forms.zip


NOTE: This document was originally written for Visual dBASE 7.0/7.01, it has been updated for dBASE Plus to include information about new properties, events, etc., and any new controls since the document was first written. If a control is new it will be noted.

In addition, this document refers to dBASE a lot, but unless it is about a dBASE Plus specific aspect, the text can be used for Visual dBASE 7.0 through Visual dB2K release 0.4. Note however that some bugs that were fixed in Visual dBASE 7.5 are no longer referenced here ...


This document is aimed at helping a developer use the form controls in dBASE. Now, this seems like it might be a silly thing to write, as most people can figure out how to use an entryfield when they design their forms, but there are a lot of questions on the dBASE Newsgroups concerning various controls. Some of the questions may seem pretty basic to someone who has been using dBASE or any other Windows product for some time, and some of the questions are fairly advanced. This is an attempt to explain the basics of most of these controls, as well as an attempt to get into some of the more interesting uses that some controls may be put to ...

The following controls are not covered in this document:

There are sample forms included with this document that show the use of some of the controls, as well as various properties, events and methods of those controls. If you are uncertain how a control works, run the forms and experiment, that's how this document and sample forms were created, experimentation.

Before we get into a discussion of the different controls covered in this document, it's opinion time ... This is an opinion of mine that is shared by most, if not all, experienced dBASE developers: Never, ever use a stock dBASE control for anything! Sooner, or later, you will regret having done so. You will run across something that you need to change on all your forms. That will be a monstrous task, unless you heed this advice. Before you create your first form, you should create a set of base custom controls, upon which all others are based (see the Custom Classes HOW TO for details). The same can be said for your forms (see the Custom Forms HOW TO for details). Never use the stock controls. For the sake of simplicity, this document will use stock controls, but you should check out the HOW TO on Custom Classes to learn something about creating your own custom controls.


NOTE: With all the emphasis on using custom controls above, one question might be "How do I change my current controls on forms to custom ones?" Assuming you have a custom control file with your own custom controls, you could open the source code of the form in the source editor (right click and select "Open in Source Editor" (or press after a single click on the icon)). At the beginning of the source code for your form (see below) add the appropriate SET PROCEDURE statement:

       class comboboxForm of FORM
       set procedure to mycontrols.cc additive
       with (this)
          [code snipped]
   
Then use the search and replace options (Ctrl+R) to replace all occurances of specific words with then name of your custom controls, for example -- if using formcntl.cc in the dUFLP library (noted periodically throughout this document), you might replace all occurrances of the words "NEW ENTRYFIELD" with "NEW kmCustEntryField". From this point on the form will use any properties, events and/or methods you have defined for that custom entryfield.

There are two ways to get controls onto a form while using the form designer. The first is to use the controls that appear in the Component Palette. The second is, once you have a query or datamodule on your form that is set to open at least one table, is to drag the controls that are already datalinked to fields in the tables to the surface of the form.


NOTE: The form designer has a built-in assignment of specific field types to specific controls. When you first install dBASE, you will find that, for example, spinboxes are assigned by default to numeric and date fields. This may not be desireable -- and you can change this in the form designer. Go to the menu (when you have a form in the designer) and select File and then select "Associate Component Types". (You can also do this by right clicking on the field palette and selecting "Associate Component Types" from the popup there.) Note that Numeric and Date types have spinboxes associated with them -- if you select Entryfields for these, this will affect new controls you place in your application until you change the settings back. (This is mentioned because I have very few situations where I prefer spinboxes for numeric and/or date fields, and find I would rather use entryfields for these types. From discussions in the newsgroups, I'm not the only one who feels this way ...)

Common Properties, Events, and Methods

A form control is modified and interacted with using properties, events and methods. This section will cover some of the more useful properties, events and methods which are common to many form controls. You should refer to online help in dBASE for additional information on the properties, events and methods covered here, as well as those not covered.

Properties, events and methods that are specific to a control are covered in the discussion of that control.

Properties
Properties are the attributes of controls and include among others font, identification, size and visual properties. A property setting can be changed programmatically as well as in the form designer. The setting is normally a single value, for example, a character string, number or reference to another object.

Events
Events are something that controls respond to, a mouse click, a control getting focus, a change in the control's value, etc. Events are always "firing" provided the event is occurring, however, in order to have something happen, you have to "hook" code to the event. (Windows itself is really just a big "event" handler -- it does not do anything until some event is fired ...)

Events whose name begin with ON, such as onOpen, occur after the action to which they refer. Thus, the onOpen event occurs after the form is opened. Events that begin with CAN, such as canClose, must return a logical value that will determine whether the indicated action may proceed. So, a form's canClose event must return true before the form may close. Returning a value of false will prevent the form from closing. Failure to return a Boolean value may yield unpredictable results.

Note that there are a set of Mouse events not discussed here (onLeftDblClick, onLeftMouseDown, onLeftMouseUp, etc.). These are pretty obvious when they fire if you look at the names of the events. The onLeftMouseUp (same for onMiddleMouseUp and onRightMouseUp) has the interesting effect that it often gets called twice on a double-click ... if you attempt to use both, you may be shooting yourself in the foot ...

Methods
Methods are code that performs an action. Controls have a number of built in methods which are called through the control, i.e., form.entryfield1.copy(). Code which responds to a control's event is also a method, but not necessarily a method of the control (usually a method of the form).

One thing you should remember is that if you use the built-in methods as a programming hook to insert your own code (this is called over-riding the method), you will nearly always want to call the original built-in method before, during, or after your code. (This would be achieved by entering: SUPER::methodname() into the code ...)

There are more methods than setFocus(), but most of them are either specific to the control, or are pretty straightforward. Some of these are clipboard specific (copy, cut, paste, undo). Some deal with moving the control (move()). You can also explicitly release a control from memory using the release() method. It didn't seem like it was necessary to really spend a lot of time on these.


Data Entry Related Controls

The controls discussed in this section are here because you can directly datalink your fields to these controls. Some of what is discussed below for these controls may seem pretty basic and possibly obvious to you. Hang in there -- I hope to cover some of the more interesting aspects of these controls as we go.

Form Appearance Controls

The following controls are used to enhance the appearance of a form. The Text control is included here, rather than under the datalinked controls above because it's not generally actually datalinked, although text controls are usually used to give the user a hint of what the datalinked (or other) control is used for.

  • Text
    The text control is one of the most often used controls in dBASE, as it is usually placed next to some other control to give the user a visual cue what the field is or to give some other definition to what's happening on the form. The text control is a highly flexible, very versatile control. It will evaluate HTML tags, rotate text, maybe even sing and dance. The price for all this power is that there is a significant amount of baggage that gets dragged along with a text object, whether you choose to use it all, or not. This, in most cases, will not be significant. However, large, complex forms that contain a lot of text objects will, in all likelihood, see performance degradation. If you are using a lot of text objects on a form and are not satisfied with the performance with respect to the time required to open the form, you may want to use textlabel controls in place of text controls.

    HTML: It has been noted that the text control does not completely evaluate all HTML tags the way you might expect. This is true. The only reason this ability exists at all is that dBASE and (the no-longer supported) IntraBuilder products (from Borland) share some of the same codebase. IntraBuilder was designed to put data out on the web, and so the text control is able to handle some HTML in the designer. You can insert any standard HTML tags into a text control. They may not evaluate the way you would expect, UNLESS you use the "Save as HTML" option in the form designer. If you then view the HTML that is output in a standard browser, you should see EXACTLY what you expect.

    Tags that do not work quite properly in dBASE include:

    • <BR> -- this is the line break tag -- the only real problem with this one is that you must include a space after the tag -- and it will act properly. Otherwise you may get some really unusual effects.
    • <P> -- the paragraph tag -- the difference between this and the <BR> tag is that the paragraph, in standard HTML, adds a blank line. This does not appear in dBASE. As noted above, if you stream this out to HTML, it works properly in any browser ...
    • Evaluation of special characters using the ampersand (&) character -- this includes any of the upper ANSI characters, such as ö (o with an umlaut). To use those, you need to have a handy-dandy ANSI chart available so you can look them up, and enter them using the numeric keypad on your keyboard. The character shown here (o with an umlaut) is ANSI 0246, so try holding the ALT key and using the numeric keypad, type 0246 and then let go of the key ... (if your language driver is set to an ANSI driver, this will work as advertised -- if your language driver is an ASCII (DOS) driver, the characters are mapped differently ... check an ASCII chart).

      The reason the ampersand does not work the way you expect is that dBASE still allows the use of the ampersand to work to create an accelerator keystroke for a control (see below -- prefixEnable). If you want to display an ampersand in your text control, you will either want to use two ampersands (&&) or make sure that the prefixEnable property is set to false.

    There are undoubtedly more that do not work as expected (such as HTML table tags). dBASE is not meant to be a complete HTML engine ...

    As a side note, if you wish to generate HTML pages you may wish to use the custom class in the dUFLP library: HTMLCLAS.CC -- you can use it to generate HTML "on the fly" ... (it has most of the standard HTML 3.2 tags and attributes)

    Images and Text Controls
    In addition to the above, it is possible to place an image in the HTML used by the text control. One of the developers of dBASE notes the following:

    "There is a dialog in design mode ( Format | Define Image ) which helps in constructing the HTML tag for an image when doing an edit-in-place of a Text object. (With a Text object on the design surface, click to select getting black handles, click again to get white handles.)

    "Note that when designing the content of the Text object, you can drag and drop an image file from the Navigator into the Text object as another way to create the reference. (This drag and drop will also work to add an image to the field text displayed in a Memo Editor.) Once the image is in the text, you can select it to modify the HTML tag using the dialog or to drag & drop it elsewhere in the text changing its anchor point.

    "Each image referenced by the HTML tag has to be in a separate file (since nothing was done to extend HTML syntax to provide for alternative image sources such as a table field)." -- BJ


    • The wrap property is used for text controls where you need to display more information than can be shown on one line. Setting wrap to true will cause the text to wrap within the height and width of the text control.

    • A variety of properties really only are germane in the report designer: fixed, leading, marginHorizontal, marginVertical, suppressIfBlank, suppressIfDuplicate, tracking, trackJustifyThreshold, variableHeight, and verticalJustifyLimit ... for the purpose of form design, you can ignore these -- setting them will accomplish little in the appearance of your controls on a form.

    • The rotate property can be useful to display text for logo purposes - with a large font, you can rotate text 90 degrees or 270 degrees, and have it readable ... if you rotate the text 180 degrees it will be upside-down -- while you may want to do that, most users may find it a tad confusing.

    • The text property is the text control's equivalent of the value property for some of the other controls -- this is what is displayed on the form.

    • The alignHorizontal, alignment and alignVertical properties are used to line the text up the way you want within the height and width properties.

    • The prefixEnable property is used to set the prefix ability of the text control -- basically, it determines whether or not the ampersand (&) character designates an accelerator keystroke (for example, if a text control is on the left of an entryfield, and you use the ampersand next to the letter 'F', the text might appear in the inspector like: &First Name:, but on the form it would appear as: First Name:. What this means is using <Alt>F as a keystroke would put focus on the entryfield. (For this to work properly, the text control must come before the entryfield in the Z-Order and nothing can come between them ...) If you set this property to false, then the ampersand will simply appear as part of the text.

    • The getTextExtent method returns the length of a text object based on the text, the font properties, and the metrics being used. This has very limited usage for most applications ...

  • TextLabel
      Added in Visual dBASE 7.5
    The textLabel control was created for Visual dBASE 7.5, and is usable there and in all versions of dBASE released since. It is simply a limited version of the text control -- no HTML, and a bit of other functionality is not there. This makes it a leaner text control that uses less resources. Unless you need the HTML abilities of the text control, you should consider using a textLabel instead.

  • Line
    The line control is just that -- it displays a line on a form.

    • The pen property is used to define if the line is solid or uses a pattern, and the width property is used to determine the width of the line ...

  • Image
    Image controls are used to display images on forms. These are not images that can be modified by your users.

    • The dataSource property is used to determine where the image "comes from". You can get the image from a resource file (a .DLL), from a file, or from a field in a table -- the confusing factor with the field is that the inspector shows "BINARY" as the option -- it refers to a binary FIELD ...

    • The alignment property is used to determine how to display the image. If you use the default "0 - Stretch" it means that the image will be stretched to fit within the height and width of the control. This can create some very interesting effects,. You can force the image to the top left of the control, to use the "aspect ratio" meaning no matter what the size, it will not be distorted, to center within the image control, or to use "True Size" (which can make for some interesting effects as well, if the image's true size is huge ...).

      An interesting thing to note is that if you have the patience to work out the details, you can actually create (or use) an image as an imageMap, kind of like some of the HTML pages you see on the web. The hard part is knowing exactly where the mouse is on the image at any one point. But, with a bit of work, the onLeftMouseDown and/or onLeftMouseUp events can be used to work like one of these imagesMaps.

  • Shape
    The shape control is an interesting one. You can select several "shapeStyles" and create an interesting logo for an application by placing text on top of a shape, or you can overlap shapes and so on ...

    • The colorNormal property uses two colors -- foreground is the border, and the background is the actual shape color.

    • The penStyle property is the same as the pen property of the line control.

    • The penWidth property is the same as the width property of the line control. If penWidth is greater than 1, chances are good that the only penStyle that will work is Solid.

    • The shapeStyle property is what determines what the shape will be (you can have squares, circles, rectangles, ellipses ...).

    SHAPE SAMPLE FORM:

    • SHAPE.WFM -- pretty basic, a couple of sets of radiobuttons are used to show how the penStyle and shapeStyle properties work.

  • Rectangle
    Rectangles are different from "shapes", but only barely. I use rectangles a lot to group controls like radioButtons and such, to make it obvious that they are a group.

    • The patternStyle property can be used to give your rectangle a pattern (lines across the background).

    RECTANGLE SAMPLE FORM:

    • RECTANGL.WFM -- pretty basic, a couple of sets of radiobuttons are used to show how the borderStyle and patternStyle properties work. (As a side note, the borderStyle is shown here -- the exact same option is used for nearly all of the visual controls, so if you want to see the different styles, this is one place to try it out.)

Misc. Controls

Just because these controls are grouped under "Misc." does not mean that they are not important to understand. The Pushbutton, Tabbox, Container and Notebook controls are ones that are used in all my own applications. The Progress control is one I use on special forms, and so on ... These controls are grouped here only because they didn't quite fit into the other two categories ...

  • Pushbutton
    The pushbutton is a control used to give the user a method of interacting with the form. Pushbuttons are often used to navigate in a rowset, place the user in edit or append modes, to save or cancel changes to a rowset, delete rows, exit forms, and much much more.

    • The default property is used when you have a set of buttons on a form and you wish one of them to be the "default" -- which means that this pushbutton will be "pushed" if the user presses the <Enter> key on the keyboard. This is handy for dialog boxes that you design ... Regardless of the default property, if another pushbutton has focus when the enter key (or spacebar) is pressed, the button with focus is the one which will react as if pushed.

    • The toggle property allows you to create a button that has an up and a down position -- and it will stay in that position until it is clicked a second time. The default position is up, and in that position, the value property (noted below) is false. If the button is down, the value property of the button is true. This can be useful for a form that has, say, a parent/child relationship.

      In the onClick event, you could check to see if the pushbutton's value property is false, and set the text to read "Parent" and the form's rowset to point to the parent rowset. If the value property is true, you could set the text to read "Child" and the form's rowset to point to the child rowset. With some imagination, I am sure you could come up with various other ideas as well.

      The TREEVW2.WFM that comes with this HOW TO document uses a lot of pushbuttons that use this and the value properties ...

    • The disabledBitmap, downBitmap, focusBitmap and upBitmap properties are used to set images for each of the states shown. If you use a split bitmap (which some of the images in the RESOURCE.DLL are), you need to be sure that the :2 part of the string used to denote the image is shown (i.e., RESOURCE:2 #resourcenumber). The streaming engine does not always remember that and the image will look very strange without it. You can use .BMP images for your buttons as well.

    • The speedBar property is used to set a pushbutton that cannot get focus (like a set of toolbar buttons). This means you cannot tab to it, and so on. This is useful for some code, where you need to, in the onClick event of the pushbutton, keyboard a value to an entryfield -- the entryfield would still have focus ...

    • The text property is what it sounds like -- you can display text on a pushbutton. You can display text AND an image on a pushbutton. It all depends on how you want your form to look. Note that if you use an ampersand (&) in the text, you will be creating an accelerator key for the button.

    • The value property was discussed above under the toggle property. One caveat -- the form designer will always stream out this property to your form's constructor code. You can generally ignore it, as it doesn't hurt anything. (This is true even with custom pushbuttons -- again, this is a fault in the streaming engine.)

    • The textLeft property can be used if you want the text on the left side of the bitmap image.

    • The onClick event is the one used the most with pushbuttons -- when the user clicks it (with the mouse, or if the "default" property is set and it is not a speedBar button (i.e., the speedBar property is false), the Enter key is pressed ...).

    Pushbuttons are used everywhere in forms, and in the sample forms for this "How To". They are, for the most part pretty straight-forward -- the one thing not obvious is the use of the toggle and value properties, but if you look at some of the sample forms that come with this document, you will see these in action ...

  • Tabbox
    The Tabbox control is usually thought of as something used specifically to change pages of a multi-page form (see HOW TO document titled "How to Work With Multiple Pages Forms" for details), but it is actually quite flexible. The default placement of a tabbox on a form is to anchor it to the bottom -- you can, however, anchor a tabbox elsewhere, and indeed, set it with no anchor at all, and place it where you will on the form.

    • The curSel property is what is used to determine which tab of the tabbox has been clicked.

    • The onSelChange event is fired every time the user clicks on a tab and changes the active tab to a new one. This can be used in a multipage form to change the page of the form:

                  form.pageNo := this.curSel
             
      or it could be used on a form that had a large list of names to go to the first name that matched the letter (this would assume a tabbox with a lot of letters for the tabs).

    TABBOX SAMPLE FORM:

    • TABBOX.WFM generates a 1000 record table on the fly for the purpose of showing some of it's abilities -- see page 2. When the form is closed it can clean up after itself, and the sample table is gone. If you allow it to clean up and remove the table, and then open the form in the designer, click the "ignore" button when asked about the table ...

  • Progress
    The progress bar control is used to show the status of some process.

    The progress bar only really has three properties you need to worry about here. rangeMax, rangeMin and value. You set the rangeMax to the highest value you want it to be at (for example, with a table, you might use the rowset's count() method), the rangeMin would be the lowest value (zero or 1 is good), and as you progress through whatever you are doing, you set the value property. You could simply increment it:

            form.progress1.value++
        
    Which would add one to the value property each time you went through some processing loop.

    Currently the progress bar uses the form titlebar color which is defined by your Windows color schemes. There is no way to change the color of the progress bar programmatically (there is no property) at this time.

    The progress bar also has no "text" abilities -- you cannot define text to be displayed on, or around the progress bar to show your current state. While this would be useful, you could simply add a text control under (or even on) the progress bar that was updated in whatever code you were using to update the progress bar.

    It is possible, when using some methods associated with the database object (packTable, reindex ...), to update a progress bar -- see the onProgress event of the session class in online help. This update is not very good, however ... it only updates every approximately 30% as these methods process.

    SAMPLE FORM:

    • See the SLIDER.WFM form -- there we have a slider control hooked up to a progress bar. The concepts are similar for most uses.

  • Container
    The container class is a very useful class indeed, and if you are not using this, you should be (ok, dogmatic opinions aside ...).

    This particular control is great for several purposes -- one is to group a set of controls together -- by placing them onto a container, those controls can be moved around the form all in the same exact relationship to each other, and you do not have to worry about it. Another purpose is to create your own set of custom controls -- perhaps a set of controls that you will re-use on several forms (see the Knowledgebase article on working with Custom Classes). You can probably determine other reasons to use these. They are quite handy ...

    There are no specific properties that really need mentioning. You may want to set the transparent property to true, so that the form's colors or background image shows through, and working with the borders and such can provide some good effects, but frankly, the point of the container is the "non-visible" parts ...


    Containers can make your references to your objects a bit confusing, until you get used to working with them. If you have a container that has two objects on it, and you wish to refer to the second object from some code associated with the first, you cannot simply refer to the second object as:

               form.object2
           
    The second object is contained in the container, so your reference becomes:

               form.container1.object2
           
    And of course, if you have a container that has another container, and you wish to reference objects from one container to another, the syntax gets even more fun.

    Another way of maneuvering through the object hierarchy is to use the "parent" property:

               this.parent.object2
           

    It takes a bit of getting used to the syntax used with containers, but once you do you will truly understand the power of the Object-Oriented Programming Language in dBL.

    One suggestion -- I have found that it is VERY easy to forget where you are in the object hierarchy -- in most cases, except for the form and query objects (and if the query is contained in a datamodule, then the datamodule is the one without), there is a parent.

    While in the designer, you can use the inspector to find where you are in the object hierarchy -- the combobox at the top of the inspector shows you a layout that can be quite handy for finding what is the parent of what object ...

    You can determine where you are in your code while you are testing it, by using things like:

               msgbox( "Parent of this is: "+this.parent.className,;
                      "Where am I?" )
           
    If that value is what you are looking for, then you know where you are. If it is not, then you may need to stuff another ".parent" into the mix, and try again. It all does make sense after awhile, really!

    If you know where you are in the container structure, you can shorten a container reference:

               c = form.container1.container2
               ? c.text1.text
           

    Custom Containers
    There are some interesting problems in the way that custom containers work -- if you design a custom container under a specific metric, and all the controls are designed for that metric -- if you attempt to use the container on a form with a different metric, it is probably not going to display properly.

    There is no really good workaround for this at this time. I tend to just use the default metric for forms (and controls created for my forms) of Character.

    One workaround that is not optimal (because it changes a form's metric) is to set the form's metric in the container's onDesignOpen:

               this.parent.metric := 6 // Pixels
           
    Of course the problem is that this might mess with something else that the developer is doing.

  • Notebook
    The Notebook control is a special type of container (see above). The notebook control has "pages", which the regular container does not. This allows you to create a form that has multiple pages and within those pages you could have "sub-pages".

    Controls placed on a notebook control will have a pageNo property -- it will refer to the individual page of the notebook that the control will appear on. If you want the control to appear on all pages of the notebook, set the pageNo property to zero.

    • The enabled property is a logical value that is quite useful -- it allows you to enable/disable all controls on the notebook (or container) control, without having to change them individually. When this property is true, all controls refer to their own enabled property's status, when the property is false, ALL controls contained by the notebook (or container) are disabled. This is new to dB2K.

    • The dataSource property is used to set the text for the tabs.

    • The curSel property shows the currently selected tab, which can be useful for programmatic checking ...

    • The focus property is used to determine when to give the tab focus when it's been clicked. (This is a strange one ...)

    • The buttons property is used to show buttons rather than tabs for the notebook.

    • The multiple property specifies whether the notebook can have multiple rows of tabs.

    • The visualStyle property sets the tab styles. See online help for details.

    • The onSelChange event is used to programmatically do something when the current tab changes.

    NOTEBOOK SAMPLE FORM:

    • NOTEBOOK.WFM is an example thrown together to show how a variety of the properties and methods work by clicking pushbuttons ... these will change properties or call methods to modify the treeView control shown.

  • Slider
    This visual control is rather interesting. It simulates a physical slider control ...

    • The enableSelection property is used to display the startSelection and endSelection properties within a colored range and with tics. If this property is set to false (the default) then the startSelection and endSelection properties have no effect.

    • The endSelection property is the high end of a startSelection/endSelection range, and must be within the rangeMin and rangeMax properties. See enableSelection above.

    • The rangeMax and rangeMin properties are used to set the high end and low end of the slider.

    • The startSelection property is the low end of a startSelection/endSelection range, and must be within the rangeMin and rangeMax properties. See enableSelection above.

    • The value property is the current value of where the slider part of the control is between the ranges.

    • The tics property is used to determine how to display the tic marks. (Automatic, Manual or None)

    • The ticsPos property is used to show where to display the ticmarks.

    • The vertical property determines if the slider is horizontal (default) or vertical.

    • The onChange event is how you would programmatically do "something" based on changes in the control.

    • The clearTics method clears out any tics set with the setTic method (below). (This is only relevant if the 'tics' property is set to 1 - Manual.)

    • The setTic method is used to manually set tic marks in a slider. To use it, set the sliderís tics property to Manual. Call clearTics() to clear any previously set tic marks. Then call setTic() with the location of each tic mark. ( setTic( n ), setTic( n+2 ), etc. ) (This is only relevant if the 'tics' property is set to 1 - Manual.)

    • The setTicFrequency method sets the tic mark frequency for automatically displayed tic marks.

    SLIDER SAMPLE:

    • SLIDER.WFM has one slider that some pushbuttons and radio buttons are set to affect the properties of, and a second slider that has it's onChange set to modify the value of a progress bar (kinda cute, but not real useful).

  • Scrollbar
    Scrollbars can be placed on a form to programmatically do anything you can think of doing with them (which might include scrolling through a table ...).

    • The dataLink property is used to link to a field in a table.

    • The rangeMax, rangeMin and value properties work like they do for other controls that are similar in nature.

    • The vertical property is used to determine if the scrollbar is vertical or horizontal.

    • The onChange event is fired for each change in the position on the scrollbar of the "thumb".

  • ReportViewer
    The reportViewer control is used to let you preview a report on a form. The original idea was to create a preview form with that in mind, but it's possible to have a preview on any form.

    • The fileName property is the name of the .REP file you wish displayed in the viewer.

    • The params property is an associative array used to pass parameters to the report. Note that this is not automatic - your report has to check for this.

    • The ref property is read-only -- it is a property used to reference the actual report from within your form. You could add pushbuttons that incremented and/or decremented the report's page properties to show different pages, and to do so you would need to refer to:

                   form.reportViewer1.ref.startPage := 5
                   form.reportViewer1.ref.endPage   := 5
                   form.reportViewer1.ref.render()
              

    • The reExecute method is used when the parameters are changed to completely re-execute the report. If you just changed the page numbers, then use the render() method as shown above.

    NOTE: The dUFLP library (noted at the end of this document) has a report preview form that can be used to see the reportViewer control in action (Preview.wfm -- make sure to understand it fully that you open it in the source editor and read the comment header at the beginning of the document).

  • Treeview
    The TreeView object will be familiar to you in that it is used in the dBASE source editor, as well as the Windows Explorer. It presents data in an outline format that may be expanded, or contracted by the user. What may be confusing to a developer new to the TreeView object is that the TreeView is really a container for TreeItem objects. You add items to the TreeView object by creating a new TreeItem object.

    You might want to use the treeview to create your own dialog boxes similar to what you get when you call the getDirectory() dialog.

    In the designer, to add treeItems, right click on the treeView control. The first selection in the popup that appears is "New Item". If you select that, a new treeItem is placed onto the tree. You can add treeItems "under" a specific item by making sure it is highlighted. This takes a bit of practice, but once you have done it a few times it starts to make sense.

    You can programmatically add treeItems to a treeView control as well. See the discussion below after the listing of properties, events and methods.

    NOTE: It is not a good idea to mix how you add treeItems to the treeView control -- either add them programmatically or add them in the designer. If you add top level treeItems in the designer, and then add lower- level items programmatically, you may find that the plus sign (used to denote children treeItems) will not appear for the top level items -- which means you double click on them to show the next level of treeItem. (Discovered by Gary White)

    The following properties, events and methods are important to the treeView control:

    • The firstChild property is used to return the name of the first child of the treeView (or in the case of the treeItem control, the first child of that specific item). This is a readonly property, and is useful if you need to loop through your treeItems ...

    • The firstVisibleChild property is much like the above, but if you have treeItems that have their visible property set to false, this will return the first visible child ...

    • The selected property will give you the object reference of the currently selected treeItem -- if there is none selected it contains "null".

    • The allowEditLabels property allows you to disallow editing of labels by the user.

    • The allowEditTree property allows you disallow any editing at all of the tree (including adding items, deleting items ...) by the user.

    • The checkBoxes property determines if each treeItem has a checkbox.

    • The checkedImage property is used when not using checkBoxes to display a specific image when an item is selected.

    • The disablePopup property is used to disable the popup menu (right mouse click on the treeView) associated with the treeView automatically.

    • The hasButtons property determines whether [+] and [-] buttons are used for treeItems that have children.

    • The hasLines property determines if there are lines linking the treeItems.

    • The image property is the default image displayed between the checkbox (if any) and text when the treeItem has not been selected. (This is not required)

    • The imageScaleToFont property determines whether or not to scale the images to match the font.

    • The imageSize property is the height of all images used in the treeView in pixels. Only use this if imageScaleToFont is set to false.

    • The indent property is the horizontal indent (in pixels) for each level of the treeView.

    • The linesAtRoot property determines if there are lines connecting the first level of the treeView's items.

    • The selectedImage is the image used if an item is selected.

    • The showSelAlways property is used to determine if you should show the selected item as selected even if the treeView does not have focus.

    • The toolTips property determines if you should display the text of the items as toolTips (speedTips) if the text is too long to appear in the area provided.

    • The trackSelect property determines whether to highlight and underline items as the mouse passes over them.

    • The uncheckedImage property determines the image to use if the treeItem is not checked instead of using a checkBox.

    • The canChange event fires as a user attempts to move off a label if they attempted to change it. It must return a true or false value. (This can be used to check to see if the value matches set conditions ...)

    • The canEditLabel event fires before the user edits a label, and can be used to disallow it (returns a true or false value).

    • The canExpand event can be used to prevent a user from expanding or collapsing a treeItem based on the developer's own requirements.

    • The onChange event fires after the selection moves to another treeItem.

    • The onCheckBoxClick event fires when the checkbox for a treeItem is clicked.

    • The onEditLabel event fires after the label is edited, may optionally return a new label (see online help).

    • The onExpand event fires after a treeItem is expanded or collapsed.

    • The count method returns the total number of treeItems in the tree.

    • The releaseAllChildren method deletes all treeItems in the tree.

    • The sortChildren method sorts the child items -- this does not sort children below the top level (see treeItem ...).

      Note -- if you edit a treeItem's text and call sortChildren(), the onEditLabel method is grabbing the value of the text before the sort occurs, and the item may not be sorted properly (it's sorting the old value). See the form TREEVIEW.WFM for examples of how to get them to sort correctly.

    • The visibleCount method is like the count method above, but only counts the visible treeItems in the tree. There appears to be a bug in this method - it returns what seems to be the number of possible visible treeItems in a treeControl, not the number of actual treeItems.

    • The getItemByPos method should allow you to reference a treeItem based on its position in the tree. This method was designed to allow you to determine the object reference of a treeItem based on its position, so that if you were to, say, click on a treeItem the treeView should be able to tell you what item was clicked on, or a 'null' value if no item was clicked on. This is new to dBASE Plus.

    The TreeItem control is a child control of the treeView control. This is what is referred to in all of the above, when discussing "children" and so on. The treeItem is one individual entry in the treeView. Note: You cannot create a treeItem without a treeView object as its parent.

    • The firstChild property is a reference to the first child treeItem of the current treeItem.

    • The nextSibling property can be used when looping through the treeItems in the treeView control. This refers to the next item at the same level as the current one.

    • The noOfChildren property returns the number of children of the current treeItem.

    • The prevSibling property is related to the nextSibling property, but going the other way ...

    • The bold property determines if the text (label) of this treeItem is bold.

    • The checked property has two uses -- you can programmatically set the checked property so that this item appears as checked, and you can query this property to see if the item has been checked.

    • The expanded property works much as the checked property (you can set it yourself, or query it) -- it returns a logical value based on whether or not the children of this treeItem are showing.

    • The image property is the image specifically for this item that shows between the checkbox and text (i.e., you can individually set an image if you desire for each treeItem, or use the properties set for the treeView ...).

    • The level property returns the treeItem's level in the tree.

    • The selectedImage property works like the one explained for the treeView, but as the image property above, can be set individually for each treeItem.

    • The text is much like the value property of many controls, this is the text that displays for the treeItem.

    • The ensureVisible method expands the treeItem above this, and scrolls the tree if necessary to show this treeItem.

    • The select method makes this treeItem the selected one in the tree.

    • The setAsFirstVisible method works as the ensureVisible method but makes sure that this is at the top of the tree.

    • The sortChildren method does what it sounds like, it sorts the child treeItems.

    Here is a short discussion of using the treeView and treeItem controls by coding them. This code and discussion is by Gary White (credit where credit is due department):

    To create a treeView control on a form:

           form.treeView1 = new treeView( form )
           with ( treeView1 )
              height = 10
              left = 2
              top = 1
              width = 20
           endwith
        
    You would then add top level items to it with the code:

            form.treeView1.treeItem1 = new treeItem( form.treeView1 )
            with ( form.treeView1.treeItem1 )
                 text = "First top level item"
            endwith
        
    Worth noting is the code in the first line:

            new treeItem( form.treeView1 )
        
    The code inside the parentheses is the parent of this treeItem. So, if you wanted to place a "child" item below this treeItem, you would use something like this:

            form.treeView1.treeItem1.treeItem1 = ;
                         new treeItem(form.treeView1.treeItem1)
            with (this.treeView1.treeItem1.treeItem1)
                 text = "TreeItemChild1"
            endwith
        
    There is a small bug in the VdB7.01 treeView implementation. Setting the allowEditTree property to false should prevent the user from adding or deleting items from a treeView object. Unfortunately, if the user presses the delete key, this property is not respected and the currently selected item will be deleted. One possible work-around is to use the onGotFocus and onLostFocus events to disable, or enable the delete key:

            onGotFocus = {;on key label del ?? chr(7)}
            onLostFocus = {;on key label del}
        
    The properties, events and methods of the treeView and treeItem classes are explained in more detail than in this document in dBASE's online help file. Most are similar to those of other controls. Events are all handled in the treeView class, while properties and methods are divided between the treeView and treeItem. You'll often need to look back and forth between the two objects to accomplish what you may want.

    If you wish to use the treeView object for editing table data, it is imperative that you use a unique key value to name the treeItem, so that you can find the proper record to edit. The treeView does not automatically navigate within a table. You can use the treeView's onChange event to manually navigate to the proper record. So, if you have an autoIncrement field as a unique key value in a table (say as an AcctNo field), but you wanted to display the Name field in the treeView, you would add sub-items to the treeItem like this:

           function form_onOpen
              local t, oItem
              t = this.treeView1.treeItem1
              this.rowset.first()
              do while not this.rowset.endofset
                 oItem = new treeItem( t, str(this.rowset.fields["AcctNo"].value ) )
                 oItem.text = this.rowset.fields["Name"].value
                 this.rowset.next()
              enddo
              t.sortChildren()
           return
        
    Then, the treeView's onChange event might look like this:

           function treeView1_onChange
              form.rowset.findkey( val( this.selected.name ) )
           return
        
    Of course, editing a treeItem's text does nothing to change table data. You could use the onEditLabel to post the changes:

           function treeView1_onEditLabel(text)
              if form.rowset.fields["AcctNo"].value == val( this.selected.name )
                 form.rowset.fields["Name"].value = text
                 form.rowset.refreshControls()
                 this.treeItem1.sortChildren()
                 form.refresh()
              endif
           return
        
    You can see a more complete implementation of the TreeView in the GetADir.cc in the dUFLP (available in the Knowledgebase).

    TREEVIEW SAMPLE FORMS:

    • TREEVIEW.WFM creates two tables and populates them with the old DML GENERATE command (which means that the data will appear as random characters). It populates the tree with treeItems based on fields in the table, and sorts them. If you navigate through the treeItem the entryfields on the right will be updated as well. See notes at the beginning of the form -- the treeView is not really intended to be used for table navigation/editing, this is an example only.
    • TREEVW2.WFM is simply an example thrown together to show how a variety of the properties and methods work by clicking pushbuttons ... these will change properties or call methods to modify the treeView control shown.

Summary

Well, there you have it. This may seem like a brief summary for some of these controls -- there is a lot that can be done with them, and this document really only scratches the surface. Check the sample forms that came with this HOW TO document -- these are designed to show various features and abilities of the controls discussed here. They cannot possibly cover every possible contingent for how you might wish to use some of the controls, but between this document, the samples provided with it, and the dBASE newsgroups -- the chances are good that nearly anything you might want to do can be done, and someone can help you do it.

The only way to truly understand some of these controls is to try to work with them, look at examples that others have written, and ask for assistance on the dBASE newsgroups.

A lot of custom controls are available in the dBASE Users' Function Library Project (dUFLP) in the Knowledgebase.

Note that this library is updated on an irregular basis, so you may want to check back periodically ...

Special Thanks: to Gary White for the patience he has in helping me with these HOW TO documents. His editing and coding advice has been invaluable, and should make this that much easier for you to read and understand.

Also thanks to Todd Kreuter, Bob Rimmington and Phillip Hansen for going over this with a fine-tooth comb and also giving some suggestions ...


DISCLAIMER: the author is an employee of dBASE, Inc., but has written this on his own time. If you have questions regarding this HOW TO document, or about dBASE you can communicate directly with the author and dBVIPS in the appropriate newsgroups on the internet.

HOW TO files are created as a free service by members of dBVIPS and dBASE, Inc. employees to help users learn to use dBASE more effectively. They are edited by both dBVIPS members and dBASE, Inc. Technical Support (to ensure quality). This HOW TO file MAY NOT BE POSTED ELSEWHERE without the explicit permission of the author, who retains all rights to the document.

Copyright ©2004, Kenneth J. Mayer. All rights reserved.

Information about dBASE, Inc. can be found at: http://www.dbase.com


EoHT: CONTROLS.HTM -- January, 2004 -- KJM