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:
- Grid -- The grid control
is covered in a separate document. That document and sample forms can be gotten
in the Knowledgebase.
- Subform -- The subform
control, a type of form, is covered in a separate document. That document and
samples can be gotten in the Knowledgebase.
- Browse -- The browse control
has been superseded by the grid control and should not be used except in
legacy applications that have not been updated to the latest versions (32-bit)
of dBASE.
- Ole, Paintbox, ActiveX
-- These controls go beyond the scope of this document and would best be covered
individually.
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.
-
The dataLink property is one of the the most
important, as it is used for any datalinked control that you are linking to a
field in a table. In dBASE this means that you have placed onto the surface of
the form a query (dragging a table from the navigator, for example), a
datamodule, or a datamodref. The query or datamodule contains the references to
the table(s), and you can datalink a control from the inspector. If you are
using the old XDML form of USE MYTABLE you may also datalink controls to the
underlying table.
The dataLink is "live" -- this
means that as soon as you run the form, you are working with the table's field(s).
There are a lot of tricks
working with tables, and you should read the HOW TO document on OODML, to learn
the details. The basics are that you really are working with a record "buffer" --
this means you see on the screen an "image" of the record (or row), and if you
make changes, it is to the image. If you navigate or save the changes, then they
are written back to the table. However, you can also abandon those changes before
they are written to the table. All of this is done automatically for you by
the rowset object of dBASE.
One trick I will suggest here, but you can take it or leave it -- set the
rowset object's autoEdit property to false. This will disable changes to a
rowset unless the user clicks an edit or append button (which you would need
to provide on your form). It also will not affect the color of the
entryfields -- see discussion on this in the section on using entryfields.
-
The dataSource property is only used for some of
the components (combobox, listbox, notebook, tabbox or image). It can be an
array (literal or other) for all but the image control. For the combobox or
listbox controls, it can be a field, the structure of a table, a list of files,
and more -- these will be discussed at least briefly for those components. For
an image, this is the source of the image (a field in a table, a file ...).
-
The name property is very handy when working with
any of these controls. The problem is when you place any control directly on the
form from the component palette, it is given the useful name of "classnameN" where
"classname" is the name of the class (i.e., Entryfield), and "N" is a number
(i.e., 1). So the first entryfield you place onto a form from the component palette
would be named: Entryfield1. This is not the most useful name you could have.
If you drag a field from the Field palette, you will get a more useful name:
"ClassnameFieldnameN", where "Fieldname" is the actual name of the field.
If you have spaces in it, these will be removed or replaced with underscores,
"Classname" and "N" are the same as above, so if you dragged a "First Name" field
to the form from the field palette, you might see a name of EntryfieldFirst_Name1.
It is a good idea to use descriptive names for your controls, because otherwise,
particularly on a complex form, it will get very difficult to know which object
is which. You could use something like what is used by the field palette, but
unless you have multiple first name entryfields on a form, you can probably
remove the number.
-
The baseClassName property is useful in that
sometimes in your code you may want to loop through the controls on a form to
modify how the controls are functioning. baseClassName will always return the
type of control, even if you are using custom controls that are subclassed from
a stock control. For example, if you have a custom entryfield that you name
MyCustEntry, the className property of an instance of that entryfield will be
"MYCUSTENTRY", but the baseClassName property will always be "ENTRYFIELD".
This is new to dB2K.
-
The anchor property (not shared by all controls,
but ...) determines where to "anchor" a control -- this allows you to set it
to a specific part of the form, and if you resize the form (or you allow your
users to) it will always be in the same location ...
-
The function and picture
properties are used to format the value of a control. A function formats the
entire value, while a picture gives you character-by-character control. Suppose
you need a 20 character field value formatted in all uppercase characters.
You could enter 20 "!"'s in the picture property, or simply set the function
property to "!" and leave the picture property blank. Using these two properties
together, you can include literal characters in the displayed value that are not
part of the actual field value. For example, you can store a phone number in
a 10 character field as "8008675309", set the function property to "R" and
the picture property to "(999) 999-9999", and the displayed value will be
"(800) 867-5309".
-
The speedTip property is used to display a cue to
the user of what an object is there for. Not all controls have them, but most
form controls do. Most form controls have them as of dBASE Plus vers. 2.0.
-
The transparent property is for those controls
with text -- if this is set to 'true', the background of the form will show
and you only have to define the foreground color of the text. The background
will default to that of the form and, if the form has a background image,
that will show through as well. In dBASE Plus, version 2.10 and later,
some controls that did not have a transparent property have been modified
to have one. Specifics will be discussed about these when we get to the
individual controls, as the behavior may not be what you expect to see.
-
The value property returns the current value
contained in the control. If you are datalinked to a field in a table, this
will be the value in that field (unless the field value is "morphed").
"Morphing" a field's value is basically storing one value in the field
and displaying a different value in the control that is being used on the form.
Morphing can be used in lookup tables, where you store a key or code value
in the main table, and perform a lookup in another table -- what the user
sees is a completely different value from what is stored in the table.
dBL has a method of handling this automatically -- this is discussed with
the Combobox control, and can be examined in more detail in the online help
under "lookupSQL".
-
The visible property enables you to make a
control visible, by setting it to true (the default value), or invisible,
by setting it to false.
-
The printable property is also what it sounds
like. If you use the form's print() method you can print the form layout --
if you set a control's printable property to false, it will not print when you
do this.
-
The border and
borderStyle properties are used to give a specific look to the
outline of your controls. Most have a default borderStyle set. You should
experiment with these styles to find the one(s) you prefer for your controls ...
(see the form RECTANGL.WFM that comes as a sample with this document for how
these look ...)
-
The dragEffect property is used to specify if
a control is draggable, and how it is dragged. The default value is zero (None),
but can be 1 (Copy) or 2 (Move). Copy means that if the user drags the control,
when the drop it, a copy of the control will be made. Move means that the
original control is moved. For more details, see online help, and
the Knowledgebase - there should be an article there by Tim Converse on
working with Drag an Drop. This is new to dB2K.
Most controls in dBASE have this property.
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.
-
The key event is called for each keystroke entered
by the user (this event is only available for some controls). It has it's uses,
but there are some interesting things to note: the keystroke that you are
"currently" looking at may not be what you expect. For an example of the use
of the key event check out SEEKER.CC in the CUSTOM directory of Visual dBASE 7
(in other versions of dBASE this will be in different locations -- in dBASE
PLUS, Seeker is contained in the dBLClasses folder, and under that, in
FormControls -- i.e., Plus\dBLClasses\FormControls). You may also want to look at
the PASSWORD.CC in the dUFLP library for an interesting method of using the key
event to display a different character than the one typed.
-
The onChange event fires when the contents of the
control change (user changes something or programmatic changes ...). If this is
used with radioButtons and you share the code (i.e., each radioButton calls the
same event handler), the event handler will be executed twice -- once for the
radioButton that was clicked and once for the radioButton that had it's value
changed by clicking on another. (See online help for more details.)
-
The onGotFocus event fires when a control gains focus --
either by the user tabbing to to the control, clicking on it with the mouse,
or a programmatic call to the setFocus() method. This can be used to change
the color of the control if the control does not have a colorHighlight property
(some don't), and it can be used to execute code you need to have executed only
when this control gets focus.
-
The onLostFocus event fires when the focus moves
to another control on the form. This can be used to do specific validation,
to re-set the color for controls that do not have a colorHighlight property, and
more.
-
The onOpen event fires after the form's onOpen
event. This can be used to set default values, to define dataSources for
those controls (combobox, listbox ...) that use them, and more.
-
The valid event can be used to do field-level
validation. Note that this one must return a logical value (true or false), no
matter what you do in the code ... if the returned value is "false" the user
cannot leave the control until the returned value is "true". (There is more
detail in the onLine help for this.) If you use the valid event, you may
want to also look at these properties: validRequired
and validErrorMsg.
-
The when event can be used to disallow access to
the control (must return a true or false value). One use of this that is fairly
common is that a lot of developers do not like the default Windows behavior
used when setting a control's enabled property to false -- the color of
the text goes gray -- by not using enabled, and instead, using the
when event set to return a false value, the user cannot access the control but
the color does not change. ( {; return false} is all that is necessary in
this event handler to do this.)
-
The onDesignOpen event is used ONLY in the
designer. This fires each time the form is opened in the designer, so be
careful using it. One thing that this can be very handy for is to assign
some "dummy" data or datasource to a control -- but only do this if your
onOpen event re-sets this (or it gets set in the form's open/readmodal
or onOpen events), as the form designer will stream out to the form's
constructor code whatever your datasource is at the time.
-
The onOpen event fires both when the form
is opened in the designer and when the form is run. NOTE: this event fires
AFTER the form itself opens, so be careful what you do here -- you may
cause some interesting results in the appearance of your form ...
-
The onDragBegin event fires when a user holds
the mouse down on a control whose dragEffect property is set to either
1 or 2, and then moves the mouse. For more information, see online help.
This is new to dB2K.
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 ...)
-
The setFocus() method is very handy to force
a specific control to have focus.
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.
- Entryfield
The Entryfield control is the most basic of the data entry controls.
Nearly all forms that deal with data entry use entryfields for a lot of the data.
There is not a lot that needs to be said here that isn't covered in the
"shared properties ..." section of this document.
-
The key event is used when a user presses a
key to evaluate what the keystroke is, and act on it. (This appears in
the Combobox and Editor controls as well.)
SEEKER:
One interesting entryfield that ships with dBASE, and appears in
the component palette on the Custom page (unless you have closed all procedures,
and such) is a Seeker control.
This control does not get datalinked to any fields in a table. Its purpose
is to perform what is called an "incremental search" -- this is a search
that is performed based on what is entered into the entryfield.
To use a seeker control see the document in the Knowledgebase concerning
"Working With the Custom Classes that Ship WITH dBASE".
A really great use of the seeker control is to combine it with a "read-only"
grid (one with the allowEditing and allowAddRows properties turned off, and the
rowSelect property turned on) -- this will show the current row as you
use the seeker ...
See sample form: SEEKER.WFM for an example of this. Note that seeker.wfm
creates a dummy table and randomly populates it.
Non-Datalinked Entryfields
There are times that you may need to use an entryfield that is not datalinked to
a field in a table. BUT, you may want to obtain data from a user (an example
might be to get a starting date for a report, or something of that nature).
The problem is that entryfields default to the type "Character", with a max
length of 25 characters. This means entering a number or a date will not
automatically use the standard error checking built in to dBASE and the value
you get when you check the entryfield's value property may not be what you
were expecting. For example, consider the following code:
local f
f = new valuesForm()
f.open()
return
class valuesForm of FORM
with (this)
scaleFontBold = false
height = 7
left = 10
top = 1
width = 18
endwith
this.entryfield1 = new entryfield(this)
with (this.entryfield1)
height = 1
left = 5
top = 1
width = 8
endwith
this.entryfield2 = new entryfield(this)
with (this.entryfield2)
onChange = {;form.entryfield3.value=form.entryfield1.value+this.value}
height = 1
left = 5
top = 2.5
width = 8
endwith
this.entryfield3 = new entryfield(this)
with (this.entryfield3)
height = 1
left = 5
top = 4.5
width = 8
endwith
endclass
If you copy and paste this code into a dBL program and run it, you will see
a form with three entryfields. Enter a numeric value in the first entryfield,
tab to the second entryfield, enter another numeric value and press the tab
key. The bottom entryfield will contain the results of adding the other two
values. What you expected? Probably not. The results would be the same if
you tried entering dates. The strings you enter are concatenated instead of
adding the numeric values.
The solution is to use the onOpen event of the entryfield (or a spinbox for
that matter), and you can set a value that will force the entryfield to work
as a date or numeric type. For dates, you could set a blank date with {}
(two curly braces), or you could set a value to the current date using the
date() function. For a numeric value, if you do not have a default starting
value, simply using zero will work:
function entryfield1_onOpen
form.entryfield1.value = {}
// or:
// form.entryfield1.value = date()
// or {} for empty date
// or:
// form.entryfield1.value = 0 // numeric
dBASE Plus 2.2 and later
has changed the way that numeric values are handled. The following
is a brief discussion. Note that this apply the same way for
Entryfields, Spinboxes, and Grid cells.
-
Numeric values now start entering data from the left of the decimal
point, much like calculators and a variety of software. If you type
the decimal, the cursor jumps to the right of the decimal point, and
data entry goes to the right.
-
Numeric values are left justified in the entryfield (or spinbox ...).
In older versions of the software numeric values were right justified in
the entryfield/spinbox/etc.
-
Zero values will appear as '.00' when editing. Null values
will show an empty entryfield, although if a decimal point is defined, that
will appear. (i.e., if your entryfield is datalinked to a numeric field
with decimal values ...) What is meant by "when editing" is when
the user is in either append or edit mode, and the entryfield has
focus. When the entryfield does not have focus, zero values should
appear as '0.00' (unless a picture or function is defined, in which case the
picture and/or function will take effect as soon as the cursor
exits the entryfield).
-
It is a good idea to set the picture property (ex. "9999.99", if your
numeric field is defined as width of 7 with two decimal places, for example).
If you use a picture without any decimals (values to right of the
decimal point), i.e., "999", and the value is zero, you will see a zero.
-
When you are adding or entering numeric data that has been formatted
with the picture and/or function properties these will disappear when the
entryfield has focus (while modifying the data), and once the entryfield
loses focus the data will be formatted appropriately.
-
The entryfield's selectAll property will act like always -- if
the property is true (the default state) then tabbing to an entryfield
will highlight everything and if the user types, whatever is
contained in the entryfield will be over-written, and entry will
start at the decimal point (numbers will move left) as described
above.
If the selectAll property is false, tabbing to the
entryfield will not highlight what is in the entryfield. The cursor
will start at the left of the entryfield, and the user may need
to delete numbers in the entryfield.
-
If the user clicks on the entryfield with the mouse, the cursor
will appear where they clicked.
-
Spinbox
The spinbox control is really just an entryfield with a couple of pushbuttons
added to the right. It is used for numeric or date values, to increment or
decrement the values by using the buttons (which show up/down arrowheads).
The spinbox has all the characteristics of the entryfield, and the following as
well:
-
The rangeMax, rangeMin
, rangeRequired properties are
used to set a top/lower value and to enforce that value -- i.e., if the
rangeRequired property is set to "true" the user cannot move past the
rangeMin or rangeMax values or enter a value outside that range.
-
In addition, there is the spinOnly
property -- this can be used to enforce the use of the buttons, and
ONLY the buttons by the user -- they cannot enter a value by typing it
if this is set to true.
-
The step property is used to set the value
the pushbuttons increment/decrement the value of the spinbox. The default
for this is 1, but it can be changed to something else.
-
Checkbox
The checkbox control is normally used with a logical value, and is
designed to show a check if the value is true, and an empty box if
it is false.
-
The checkBox has one property that most of the other controls do not
(radiobuttons and pushbuttons share this property) --
textLeft -- if this is set to true,
the text of the checkbox will appear on the left side of the checkbox.
The default is false, which means that the text appears on the right side
of the checkbox.
-
Radiobutton
The radiobutton control often confuses people -- this is because the value
property returns a logical (true/false) value, but what is stored in the
field of the table is the TEXT property of this control. It is possible to
use the radiobutton for non-text values, but there is another level of coding
that would be necessary to accomplish this (it's called Field Morphing) that
we will not get into in this document.
The purpose of radiobutton controls is to present the user with a limited
set of choices for a character field. A really simple example would be
setting ice cream flavors, and you only had three: Chocolate, Vanilla or
Strawberry. By setting three radioButtons onto a form with the text
reading "Chocolate" for the first, "Vanilla" for the second, and
"Strawberry" for the third, and datalinking all three of these radioButtons
to the same field, the user will only be allowed one of those three choices.
Tip: use the group property for the
FIRST radioButton -- set it to true (if not done for you by the designer).
Make sure no other controls appear in the "Z-Order" between the radiobuttons.
(The "Z-Order" is the sequence the events are defined in the form's constructor
code. You can modify this in the designer through the layout menu, and in the
source code directly by cutting and pasting code appropriately.)
If you follow this tip you should not have problems with your radiobutton
controls behaving properly (for example, a user using the arrow keys will
loop through the radiobuttons ...).
-
The radioButton also has a textLeft
property -- see the checkBox for details.
-
Editor
The editor control is used for large quantities of text.
It has some really interesting
capabilities, but you must be careful ... some things don't
work quite the way you might expect.
-
The dataLink property has the ability to
datalink to a memo field (or even a long character field) of a table,
but it also has the ability to link to a file.
-
The lineNo property shows the current line
number in the editor control. This may be the current line in a file, if
you are datalinked to one, but if the editor is not as wide as your file's
lines, and the wrap property (see below) is set to the default of "true",
then the lineNo may not match ...
-
The columnNo property shows the current
column number in the editor control. This is related to lineNo
above, and can be used in conjunction to determine where the cursor
is on a character by character basis in the editor control.
This is new to dB2K.
-
The modify property allows or disallows the
user's ability to edit the contents of the editor object's value property.
-
The wrap property is used to wrap text --
it defaults to true, but you can set it to false. If you do, you will
automatically get a horizontal scrollBar at the bottom of the editor
(see the scrollBar property below).
-
The marginHorizontal and
marginVertical properties deal with how close to the
edge of the editor control the actual text will appear.
-
The popupEnable property allows a special
popup for memos to appear on a right mouse click. It includes some useful
things, like a find dialog to find text, and more. Interesting note,
the last item in the popup "Show Format Toolbar" can ... er ... be interesting.
It allows the user to insert HTML into the editor (see "evalTags" below) --
the problem is, once the HTML is inserted, there is no way to remove it, and
it becomes part of the text -- even if you set the "evalTags" property to
false, it's pretty much impossible to remove the HTML from a memo field.
If the form is MDI (Multiple Document Interface -- opened with the Open()
method), this toolbar is available, if the form is SDI (Single Document
Interface -- opened with readModal()), the toolbar is NOT available.
-
The evalTags property is used to evaluate
HTML tags in the editor. If it is set to false (the default is true),
then rather than evaluating them, you should see the tags ...
-
The scrollBar property can be used to set
vertical and/or horizontal scroll bars on, set to "auto" (use them if
needed) or turn them off completely.
-
The Editor has finally, in release 2.2 of dBASE Plus
got a
colorHighlight property, which is used
to define the color of the editor when it has focus.
If you are using versions of dBASE earlier than dBASE Plus 2.2
you can simulate this by using the editor control's onGotFocus event and
changing the colorNormal to a different setting, and in the onLostFocus event
setting it back to what it was before.
Editors and the Rowset's autoEdit Property
The editor control may ignore the rowset's autoEdit property,
which can be a bit disconcerting and frustrating. If you add the
following code to the editor control's key event, you can avoid this
particular problem:
function key
/*
This code by Gary White is provided to
get around a problem with rowsets that
have the autoEdit property set to false,
and editors. The editor seems to be immune
to this property once you make a change
in it -- if you then save or abandon, you
can actually edit the contents of the editor
object ...
*/
// this = editor
// dataLink = field
// parent = fieldArray
// parent = rowset
if type( "this.datalink.parent.parent" ) # "U"
r = this.datalink.parent.parent
if r.autoEdit == false and ;
( r.state # 2 and r.state # 3 )
return 0
endif
endif
HTML and Editor Controls
Editor controls, as mentioned elsewhere, can be used to display
text formatted with HTML.
One item of interest here is that the editor control can actually use
the HTML <IMG> tag. However, please note that HTML itself does not
know anything about tables, rowsets, fields, and so on, so you cannot
directly set the image source to look at a field.
However, if what you need to do is embed an image into an editor control's
HTML that is stored in a table, what you can do is use the binary field
object's copyToFile() method, and then in the editor control set the
<IMG> tag's SRC to point to that file. You might have to do this sort
of thing by using the rowset's onNavigate or canNavigate
event(s) to copy the file out to disk and then delete it. This should give
you a pointer or two, though.
Note that you can drag & drop an image into a memo editor and the
appropriate HTML tag will be stored in the memo.
Please note that many other HTML tags can be used as well in your HTML in
the editor, but not all. These tags include lists (<UL> <OL>),
header, font (useful for colors), italic, bold, underline, etc ...
See the Text control and
information about HTML there for more details on what is available ...
-
Listbox
Listboxes are quite useful, in that they can be used to list the contents of
an array, or the contents of a field in a table, and even more fun, they can
be used to list the contents of a field in the table you are editing, and
be used to move the row pointer (this may not be desirable, but it is
an interesting effect ...).
Sample Form:
-
LISTBOX.WFM -- this simple form demos using a table's fields
in OODML for the dataSource of a Listbox.
Combobox
The Combobox may seem like it's related to the listbox, but it's really related
more to the entryfield. It is an entryfield object with a pushbutton used to
"drop down" a list of selections (from the dataSource).
The combobox control can be quite useful for a lot of different situations,
and can be automatically used if you set up a field in a table with the
lookupSQL property.
The field object's lookupSQL property can be handy -- it gives you the
ability to do an automatic lookup on another table (and even the same
table, but that can get very strange and is not recommended).
Briefly, you can set this as a custom property of the field you
wish to lookup in another table, or you can set it in the DataModule
or Query object in the query's onOpen (or canOpen) event.
For a lookupSQL to work properly, you must have a key value that is
stored in the main table (the one you are working in) that matches a
key value in the lookup table. The lookup table would ideally have at
least one other field.
What you are doing is showing the user the value of the second field
(or a calculated field -- we won't get into that here) in a combobox,
but STORING IN THE FIELD the key value that matches what appears in
the combobox.
An example of this would be something like the following: A customer
table stores state ids (for US states),
but when you look at a form that displays the customers, the combobox
shows the full name of the state. The actual value stored in the
customer table would be the state id, not the full name of the state,
but what the user would see is the full name of the state.
NOTE: Do NOT set a DataSource for a combobox that is created
by the software for use with a lookupSQL field -- the datasource is
created automatically for you, and anything you do will be overridden.
More details on this can be found in online help.
Sample Form:
-
COMBOBOX.WFM -- this simple form demos the ability to have one combobox
affect the datasource of the second. No tables are used for it, but the
heading and other comments in the form's source code discuss using tables
instead of arrays ...
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.
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.
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