Subclassing in the Visual DataFlex IDE

A Data Access Worldwide White Paper
by Vincent Oorsprong

December 2002
Last Edited: June 7th, 2003

Overview
The aim of this document is to supply information about a new wizard I developed for Visual DataFlex. The wizard helps in creating one or more subclasses by supplying the necessary DataFlex code and by making the IDE aware of the new class.

Visual DataFlex, WebApp and Character Mode DataFlex (up from revision 3.0, released in 1990) allow you to create an Object Oriented Program. When creating a visual or a non-visual interface, you are creating objects of a certain class. The class library delivered with the product might cover all your immediate wishes, but the ability to create new classes will enhance your programming knowledge and efficiency in the long run. As soon as you add a procedure, a function or a new property definition to an object, the compiler will automatically create a class for you with the programmed extension and then base your object upon this class. If you instead want to use your own classes and want to use them in the Visual DataFlex IDE, you need to make the IDE aware of these new classes.

Why Subclassing?
Why would you want to subclass? There are multiple reasons, but perhaps the most important one is maintenance. If you have two or more objects of the same class which require equal or almost equal code additions, you will need to repeat these additions for each object. You can avoid the process of going into the components that contain these objects and changing the source code by subclassing. If you have a subclass, you can do that at a central place and after recompilation all the objects based on this class will have the changed functionality.

A New Class is Born
To create a new class, you first need to find out the best superclass to base your new class upon. This requires you to study the documentation to see which superclass already has the functionality you want. This sounds worse than it is since you already chose the correct class for your controls or invisible objects.

The code for the new class can be written in the component source code, but it is usually stored in its own source code file (package). In both cases the requirements are that the compiler is aware of the new class before you use it for the first time.

Because we want to teach the process to you in the correct manner, we create a packagefile with a unique name. As you can use long filenames in WebApp and Visual DataFlex, I advise to use the name of the new class for the name of the package. For example, if your class is cMyEdit, than your packagename can be cMyEdit.Pkg. Coding each class in its own package prevents you from including more source code than is strictly necessary.

Let us, as an example, assume that you have created an edit object that shows a message when all the text space is used and converts all character input into uppercase characters (by setting the capslock_state property).


Figure 1: Object coding instead of using a class.

In full source code mode, the code for this object would look like:

Object oMyEditor is a Edit
   Set Size to 126 287
   Set Location to 5 5

   //AB-StoreStart
   Set Capslock_State To True
   Set Text_Limit To 1000

   Procedure OnMaxText
      Send Bell
      Send Info_Box "All the space available for editing has been used!"
   End_Procedure // OnMaxText
   //AB-StoreEnd

End_Object // oMyEditor

When subclassing, the first step is to create the class outline, the skeleton:

Class cMyEdit Is An Edit
End_Class // cMyEdit


After you have the skeleton, you can move the procedures and functions from your object to the classbody. That results in:

Class cMyEdit Is An Edit
   Procedure OnMaxText
      Send Bell
      Send Info_Box "All the space available for editing has been used!"
   End_Procedure // OnMaxText
End_Class // cMyEdit


The code in an object that resides outside of procedures and functions is executed when the object is created (constructed). Therefore, all code present between object and end_object which is placed outside of a procedure or function (like newly created properties) should go in the procedure construct_object. Now our class would look like:

Class cMyEdit Is An Edit
   Procedure Construct_Object
      Forward Send Construct_Object

      Set Capslock_State To True
      Set Text_Limit To 1000
   End_Procedure // Construct_Object

   Procedure OnMaxText
      Send Bell
      Send Info_Box "All the space available for editing has been used!"
   End_Procedure // OnMaxText
End_Class // cMyEdit


Now you could create an object of this class by simply writing:

Object oMyEditor is a cMyEdit
   Set Size to 126 287
   Set Location to 5 5
End_Object // oMyEditor

If you later wish to extend the functionality (for example, adding the ability to replace the text for the messagebox), you can modify the class and all objects of this class to support the new functionality.

Making the IDE Aware of Our Class
After we have written our class source code we can use it anywhere in our programs. However, if we change the classname of an object in one of the components editable in the IDE to our new classname, we will get the following error from the IDE.


Figure 2: Error reading source code

You can not load this component in outline mode if you do not make the IDE aware of the new class. Making the IDE aware of the new class involves creating an IDE CLASSDEF (.DFC) file and registering the class in the IDE's classlist.

