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.

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.

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 ...)


Types of Forms

There are three main "types" of forms that I can think of:


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:

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