Session E-CLASS
Building
Applications
Using Class Libraries
Toni M. Taylor-Feltman
Neon Software
Overview
At the heart of Object Oriented Programming are class libraries. Class libraries are the building blocks of all object oriented applications. Visual FoxPros class designer gives developers the tool necessary to create these "building blocks." Creating applications from class libraries results in more easily maintained applications.
This session focuses on how class libraries will help us create better applications, faster. Class libraries are more than just a group of objects that are dropped on a form. They are the nucleus of all applications. We will explore how to design reusable class libraries that can be utilized throughout the application development cycle. The applications we create will be easy to maintain and enhance. You will be able to leverage the class structure in every application you create. With the right set of tools, building an application becomes a "plug and play" job. At the same time, maintenance becomes a breeze.
In order to gain the most from this session, we are going to look at a very simple example that that may look familiar to you. The example is EX1 that shipped with FoxPro 2.x. This example uses some obsolete features from FoxPro 2.x, such as the foundation read, however, it is a simple example of how most applications work. We are going to build an example very similar to EX1 using class libraries, while looking at each of the objects that make up the application. Then we will explore how easily the application can be modified and enhanced.
EX1: What is it made of?
There are several pieces to the EX1 application. The first is the main program, ex1.prg. This program creates and sets some global memory variables, sets the application environment, calls a the menu, ex1.mpr, and places the application in a "wait state." Once the application wait state is cleared, the application environment is reset.
The menu ex1 is used to drive the application. The menu option, Application, is where all of the forms are located. When one of these forms is selected from the menu, the form appears with a toolbar that is used for navigation. The same toolbar is used for every form, yet when a new form is selected from the menu, a new toolbar does not appear. Once the Quit option is selected from the menu, the application wait state is cleared and the cleanup portion of the main program is executed. This is where the application environment is reset.
Each form has most of the same properties. All of the forms use a window that is centered on the desktop. Each of these windows use Ms Sans Serif, Bold, Size 8 as the default font. The window is a User style window with the following options: Close, Move, Minimize, and Single Border. All of the forms have similar Cleanup Code and Read Deactivate clauses.
Inside each form there are labels, text boxes and edit regions. The text boxes and edit regions are the objects used to enter data into the tables. The labels are the objects that represent prompts for the text boxes and edit regions. This project also contains two utility forms. They are the Browser and Finder forms. These forms are called from the Find and Browse options of the tool bar.
How was it created?
In order to fully understand how creating applications using class libraries will help you create better applications, lets look at what went into creating Ex1 and how one might create an application based on the concepts introduced in Ex1.
First of all, we need to look at what went into creating Ex1. The following items are in no particular order, but they represent all of the components of the application.
In order to add a new screen to the Ex1 application, the following would need to take place:
A similar set of steps could be followed to create an entire new application based on the concepts in Ex1. When it is laid out in this fashion, it doesnt look like that much work, however, Ex1 is a simple example that uses many of the FoxPro defaults such as the window style for a form. Each time you want to move away from the defaults, you add a step to the process.
It would be nice if you could create a template, or series of templates, of the type of application you want to create and build each of the applications based on these templates. Then, you would have defaults that match your application programming style rather than FoxPros. This is where building applications using class libraries will make application development easier for you.
Getting Started on the Class Libraries
Before we start creating the classes that will be used during application development, we need to decide what items should go into the classes. If we look back at the components of Ex1, we should be able to get a good idea of what items would fit into a class.
The Main Program
In Ex1, the main program saves environmental settings that the application may change, establishes the application operating environment, establishes the application wait state and resets the working environment. This series of events will take place in almost every application we create. Therefore, the main program would fit well as an Application Class object.
The application will be an object that will have custom properties used to store environment information. Also, the application object will become the top of the application hierarchy and common routines will become custom methods of this object. This approach will allow us to keep the common application code in one place where modifications will be amended in every application that uses the object.
Keep in mind that we will still need a main program which will make sure that the application object can be created and will destroy it when the application is terminated.
The Application Menu
Just as in Ex1, the application menu will drive the application. Most of the forms, by nature, will be accessed from the menu. The menu differs from other components in an application in that it is more specific to the application. Each application will have different forms and other items that may be called from a menu. Because of this, a menu class may not fit well in the application hierarchy.
The Visual FoxPro Menu Builder does access class objects in the same fashion as the form designer in that class objects cannot simply be dropped into a menu. We will reference methods and properties from our classes through the menu, however it will not be its own class object. A more advanced framework may include an object for managing menus.
The Tool Bar
In the example, Ex1, the tool bar was created as a separate form. This form was added to the other forms by way of a screen set. Since the tool bar functions are the same for every form it is attached to, it is generic. A Tool Bar Class would fit well into our application hierarchy.
In FoxPro 2.x applications, when the tool bar was included with a form it used the same .SCX file, however the code for it was duplicated in each screen program. Our class structure will be intelligent enough to know when a tool bar is active so that we only have one instance running at any one time. This will help eliminate much of the screen flicker that we see in FoxPro 2.x applications when the user switches windows.
The Forms
Each data entry form will have many of the same properties. The window style, colors and fonts will always be the same. These properties can be set in a base form class object which would then be used as the template for all data entry forms. Another benefit to using this approach is that any common code can be placed in the form class object as methods and will automatically be inherited by all instances of the form class. This allows us to keep the common code in one place rather than requiring that it be found in each individual form.
In our example, we will not require that a tool bar be used with the form. If we did not want to use the tool bar with a form, we could easily place the navigational command buttons directly on the form.
Form Objects
All of the objects used on a form will have similar characteristics as well. For example, all labels should use the same font and alignment and command buttons should use the same font and colors. If we create a class for each type of object that will be used on a form, we can set the properties at the highest level and all objects based on the parent class will inherit the properties. This will insure uniformity in our forms.
The Code
The Application Class
The Application object will be the driver of our applications, therefore it contains many custom properties and methods. This is similar to the memory variables and procedures that were contained in the main program of your FoxPro 2.6 applications. This class is based on the Custom base class. It is considered a non-visual class because there is not a screen object associated with the class (i.e. nothing you can see <g>). The following table explains each of the properties and methods used in the application class.
Property/Method | Value/Purpose | Type |
---|---|---|
Name | "Application" | P |
cDatabase | Main DBC Name | P,C |
cAppname | The name of the application | P,C |
cMainMenu | The name of the main menu | P,C |
nVersion | Version Number of the application | P,C |
cOldScreenCaption | The caption of the desktop window before the application runs | P,C |
cSetTalk | Setting of SET TALK | P,C |
cSetPath | Setting of SET PATH | P,C |
cSaveDir | Current directory | P,C |
cSetClasslib | Setting of SET CLASSLIB | P,C |
cSetEscape | Setting of SET ESCAPE | P,C |
cSetSafety | Setting of SET SAFETY | P,C |
cSetDate | Setting of SET DATE | P,C |
cSetProc | Setting of SET PROCEDURE | P,C |
cSetStatus | Setting of SET STATUS | P,C |
nSetMemo | Setting of SET MEMOWIDTH | P,C |
cSetMultilocks | Setting of SET MULTILOCKS | P,C |
cSetHelp | Setting of SET HELP | P,C |
cSetDeleted | Setting of SET DELETED | P,C |
cSetExclusive | Setting of SET EXCLUSIVE | P,C |
cSetNotify | Setting of SET NOTIFY | P,C |
cSetBell | Setting of SET BELL | P,C |
cSetNear | Setting of SET NEAR | P,C |
cSetExact | Setting of SET EXACT | P,C |
cSetIntensity | Setting of SET INTENSITY | P,C |
cSetConfirm | Setting of SET CONFIRM | P,C |
cOnShutdown | Setting of ON SHUTDOWN | P,C |
lDeveloper | Is a developer running the application? | P,C |
cSetCentury | Setting of SET CENTURY | P,C |
lok2Exit | Logical that determines if the Ok2Exit method has been executed yet. | P,C |
nFormCount | The number of forms currently active. | P,C |
oToolBar | The property used to reference the tool bar object. | P,C |
Destroy | In case the application is being exited by a means other than the Quit option on the menu, or clicking the close box, we call the Ok2Exit method to clean up the environment when the application object is destroyed. | M |
Init | Saves the SET environment and set the necessary environment for the application. This also opens the DBC associated with the application. | M |
Do | The DO method which gets the application running. | M,C |
SaveSets | Saves the SET environment | M,C |
SetSets | Sets the SET environment for the application | M,C |
RestoreSets | Restores the SET environment when the application is exited | M,C |
DoForm | Runs a form | M,C |
DoMenu | Runs the menu program | M,C |
Ok2Exit | Determines if it is OK to release the application object | M,C |
ReleaseToolbar | Determines if the Tool Bar needs to be released. | M,C |
AddToolbar | Determines if the ToolBar object needs to be created. | M,C |
Type: P=Property, M=Method, C=Custom Property or Method
Tool Bar Class
The Tool Bar Class is made up of several pieces. The tool bar is an object itself. The tool bar contains command buttons that allow for navigation and editing. The following table outlines all of the items in the tool bar.
Name | Base Class | Property/Method | Value/Purpose | Type |
BaseTool | ToolBar | Caption | "Navigation Tools" | P |
Height | 30 | P | ||
Left | 0 | P | ||
Top | 0 | P | ||
Width | 178 | P | ||
ControlBox | .F. | P | ||
Name | "BaseTool" | P | ||
Destroy | Make the tool bar invisible so that it clears faster. | M | ||
Init | Dock the toolbar when it is first instantiated. | M | ||
Refresh | Set the enabled property of the Top, Previous, Last and Next buttons based on the status of EOF() and BOF() | |||
btnTop | CommandButton | Top | 4 | P |
Left | 6 | P | ||
Height | 23 | P | ||
Width | 24 | P | ||
Picture | ..\bmps\frsrec_s.bmp | P | ||
Caption | "" | P | ||
Default | .F. | P | ||
Name | btnTop | P | ||
Click | Run the First method in the Active Form and refresh the tool bar. | M | ||
btnPrev | CommandButton | Top | 4 | P |
Left | 29 | P | ||
Height | 23 | P | ||
Width | 24 | P | ||
Picture | ..\bmps\prvrec_s.bmp | P | ||
Caption | "" | P | ||
Default | .F. | P | ||
Name | btnPrev | P | ||
Click | Run the Prev method in the Active Form and refresh the tool bar. | M | ||
btnNext | CommandButton | Top | 4 | P |
Left | 52 | P | ||
Height | 23 | P | ||
Width | 24 | P | ||
Picture | ..\bmps\ nxtrec_s.bmp | P | ||
Caption | "" | P | ||
Default | .F. | P | ||
Name | btnNext | P | ||
Click | Run the Next method in the Active Form and refresh the tool bar. | M | ||
btnLast | CommandButton | Top | 4 | P |
Left | 75 | P | ||
Height | 23 | P | ||
Width | 24 | P | ||
Picture | ..\bmps\ lstrec_s.bmp | P | ||
Caption | "" | P | ||
Default | .F. | P | ||
Name | btnLast | P | ||
Click | Run the Last method in the Active Form and refresh the tool bar. | M | ||
btnNew | CommandButton | Top | 4 | P |
Left | 104 | P | ||
Height | 23 | P | ||
Width | 24 | P | ||
Picture | ..\bmps\ new.bmp | P | ||
Caption | "" | P | ||
Default | .F. | P | ||
Name | btnNew | P | ||
Click | Run the Addthod in the Active Form and refresh the tool bar. | M | ||
btnSave | CommandButton | Top | 4 | P |
Left | 127 | P | ||
Height | 23 | P | ||
Width | 24 | P | ||
Picture | ..\bmps\ save.bmp | P | ||
Caption | "" | P | ||
Default | .F. | P | ||
Name | btnSave | P | ||
Click | Run the Save method in the Active Form and refresh the tool bar. | M | ||
btnCancel | CommandButton | Top | 4 | P |
Left | 150 | P | ||
Height | 23 | P | ||
Width | 24 | P | ||
Picture | ..\bmps\ undo.bmp | P | ||
Caption | "" | P | ||
Default | .F. | P | ||
Name | btnCancel | P | ||
Click | Run the Undo method in the Active Form and refresh the tool bar. | M |
Type: P=Property, M=Method, C=Custom Property or Method
Form Class
The form class has been designed to be very flexible. Since the form determines if the tool bar should be used, the form class controls hiding and showing the tool bar. Also, the methods used to navigate the form are contained in the form rather than the tool bar. This will allow the developer to customize the navigational methods in particular forms. The following table shows the properties and methods used in the form class.
Property/Method | Value/Purpose | Type |
---|---|---|
BorderStyle | 2 | P |
BufferMode | 1 | P |
DataSession | 2 | P |
ScaleMode | 3 | P |
BackColor | 192,192,192 | P |
Caption | Base Form | P |
FontSize | 9 | P |
Left | 0 | P |
Name | BaseForm | P |
Top | 0 | P |
lToolBar | Should the tool bar be used? | P,C |
Activate | 1. Make sure the alias
used for the screen is selected. 2. Make sure that the tool bar is refreshed or hidden when the form is activated. |
M |
Destroy | Sets the visible property to .F. so that the form clears quicker and removes one from the number of forms active. | M |
Init | Checks to see if this form uses the tool bar. If so, AddToolBar in the app class is run to make sure that the toolbar object exists. Also, one is added to the number of forms active. | M |
QueryUnload | Determines if it is Ok to release a form. | M |
Unload | Run the ReleaseToolBar Method in the application object to determine if the tool bar should be released. | M |
Add | Add a new record to the table or view used in the current form | M,C |
ChangePending | Returns a character string indicating if a change has been made to the current record in the current form. | M,C |
First | Display the first record in the data source. | M,C |
Last | Display the last record in the data source. | M,C |
Ok2Continue | Prompts user to save record if necessary before moving the record pointer or closing the form | M,C |
Next | Display the next record in the data source. | M,C |
Prev | Display the previous record in the data source. | M,C |
Save | Save changes to the table or view used in the current form. | M,C |
Undo | Cancels uncommitted changes made to the table or view in the current form. | M,C |
WriteBuffer | Writes the value of a control to the corresponding field if the value has changed. | M,C |
Type: P=Property, M=Method, C=Custom Property or Method
Objects Classes
The purpose of creating a object classes is to set the common properties in the class. Then we will be able to use these class objects on any form and the properties will be the same. In our example, only one of the objects, the text box, contains code in a method.
The Init Method of the custom text box will set the input mask and width properties when the object is instantiated. This will allow us to base these properties on the actual size of the ControlSource used for the object.
The following table illustrates which default properties have been set for each object. This table does not contain all of the possible object types, only the ones that we might use in our example.
Object Name | Base Class | Properties Set |
---|---|---|
chkCheckBox | checkbox | Height = 22 Width = 148 FontBold = .F. FontSize = 9 BackStyle = 0 Caption = "Check1" Name = "chkCheckBox" |
cboComboBox | combobox | FontBold = .F. FontSize = 9 Height = 22 Style = 2 TabIndex = 1 Width = 200 DisabledBackColor = 192,192,192 Name = "cboComboBox" |
cmdCommandButton | commandbutton | Height = 22 Width = 80 FontBold = .F. FontSize = 9 Caption = "Command1" Name = "cmdCommandButton" |
opgOptionGroup | optiongroup | ButtonCount = 2 BackStyle = 0 BorderStyle = 0 Value = 1 Height = 60 Width = 90 Name = "opgOptionGroup" optOption1.Caption = "Option1" optOption1.Height = 18 optOption1.Left = 5 optOption1.Top = 5 optOption1.Width = 68 optOption1.Name = "optOption1" optOption2.Caption = "Option2" optOption2.Height = 18 optOption2.Left = 5 optOption2.Top = 25 optOption2.Width = 68 optOption2.Name = "optOption2" |
edbEditBox | editbox | FontBold = .F. FontSize = 9 Height = 44 Name = "edbEditBox" |
lblLabel | label | FontBold = .F. FontSize = 9 Alignment = 1 BackStyle = 0 Caption = "Label1" Comment = "" Height = 22 Width = 80 Name = "lblLabel" |
shpShape | shape | BackStyle = 0 Height = 22 Width = 80 SpecialEffect = 0 Name = "shpShape" |
txtTextBox | textbox | FontBold = .F. FontSize = 9 Format = "K" Height = 22 InputMask = "" Width = 80 Name = "txtTextbox" |
lstListBox | listbox | FontBold = .F. FontSize = 9 Height = 66 Width = 80 Name = "lstListBox" |
Building the Application
Now that we have designed the classes to be used to create our application, we will create it. As you will see, it is very simple to build the application based on these classes. We will be creating a sample application involving three forms, Customer, Offices and Parts. The Customer and Parts forms will use the tool bar and the Offices form will not. Lets do it!
Step 1 - Create the project
To start, we need to create a project. Since the menu is not a class, we will copy a template menu to the application directory and add it to the project. We will also add a template startup program to the project and make it the main program file.
Step 2 - Adding the Application Object
The main program will take care of creating the application object, however, we will subclass the application object for each application so that we can set some of the properties that will be specific.
From the Classes Tab of the project manager, select New. The class name will be Sample and it will be based on the Application class in the Startup class library. Then we need to modify this new class and change the following properties:
Step 3 - Creating the Forms
Now that we have the application class set up, we can create the forms. Before we create a new form, we need to set the Form Template Class to our BaseForm in the Baseform class. This can be done from the Forms tab under the Options dialog. Now we are ready to create a form.
Caption: Customer
Information
Name: frmCustomer
lToolBar: .T.
You can follow these same steps for the other two forms as well. Remember that the tool bar will not be used on the Offices form.
Step 4 - Adding the form calls to the menu.
The last thing that we need to do is add the appropriate code to the menu that will call our forms. When we open the menu from the Project Manager, you will notice that the Application Menu Pad is empty. This is where the form calls will go. We will be running the forms through the Application objects DoForm Method. The command with be:
oApp.DoForm("<form name>")
Step 5 - Building and running the application
Now we are ready to build the application and run it!
Making Changes
You may be saying to yourself that you could create an application in a similar fashion under FoxPro 2.x, so what is the big deal? Under FoxPro 2.x, you may have had empty forms, text boxes, check boxes...etc, that you used as templates. These worked really well when creating a new application, but what about maintaining applications? If you found a bug in the Setup Code of your template form, you had to make the change in the template form as well as each form, in every application, that used the template.
The beauty of using classes is that properties and methods are automatically inherited! You dont even need to think about it. When you make a change in the parent class, every object based on that parent will have the change immediately. To gain a better understanding of this, lets look at the following changes...
Conclusion
The one downfall to this approach is that you really have to plan out your applications. There is a great deal of work necessary before you ever start working on the actual pieces of an application. On the other hand, if you dont do it right the first time, it is very easy to implement the changes. This is very important when it comes to working with a new programming language. When you start working with Visual FoxPro 3.0, you will be lost! You may not do everything the "best" way the first time. It is comforting to know that when you do find the "best" way, you only need to implement the change in one place.