The minimum contents of the DFC file for our cMyEdit class should be:
//AB/ CLASSDEF
Class cMyEdit Is An Edit
   Inherit
End_Class


The header line of the DFC file must be exactly as shown. Otherwise, the IDE will reject the DFC.

Where a subclass in Visual DataFlex always inherits behavior (properties, methods) from the superclass, you need to tell the IDE whether this subclass inherits the properties from its superclass or not. Not inheriting is only in use in the IDE and not in DataFlex code itself. The technique of not using the INHERIT keyword is an advanced one and is not advisable in this phase. The best place for this keyword is immediately after the classname line.

Our class sets the default value for two properties (Capslock_State and Text_Limit). Both properties exist as Visual DataFlex properties but are not exposed via the object properties toolbox. To change this we can add them to the DFC file. This is done by changing the DFC file to:

//AB/ CLASSDEF
Class cMyEdit Is An Edit
   Inherit
   Property No_Execute Integer Text_Limit 1000
   Property No_Execute Boolean Capslock_State True
End_Class


As you can see, the property definition statement is a little different from the Visual DataFlex source code variant. An extra instruction, No_Execute, is inserted between the property command and the type one. These are instructions for the IDE. The following instructions are possible:

KeywordDescription
No_VisibleThis property will not appear in the property list
No_GenerateThis will never get generated to a source file. Usually used in conjunction with No_Visible
Always_GenerateThis property is always written to the source file
No_Execute This property is not directly executed within the IDE. It can be loaded, changed and saved. This can be used when a sub-class is being defined in the meta-data that contains properties not known to the IDE.
ItemWhen loaded and saved this property is a zero item property. e.g., Set Value item 0 to var.

You can combine two or more of the above keywords but they should not be excluding each other. So a combination of always_generate and no_generate
, for example,
makes no sense and should not be used.

In our example we only use No_Execute since we would like to see and be able to set the properties via the Object Properties toolbox and generate source code lines only when the property values are different from the values loaded via our DFC file. You should use the same defaults in the DFC file as in the .PKG file. The reason for this is that when an object is created from a class, the procedure construct_object is executed. When this code sets default values for properties, it is not necessary (and not desirable) to have the settings duplicated in Visual DataFlex source code.
Besides setting a default value for properties, we can also define new properties in the DFC file. This is done like above but you should always insert the keyword no_execute since the IDE will never know how to execute your property.

Note: Not all properties understood by the IDE should be flagged as executable because they can make working with the objects in the IDE quite difficult (or impossible). As an example; do not try to make enabled_state executable. It will nicely make the color of the object change, but you will not be able to select the object with the mouse anymore (like in run mode)

Note: The code inside the DFC file will NOT change the runtime behavior of the classes. It will not change your application at runtime, and it will only make the IDE aware of the class.

What Else Can We Do with the DFC File?

The following table shows IDE instructions which are effective in the IDE design only. These instructions will change the way the IDE works with the object of the class. For example, the IDE can write the package name to include the Visual DataFlex code at compile time. This helps you in that you do not need to write too many USE statements anymore.

Property NameDescription
ObjectNameMaskInitial object name. As a part of the name one can add <count> which will be the current counter for objects from this class. This makes object names like dbForm1, dbForm2 etc.
Control_TypeWhat type of control is it? Used to tell the IDE whether the control is database aware or not.
Band_AllowedCan the control be Selected (rubber-band placed around it)?
Move_AllowedCan the control be moved?
MoveSize_AllowedCan the user move or size the control (for example, TabPages cannot be moved or sized, as their parent controls those properties)
Drop_AllowedCan controls be dropped onto this class? Valid values are TRUE, FALSE and any number. When any number is used only objects of that number can be dropped on top of this object. Specifying this value is done via the Type_Of_Drag property.
Type_Of_DragWhat type of drag operations are permitted?
Item_Based_ObjectIs the class item-based?
Floating_Menu_IdWhat is the object-id of the Context Menu to use? Only predefined context menu's can be used.
Row_Based_ObjectIs the class row-based?
Entry_Item_ObjectShould an Entry_Item be generated?
ClassVersionImplemented, but not currently used
ClassVendorImplemented, but not currently used
ClassPackageThe name of the file that contains the VDF class. When an instance of the class is used, this property will be written out as a "Use" statement. For example, if you set this property to "BigButton.pkg", the SRC file would include a line: "Use BigButton.pkg". If you include all your classes in the pre-compiled package, you will not need to set this property, otherwise, you will always set it.

