Beginning Forms
Last Modified: January 22,
2004
Ken Mayer, Senior SQA Engineer
dBASE, Inc.
Note: This document
was originally created for Visual dBASE 7.01, and has subsequently been
updated for dB2K, with minor revisions for dBASE Plus. The document may refer
to specific versions, but for the most part, all versions of the product since
7.01 are covered here. In addition,
you may want to do some reading in the Visual dBASE 5.x How To documents, specifically
for the one named FORMVARS.HOW, as this discusses some concepts about communicating
between forms that are mostly still usable in the current products.
Also please note that the
terms OODML and XDML are used in places in this HOW TO document -- OODML refers
to the dBASE Object Oriented Data Manipulation Language,
where XDML is the XBase Data Manipulation Language used in dBASE/DOS and Visual
dBASE 5.x. You can still use XDML in the 32-bit products, but most of the examples
used will be for OODML.
What Is a Form?
A Form is a Window in the Windows
environment. Any place where a user can interact with or get information about
an application is done through a Window.
In dBASE, we call these
forms. A form may be used to interact with data, to query a user for specific
options for a report, to obtain information about what to name a file, or to
simply display the status of a long operation. If you need a way to interact
with the user, you will most likely use a form to do so.
As a developer, you will
spend a lot of time in the form designer, and this HOW TO document is aimed
at helping you out ... If you are new to dBASE, you are in for a treat -- this
development environment is pretty amazing. If you are coming to dBASE from earlier
versions of dBASE (dBASE/DOS or Visual dBASE 5.x) you may have some things to learn
or even un-learn.
Please note that this HOW
TO document is not going to spend a lot of time discussing all of the possible
controls you can place on a form -- that is already covered in great depth in
"Form Controls" and the HOW TO on using the Grid (in this Knowledgebase). The
document will not spend much time on multiple-page forms (see how to on Multiple
Page Forms) or Custom Forms (and another HOW TO covers this topic). This document
is also not going to teach you how to build an application in dBASE, although
of a neccesity some concepts involved in this will be covered. (There is a Tutorial
in the Knowledgebase that may help, and there are other sources available to
learn how to do this as well.)
The main thrust of this
document is to get you, the developer, familiar with the Forms Designer and
a lot of the concepts involved in designing forms.
Design Concepts
There are a few concepts that
need to be covered in order to understand what we will be discussing in the rest
of this document.
Multiple Document Interface?
Single Document Interface? Other?
One of the more interesting concepts of application development is that of "MDI"
or "SDI" applications. What does all that mean?
If you have ever worked
with a Word Processor (Word, WordPerfect, or whatever), you may have worked
with more than one document at the same time. This is a "Multiple Document"
application. It means you have several documents open and you can switch between
them. There are some behavioral features of MDI we will discuss shortly as well.
There are other applications
that are called "Single Document Interface". These mean, normally (although
not always) that you have a single form open, and you work with that. When you
are done with that form, you can open another.
There are also some applications
that are sort of hybrids -- a combination of MDI and SDI. We won't spend much
time on those, but you may see why you might want to develop something like
that as we go.
First let's compare MDI
and SDI applications:
|
MDI |
SDI |
Contained in application
frame
( _app.frameWin ) |
Yes |
No |
Menubar Window on each
form? |
No |
Yes |
Menubar Window on application
frame? |
Yes |
No |
Toolbar Window on each
form? |
No |
Yes |
Toolbar Window on application
frame? |
Yes |
No |
Window Menu applicable? |
Yes |
No |
Forms appear in taskbar? |
No |
Yes |
Minimize/Maximize forms
affects all
open forms? |
Yes |
No |
Each form treated separately
when
minimizing/maximizing? |
No |
Yes |
Windows always offer
resize,
minimize and maximize? |
Yes |
No |
Information provided by
Gary White
This is a quick overview,
but it should give you some idea of the general differences. When we get to
the properties of forms, we will see why this can be important.
The Designer Surface
The Designer Surface is discussed
in some detail in another HOW TO document in the Knowledgebase. Rather than cover
all of that information again, please read that document. It also discusses the
Component Palette, the Field Palette, and the Inspector. This document assumes
you have some familiarity with these tools.
Form Properties, Events and Methods
A form is an object in dBASE,
and as such it has properties, events and methods. Let's take a look at them ...
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 activeControl
property is an object reference to the control that currently has focus on
the form.
- The allowDrop
property is a logical value -- if it is set to true, drag and drop operations
will allow the user to drop a control onto the form surface. This
is new for dB2K.
- The autoCenter
property is a logical value -- if it is set to true, when the form opens it
will automatically be centered on the screen.
- The autoSize
property defaults to false -- if it is set to true it will ignore the height
and width properties and size itself so that all controls on the form appear.
While this may seem like a useful ability it can produce some odd results.
- The background
property is a reference to a background image that can be placed on the form.
This is a feature new to Visual dBASE 7. It is very much like setting a background
image in an HTML document -- it will be tiled across the form, no matter how
big the form is. If you use this feature you may wish to consider ensuring
your text controls (and any other controls that have this property) have their
transparent property set to true.
- The baseClassName
property is the name of the class -- this is always "FORM" when referring
to a form object. See also the className property below. It can be useful
when using code to determine what an object is. This is
new to dB2K.
- The className
property is read-only -- it returns the name of the form's class. You might
expect it to return "FORM", but depending on where you are when you call it,
it may return something like "MYFORMFORM" -- it will be whatever is in the
CLASS statement that begins the definition of the form in the form's source
code.
- The clientEdge
property when set to true creates a "bevelled" edge around the form. This
can be useful to differentiate a form that is a dialog form from a data form,
but there is no requirement that you do this ... (and some developers just
prefer to use this edge on all their forms, some prefer to not use it at all
-- it really is a preference issue).
- The colorNormal
property determines the color of the background of the form.
- The designView
property is a holdover from dBASE 5.x -- if you are using the OODML, (data
objects, queries, rowsets, all that) do NOT use this feature!! You'll only
confuse dBASE and yourself. If you migrated your Visual dBASE 5.x forms to Visual
dBASE 7, dB2K or dBASE PLus
this may be something you used ...
- The elements
property is an array that can be used to work through the elements on a form.
Note however that if you use a container control (or a Notebook, which is
a specialized container) that the controls that are on the container are NOT
in the elements array (and that the container and notebook controls do not
have an elements array of their own).
- The enabled
property determines if a form can be accessed by the user.
- The escExit
property is used to determine if pressing the <Esc> key will close the
form.
- The first
property is an object reference to the first component on a form in the Z-Order.
This can be useful for code that loops through controls on a form (you need
a starting point, and combining this with the property most controls have
of before, which points to the next object in the z-order, you can
loop through until you return to the first ...)
- The height
property is the height of the form.
- The helpFile
property is a reference to the help file (if using Windows style help).
- The helpId
property is the id used in the help file -- if the user presses <F1>
this ID is used to find the correct position in the Windows help file designated
above.
- The hWnd
property is the Windows handle to the form. This is mostly used for Windows
API calls.
- The hWndClient
property is a Windows handle to the client area of the form (not the titlebar
and such).
- The icon
property can be used to set an icon for a form. In the title bar of most windows
is an icon -- this is how you set this. The icon can be either a resource
or an external file.
- The inDesign
property is a logical value used to determine if the form is in the designer
or if it is being executed. If it is in the designer it returns a 'true'.
This might be useful in a form's onOpen event -- if you are in the designer
you might not want some code executed, or you might want different code executed
... You can also check this property in a control's onOpen event.
- The left
property determines where the form is in relation to either the applicaton
frame (in the case of an MDI application) or the left side of the screen.
- The maximize
property determines if the maximize button is visible on the titlebar of the
form. Note that if a form is an MDI form, setting this property to false has
no effect.
- The mdi
property is used to determine if a form is MDI or SDI.
- The menuFile
property is a pointer to the menu. In the case of an SDI form this means that
the menu will be placed at the top of the form under the titlebar.
- The metric
property is the units of measurement used on the form. The default is to characters.
NOTE: when using custom controls on a form, this property gets very
important. If a custom control is really a container that contains other controls,
if the metric of the contained controls does not match the form, the results
will be ... strange ... (this is a known bug).
- The minimize
property is similar to the maximize property above.
- The mousePointer
property determines what the mouse pointer looks like when it is over the
form.
- The moveable
property determines if the user can move the form when it is open (note that
this has no effect if the form is MDI).
- The nextObj
property is a reference to the object that is about to receive focus (i.e.,
when the user presses the <Tab> key).
- The pageNo
property is a numeric reference to the page number -- it is possible to create
multi-page forms (see MULTPAGE.ZIP at my website).
- The persistent
property is a logical value that determines if any SET PROCEDURE statements
used by the form designer (when streaming the source code out to the .WFM)
will include the persistent keyword. For more information on this option,
please see online help. This is new to dB2K.
- The popupMenu
property is a file reference to a popupMenu file -- this will appear if the
user right clicks on the form.
- The refreshAlways
property is a logical value that is used to determine if the form should always
be updated after each navigation, update, etc.
- The rowset
property is a reference to a rowset (OODML). Many custom controls look for
"form.rowset" to determine what rowset to manipulate. In a form with more
than one rowset this is usually the first rowset placed on the form. (It is
possible to programmatically change this, which can be useful in multi-table
forms.)
- The scaleFontbold
property determines whether the the font used for the character metric is
boldfaced or not. (see online help for more details)
- The scaleFontName
property is the base font used for the character metric. (see online help
for more details)
- The scaleFontSize
property is the size of the base font used for the character metric. (see
online help for more details)
- The scrollBar
property is used to determine if a scrollbar is to be used if the form is
not large enough to display all controls that are contained by it.
- The showSpeedTip
property is a logical value to determine if, when a mouse is placed over controls
on a form, the user will see the speedTips (which are hints ...).
- The sizeable
property determines if a form is sizeable or not (this has no effect if the
form is an MDI form).
- The smallTitle
property determines if a form has the small title that you see on palettes
and such. No icon appears on this titlebar, and a side effect is that the
form does not have an entry in the taskbar if it is a modal form (normally
a modal form has its own entry in the taskbar). Note that if the form is opened
with the mdi property true, then this property has no effect ...
- The statusMessage
property is text that displays in the statusbar at the bottom of the screen.
- The sysMenu
property determines whether the form’s system menu icon and close icon are
displayed when the form is not MDI.
- The text
property is the text that displays in the titlebar.
- The top
property is the location of the top of the form in relation to the application
frame if an MDI application or the top of the screen otherwise.
- The topMost
property determines if the form stays on top (this has no effect on MDI forms).
- The useTablePopup
property determines whether to use the default table popup if no popupMenu
is defined for the form. The default table popup contains navigation and other
commands that affect the rowset assigned to the form's rowset property.
- The view
property is an old XBase DML command used to define the .QBE used for a form.
If you are using OODML, ignore this property.
- The visible
property determines if a form is visible on screen or not.
- The width
property is the width of the form based on the form's metric property.
- The windowState
property can be used to set the form's state (Minimized, Maximized, Normal)
or you can query the current state.
Wow. That's a lot of properties.
With luck some things may be starting to gel in your mind.
Events
Events are something that forms respond to, a mouse click, a
form getting focus, a change in the forms's position, 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,
such as the mouse moving or a key on the keyboard being pressed ...)
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 canClose
event can be used to determine if the user is allowed to close the form. It
must return a true or false value.
- The canNavigate
event can be used to determine if the user can navigate in a rowset. (This
does NOT work with the OODML rowsets)
- The onAppend
event fires after the new record buffer has been created. This can be used
to store default values (this does NOT work with the OODML rowsets).
- The onChange
event fires after leaving a record that has changed, but before onNavigate
fires (this does NOT work with the OODML rowsets).
- The onClose
event fires after the form has closed.
- The onDesignOpen
event fires after the form opens in the designer.
- The onDragEnter
event is used to tell dBASE what to do when an object is dragged onto a form
-- when it "enters" the form itself. This is new to dB2K.
- The onDragLeave
event is used to tell dBASE what to do when a control being 'dragged' leaves
the area covered by the form. This is new to dB2K.
- The onDragOver
event tells dBASE what to do when a control being 'dragged' is over a form.
This is new to dB2K.
- The onDrop
event tells dBASE what to do if a control is dropped onto a form. This
is new to dB2K.
- The onGotFocus
event fires after the form has recieved focus (clicking on it, selecting it
in the Window menu, if an MDI application ...)
- The onHelp
event fires when the <F1> key is pressed.
- The onLeftDblClick,
onLeftMouseDown and onLeftMouseUp
events are all fired with the mouse's left button. If you set code for the
onLeftMouseDown and/or onLeftMouseUp note that these will fire
as well as onLeftDblClick -- so if need these to do different things
you're out of luck.
- The onLostFocus
event fires when focus goes to another form.
- The onMiddleDblClick,
onMiddleMouseUp and onMiddleMouseUp
events fire for a mouse that has a middle button like the left events above.
- The onMouseMove
event fires when the mouse moves over the form. If you hook into this event,
you will get coordinates available to you ...
- The onMove
event fires when the form moves.
- The onNavigate
event fires when the user navigates in a rowset. This works with the OODML
as well as the XDML.
- The onOpen
event fires when the form opens.
- The onRightDblClick,
onRightMouseDown and onRightMouseUp
events fire like the onLeftMouse events above.
- The onSelection
event fires after the form has been "submitted" (closed). This event was written
with the assumption that a user would press <Enter> to close a form
(which can be done by setting a pushbutton's default property to "true")
to "submit" the form.
- The onSize
event fires after the form has had its size changed, which includes the windowState
property being changed (although there is at least one bug there -- minimizing
a form does not cause this event to fire).
Suggestion: You may want
to experiment a bit with these events to see what fires when. A simple way to
do this is to add a short codeblock to each (and those events that start with
"can", add ";return true" to the end of the codeblock before the closing brace):
{; ? "eventname fired" }
Do this for each
event you wish to test on a sample form. Run the form, and then try moving the
mouse, or double-clicking the mouse (note when you do that onxxxMouseUp and
onMousexxxDown events fire also!), and so on. It's an interesting exercise ...
Methods
Methods are code that performs an action. Forms have a number
of built in methods which are called through the form, i.e., form.close(). Code
which responds to a form's event is also a method.
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 abandonRecord
method is used in the XBase DML record buffer of the form. This method is
not used with the OODML, but the rowset object has it's own abandon
method.
- The beginAppend
method -- see above ...
- The close
method is how the form gets closed ( form.close() ).
- The isRecordChanged
method -- see abandonRecord above.
- The move
method is a way to programmatically move a form. You can call this method
with parameters that denote the top and left positions and the form will move
accordingly.
- The open
method is how you open an MDI form ( form.open() ).
- The pageCount
method returns the number of pages of the form that have controls on them.
It does this by returning the highest pageNo property of all controls in the
form's constructor code.
- The print
method can be used to print the form. Note that your results may vary ...
- The readModal
method is how you open an SDI form. For this to work, the mdi property
MUST be set to false.
- The refresh
method redraws the form.
- The release
method releases the form object from memory. Note that you should not do this
in the form's onClose event as it will often cause a GPF.
- The saveRecord
method see abandonRecord above.
- The setFocus
method can be used to set focus to this form -- this is useful if multiple
forms are open.
- The showFormatBar
method can be used to turn the format toolbar on or off.
Types of Forms
There are three main "types"
of forms that I can think of:
- Data Forms -- these are
ones that interact with the data -- these will have datamodules, databases,
queries, rowsets, etc., and objects that are designed specifically to interact
with the rowset and field objects. There are many, many ways to design data
forms, but these are the ones that you see the most in any application, as
they are the most important for the application. These forms may be modal
or modeless (opened with the readModal() or open() methods of
the form).
Data forms should use
the rowset property of the form if you are using the OODML (if you
are migrating forms from earlier versions of Visual dBASE, then you will
be using the view and/or designView properties). The reason
to use the rowset property is that it is much easier to work with
an object reference of:
form.rowset.next()
than one that looks
like one of the following:
form.query1.rowset.next()
// or:
form.datamodref1.ref.query1.rowset.next()
There are some tricks
to using this discussed below, with more in the HOW TO document "Misc. Code
Examples" in this Knowledgebase.
- Dialog Forms -- these
are forms that interact with the user to get specific information. These may
interact with the data (rowset/field objects), but they may not. In most cases
dialog forms are modal (opened with the readmodal() method of the form).
dBASE allows you to
hook into quite a few of these for your own use as a developer. Here are
the ones I can think of off the top of my head -- use online help for each
of these dialogs for more details:
- MsgBox() -- this
allows you to ask the user questions; simply let them know something (warning,
error ...); and react in your code based on what button the user has clicked
on.
- GetFont() -- allows
the you or the user to select a font and font attributes. For the most
part this is not really useful in most applications, but as a developer
it might come in handy to obtain information about a specific font.
- GetColor() -- allows
you to select a color and returns the hexidecimal value. (You need
to usually translate this a bit, as the GetColor() dialog returns values
in a different sequence than is used by the color properties of forms
and form controls.)
- GetDir() -- select
a directory/folder (this calls the standard Windows dialog).
- GetFile() -- select
a file to open (this calls the standard Windows dialog).
- PutFile() -- select
a file to save to (this calls the standard Windows dialog).
- ChoosePrinter() --
select the printer to print to, and set some properties for a report (or
for the whole application -- not necessarily a good idea). You may want
to use this from the report object's printer object: reportName.printer.choosePrinter(),
rather than using it from the application object: _app.choosePrinter().
Using it from the application object will set the default printer for
your application, using it from the report's printer object will set the
printer for that one report.
- Message/Information Forms
-- these forms usually are not designed for the user to directly interact
with, but to let them know that a process is occuring, and often what stage
in the processing the program is currently at. This lets the user realize
something really is happening and that their computer is not just "frozen".
This is a good place
to put a progress bar if your users need a visual clue ...
Tips and Suggestions
Form Design Issues
As you might imagine
there are always a ton of concerns when designing forms. Here are a few pointers:
- Design your forms to
the lowest common denominator for screen resolutions. Many developers design
their forms on high resolution (1024 x 768, for example), but find that their
customers have their monitors set to lower resolutions (often 800 x 640).
There is no good way to make your form resizable for different resolutions
using forms in dBASE. (People have tried, and the amount of coding necessary
is unweildy with less-than-satisfactory results.) When all else fails, find
out what the lowest resolution your users (customer/client) will be using,
and make sure your forms are designed for that resolution.
In order to assist
with this, the form designer shows a pair of lines that give you a place
to cut off your forms for the lowest standard resolution of 640 by 480 --
if you make sure none of your forms are larger than that set of guidelines
(they won't show when the form is executed), you should be fine.
- Text controls can use
a lot of resources -- the text controls in dBASE are able to interpret some
of the standard HTML 3.0 standard tags. This means that when you run a form,
each text control has to evaluate the contents of its text property
to see if there are any HTML tags, and if so, execute those (to format the
text).
Many folk have found
that the best way to get around this limitation is to use entryfield objects
with the border set to "None" and various other changes made to the appearance
so that they look just like text. Make sure if you do this that you set
the return property to false ( {; return false} ) or your user may
find some odd results occuring on the form.
With Visual dBASE 7.5
dBASE, Inc. added a new control called a textLabel control which
is basically a text control without the HTML ability. This reduces the amount
of resources used when running a form, and you may want to use this instead
of the entryfield suggested above, unless you are working with Visual dBASE
7.0 through 7.01.
You may wish to set
a custom control (or more if you need different types of text controls)
for this.
- When designing forms
you should avoid using the stock controls as much as possible. This is discussed
in every HOW TO document I have created as well as the dBASE Tutorial in the
Knowledgebase, because it is so important.
If you have an application
with 30 forms and your user suddenly decides that each and every entryfield
must behave a little differently, or use a different font, then you must
go through each form to change it. Unless you used custom controls
-- in which case you can make the change in the custom control file that
defines the control(s), and every form will automatically be updated. (See
the HOW TO on Form Controls in the Knowledgebase.)
- As above, you should,
for the same reasons, use custom forms as much as possible. There are some
guidelines and suggestions for this in the HOW TO document on Custom Forms
in the Knowledgebase.
Using the Form's Rowset Property
When you are working with data forms, you should, as noted earlier,
use the form's rowset property.
A lot of custom controls
assume that you have this property set, and will attempt to work on that particular
rowset.
If you have multiple tables
open for a form, you can switch back and forth between them, and change the
form's rowset property to point to a different rowset, which would then
allow any custom buttons or other controls that work on the form's rowset to
work on a different one than what the form opened with.
An example of this kind
of thing might be a form where you had master and detail rowsets (linked either
with masterRowset and masterFields or with the masterSource
properties). Normally the master rowset would be the controlling one assigned
to the form's rowset property. However, rather than having two sets of
navigation buttons (to move the row pointer through the rows), you might, instead,
have one set of buttons that work on the form's rowset, and switch the
rowset reference in some fashion. This is covered in detail in the "Misc. Code
Examples" HOW TO document in the Knowledgebase.
Running a Form
There are two main ways to run a form. The first is to execute
it like code:
do myform.wfm
This has the advantage
that it is easy to work with, but it has some disadvantages as well.
When you run a form this
way it defaults to a modeless form. You can force this to a "modal" (dialog)
form by:
do myform.wfm with true
This causes the code just
above the constructor code (the statement that reads "CLASS myformForm OF FORM")
to set the mdi property of the form to false, and to open the form with
the readModal() method, rather than the default open() method.
When you execute a form
this way, the form reference is "local" to the program, meaning that you cannot
access it from outside of the form's code. If you need to communicate with the
form in some fashion, you can't. If you need to control the form from outside
of the form's definition, you can't.
The second method of executing
a form is more complex, but it allows you some control over the form that you
do not enjoy the other way:
set procedure to myform.wfm additive
fMyForm = new MyFormForm()
// select one of the following:
// if the form is to be opened modeless/MDI:
fMyForm.open()
// or:
// if the form is to be opened modal (dialog):
fMyForm.mdi := false
fMyForm.readModal()
Once the form is open one
or two things can happen. If you opened the form with the open() method,
any code you have designed to execute after the form is opened will execute
NOW. In other words, a modeless form is now an open window waiting for
user interaction, and any code after the call to open() will continue
to execute.
If the form is opened as
a modal form (with readModal()), any code that is after the call to the
readModal() method will wait until the form is closed before it is executed.
As you can see, this makes
for some interesting design questions when you develop your application. In
most cases, if you are opening a form "modeless", you simply ensure that there
is no code that you want to execute after the form is opened in the code that
is used to open the form.
Forms Communicating with Other
Forms
Rather than re-write
the excellent work by Alan Katz, you should go to the Visual dBASE 5.x HOW TO
section in the Knowledgebase and get hold of "FORMVARS.HOW". This HOW TO document
was written for VdBASE 5.5, but most or all of the techniques discussed in it
hold true for Visual dBASE 7.x through dBASE.
Deactivate Your Data Objects
If you have an application that needs to use the same tables
in different forms (or multiple instances of the same form), or has a utility
program to pack the tables or what-have-you, you or your users may have seen
an error worded something like:
Row in use by another ... Retrying lock ...
The problem is, sometimes
the row is not in use by another. Or is it?
If a form does not deactivate
the tables, and sometimes even the database object (if one is in use), then
the table may indeed be considered by the software to "be in use", and an individual
row (the one you or your user was last looking at/editing) may indeed still
be "locked".
How can you get around
this? The best way is to deactivate your data objects. The top level data objects
(datamodref, database and query) all have an active property -- if you
set this to false, you will solve the problem.
The best place to do this
is in the form's canClose event:
function form_canClose
// if using a datamodule:
form.datamodref1.active := false
// if not using a datamodule:
// form.query1.active := false
// form.database1.active := false
In some cases this is not
always good enough, however -- usually the situation where this doesn't do the
trick is when you are using the masterRowset and masterFields
properties of the rowset for a child/detail table. In those cases, you may need
to add a call to the (child or detail) rowset's unLock method, which
will remove this particular issue.
function form_canClose
// if using a datamodule:
form.datamodref1.childquery1.rowset.unlock()
form.datamodref1.active := false
// if not using a datamodule:
// form.query1.active := false
// form.childquery2.rowset.unlock()
// form.childquery2.active := false
// form.database1.active := false
While there is no 100%
guarantee that either of these will completely do the trick, the testing done
by the author shows that it appears to work.
So, What Can I Do With All This?
Your best bet, if you are not
sure, is to look at the sample applications that ship with dBASE, other HOW TO
documents, and some of the sample code out there. In addition there is a Visual
dBASE 7.x Tutorial in the Knowledgebase. The tutorial is a project that walks
the developer through creating a simple MDI application all the way through to
deployment.
Summary
This document is very much
a "beginner's guide" to the working with forms in dBASE, so not every topic has
been covered in the detail it might deserve (or you might specifically need).
As with many of the HOW
TO documents, the idea is to get you started. You can find other HOW TO documents,
including documents on Custom Forms, Multiple Page Forms, and a detailed HOW
TO about most of the various Controls that can be used on forms in the Knowledgebase.
DISCLAIMER: the author is
an employee of dBASE, Inc., but has written this on his own time. If you have
questions regarding this .HOW document, or about dBASE you can communicate directly
with the author and dBVIPS in the appropriate newsgroups on the internet.
.HOW 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 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: BEGFORM.HTM -- January 22, 2004 -- KJM