As an example we will use the ClassPackage property from the above list of IDE properties.

//AB/ CLASSDEF
Class cMyEdit Is An Edit
   Inherit
   Property No_Execute Integer Text_Limit 1000 // The maximum number of characters that can be entered in the control
   Property No_Execute Boolean Capslock_State True // Convert all keystrokes to uppercase
   Set ClassPackage To "cMyEdit.Pkg"
End_Class

When we are ready with the DFC file we can register the class in the IDE's classlist by going to the dialog captured in figure 3.


Figure 3: Register the class in the IDE

The Name form should contain the name of the class (in this case it is cMyEdit
). When you want to make it possible to create a control via drag-and-drop you should enter the next two forms in the modal dialog. The bitmap of the Image form should be placed in the IDESrc directory and should be of 24x24 size. The name of the Controls Palette page to put in the Page form is up to you. You can select one of the existing Controls Palette pages and your control will be inserted on that page. If you do so, make sure that the bitmap you plan to use  is not already used on that Controls Palette page since the internal imagelist does not allow to load two bitmaps with the same name. The last form is only needed when you want to create a DFC file with more than one class definition inside. If you leave it blank the IDE will try to load the class definition from a DFC file with the same name as the classname.

When you have entered all the information you can click the ADD button followed by the OK button in the underlying dialog. If you close the maintain classlist dialog the IDE will reload the classes so that you can now use your new class in the IDE. The error from figure 2 should not occur anymore.

The ClassCloner Wizard
To make it easier for anyone to create a subclass from an existing DAW global or workspace class I wrote a wizard that does the following:


The ClassCloner wizard can make subclasses of all IDE registered classes.


Figure 4: Welcome to the ClassCloner wizard

If you have started the ClassCloner wizard you will see the screen as captured in figure 4. The only options here are 'next' and 'exit' and since we have the intention of creating a new subclass, the logical step is to click the Next button.


Figure 5: Select a workspace.

The next step in the subclassing process is to select the workspace of your choice. The current user workspace, if available, will be highlighted in the screen captured in figure 5. By selecting a workspace you will tell the wizard to load directory information and the subclass layer, if it exists, from this workspace. You can always go back to this screen if you think you made a wrong choice. If you select a different workspace all previously made choices will be undone.


Figure 6: Create one or more subclasses in one run.

The wizard page captured in figure 6 lets you make a choice between creating one subclass or multiple subclasses in this wizard session. The default choice is creating one subclass. The option to create multiple subclasses is built into the wizard to make it possible to subclass a complete workspace subclass layer.


Figure 7: Select source class.

The next few steps show how the single subclass process works. This is step 1 of this process. In this step you select in the comboform the source class to base your subclass on. The comboform shows all the global and workspace classes registered in the IDE's classlist. You can only continue after selecting a source class.


Figure 8: Enter new subclassname and IDE controls palette page.

The next step in our process will be entering the new subclassname and the location inside the IDE's Controls Palette. Do not use an existing classname since there is no checking here that prevents you from doing so. When you leave the palette pagename blank you cannot create an object of your subclass via drag-and-drop. The classname, however, will be known to the IDE and you can create objects based on this new class. This is particularly useful for subclassing component level classes (dbView, dbModalPanel, ReportView, ClientArea etc.) or classes that are treated in a special manner by the IDE (tabpages for example).


Figure 9: Finish the wizard.

This page shows you a summary of the actions that will take place when you click the 'Finish' button. If you do not agree with the information you should click 'exit' or go back through the wizard and change your choices. Until the 'Finish' button has been clicked nothing is permanent yet.

The next 4 paragraphs are important for you if you choose "multiple classes" in the wizard page capture by figure 6 .


Figure 10: Select multiple source classes.

This is the first step if you choose the option to subclass more than one class at the same wizard session. Select the classes you want to subclass by clicking the checkbox or clicking the button labeled 'Select All' if you want all or almost all classes. It might be easier to deselect a few classes after clicking the 'Select All' button instead of selecting all classes that you want to subclass individually. You need to select at least one class before you can continue with the wizard in forward navigation.


Figure 11: Enter options to apply to all the new subclass names.

In this page you can specify what general options you want to apply to all the classes you are subclassing. The first choice is the prefix you want to use for your subclassing. Entering no prefix is possible but then you should add the prefix or suffix in the next wizard page to avoid duplicate classnames. The sample box will show you how the prefix will look like. The second form in this page lets you enter a prefix to be used for all palette pages. When you do not enter a prefix here the subclasses end up in the same palette page as the source class. This is allowed and it is up to you whether you like this or not. The last choice on this page gives you the option to remove a leading 'c' in front of classname or not . The 'c' in Hungarian Notation (HN) stands for 'class' and it is considered a bit weird to have a classname like 'cMycImageList' when creating a subclass from 'cImageList'. A subclassname like 'cMyImageList' would be better. The default for the checkbox is true and the sample form shows the result. Do not use this if the 'c' is really part of the name and not there because of HN reasons.


Figure 12: Check the new classnames or edit the names individually.

This list will contain the selected classes for subclassing. Each subclassname and/or palette pagename can be changed manually. Verify that you are happy with the information shown in this page. You cannot remove classes from this list here. Instead, you will need to go back to the step where you selected one or more classes for subclassing in order to remove a class.


Figure 13: Finish the wizard.

This page shows you what the wizard is going to do when you click the 'Finish' button. This time it does not list all the classes individually but information about the destination location. If you do not agree with the information you should click 'exit' or go back through the wizard and change your choices. Until the 'Finish' button has been clicked nothing is permanent yet.

To incorporate the wizard into your VDF environment,  download the attached ClassCloner wizard for VDF8.3 or the ClassCloner wizard for VDF9.0. The wizard is created with Visual DataFlex 8.3 or 9.0 and only runs with that revisions. To use this wizard unzip the ClassCloner.Exe from the archive and place it in the BIN directory of your Visual DataFlex development set. You can start the wizard by double clicking or by adding the wizard to the utilities menu in the IDE (the better alternative).


Figure 14: Add the wizard to the utilities menu of the IDE.

Figure 14 shows how to add the classcloner wizard to the utilities menu of the IDE.

If you want to learn how to program a wizard like the classcloner wizard contact one of the following Data Access representatives about the Visual DataFlex Special 1 titled "classes".

^top

Data Access Worldwide
14000 SW 119 Ave
Miami, FL 33186
305-238-0012
Domestic Sales: 800-451-3539
Fax: 305-238-0017
email: sales@dataaccess.com
Newsgroup Server: news.dataaccess.com
Internet: http://www.dataaccess.com

Data Access Worldwide - Asia Pacific
Suite 5, 333 Wantirna Road, Wantirna VIC 3152 Australia
Phone: +61 3 9800 4233 f: +61 3 9800 4255
Sales: asiapacific@DataAccess.com
Support: support.asiapacific@DataAccess.com
Internet: http://www.DataAccess.com/AsiaPacific

Data Access Worldwide - Brasil
Av.Paulista, 1776 - 21st.Floor
São Paulo -SP - Brazil
CEP 01310-921
Phone: 5511-3262-2000
Fax 5511-3284-1579
Sales: info@dataaccess.com.br
Support: suporte@dataaccess.com.br
Internet: http://www.dataaccess.com.br

Data Access Worldwide - Europe
Lansinkesweg 4
7553 AE Hengelo
The Netherlands
Telephone: +31 (0)74 - 255 56 09
Fax: +31 (0)74 - 250 34 66
Sales: info@dataaccess.nl
Support: support@dataaccess.nl
Internet: http://www.dataaccess.nl

Data Access Technical Support
800-451-3539 / 305-232-3142
email: support@dataaccess.com
Visit our Support Home page to see all of our Support options: http://www.dataaccess.com/support

Copyright Notice
This document is property of Data Access Corporation. With credit to Data Access Corporation for its authorship, you are encouraged to reproduce this information in any format either on paper or electronically, in whole or in part. You may publish this paper as a stand alone document within your own publications conditional on the maintenance of the intent, context, and integrity of the material as it is presented here.

DataFlex is a registered trademark of Data Access Corporation.
Windows
is a registered trademark of Microsoft Corporation.

NO LIABILITY FOR CONSEQUENTIAL DAMAGES
To the maximum extent permitted by applicable law, in no event shall Data Access Corporation be liable for any special, incidental, indirect, or consequential damages whatsoever (including, without limitation, damages for loss of business profits, business interruption, loss of business information, or any other pecuniary loss) arising out of the use of or inability to use any information provided in this document, even if Data Access Corporation has been advised of the possibility of such damages. Because some states and jurisdictions do not allow the exclusion or limitation of liability for consequential or incidental damages, the above limitation may not apply to you.

^ top