Upgrading from Prior Revisions of Visual DataFlex

Upgrading from prior revisions of Visual DataFlex is easy. Simply follow these steps:

  1. Install Visual DataFlex Studio on the same system where you have the prior revision of Visual DataFlex installed. Since the new release of Visual DataFlex uses a new set of folders, registry entries and service names, it will not impact the running of your existing Visual DataFlex at all.

  2. From inside the studio, select "Open Workspace" from the File menu, then select "Workspaces to Migrate" from the "Files of Type" combo form. Now you can open a .ws file for any existing workspace. If this workspace has not been migrated already, the "Workspace Migration Wizard" will launch.

Of Special Note

If you are migrating an older application (Visual DataFlex 7 or Web Application Server 3) the automatic migration process described above will not work. Please see the Data Access Worldwide Knowledge Base for information on how to migrate older applications.

http://www.dataaccess.com/kbasepublic/KBPrint.asp?ArticleID=2272

See Also

  • Improvements that May Impact Compatibility


Special Notes for Migrating Older Applications

Improvements that May Impact Compatibility

The Visual DataFlex development environment is constantly evolving and improving. As you migrate older Visual DataFlex applications, you should pay special attention to the following changes that have been made since Visual DataFlex 8.0:

  • Automatically Sizing and Locating Controls

  • Class Changes

  • Compiler Improvements

  • Data Dictionary Changes

  • Deprecated Commands

  • Errors

  • Files no Longer Needed

  • Grid Changes

  • Image Support

  • Language Changes

  • Navigation Changes

  • Registry Changes

  • Resources

  • Status Panel Changes

  • Studio Meta-Data Tags

  • Web Application Changes

  • Workspace Changes


Automatically Sizing and Locating Controls

Anchors

Anchors provide the capability to design your Components so that when they are re-sized at runtime, all the child controls can automatically move and re-size themselves to use the available space, without the need for manual programming.

When you set an anchor, you determine which edges of the control you want to remain relatively constant to its parent, whenever the parent is resized. For example, if you anchored a control to the right edge, it would move horizontally to ensure that it keeps the same distance to the right edge that existed when the objects were created. If you anchored a control to all edges, then its height and width would change whenever its parent's size changed. In all cases, the target of setting anchors is to maintain the same relative size and location of controls to their parent.

A single property, peAnchors, has been added to each control. peAnchors describes how you want the control to react whenever its container (dbView, Group, etc.) changes size.

Set peAnchors to one of the following constants:

Constant | Description
anNone | The control should do nothing when the parent changes size
anTop | The control will maintain the same distance from the top edge of the parent. The control will not move or resize itself.
anBottom | The control will maintain the same distance from the bottom edge of the parent. The control will move vertically when its parent's height is changed.
anTopBottom | The control will maintain the same distance from the top and bottom edges of the parent. The control's height will change when its parent's height is changed.
anLeft | The control will maintain the same distance from the left edge of the parent. The control will not move or resize itself.
anTopLeft | The control will maintain the same distance from the left and top edges of the parent. The control will not move or resize itself.
anBottomLeft | The control will maintain the same distance to the bottom and left edges. This will result in the control moving when the parent is resized.
anTopBottomLeft | The control will maintain the same distance to the top, bottom and left edges of the parent. This will result in the control changing its height when the parent is resized.
anRight | The control will maintain the same distance to the right edge of the parent. This will result in the control moving horizontally when the width of the parent is changed.
anTopRight | The control will maintain the same distance to the top and right edges of the parent. This will result in the control moving horizontally when the width of the parent is changed.
anBottomRight | The control will maintain the same distance to the bottom and right edges of the parent. This will result in the control moving vertically and horizontally when the width and height of the parent is changed.
anTopBottomRight | The control will maintain the same distance to the top, bottom and right edges of the parent. This will result in the control's height changing and the control moving horizontally when the height and width of the parent is changed.
anLeftRight | The control will maintain the same distance to the left and right edges of the parent. This will result in the control getting wider when the width of the parent is changed.
anTopLeftRight | The control will maintain the same distance to the top, left and right edges of the parent. This will result in the control getting wider when the width of the parent is changed.
anBottomLeftRight | The control will maintain the same distance to the bottom, left and right edges of the parent. This will result in the control getting wider when the width of the parent is changed and the control moving vertically when the parent's height is changed.
anAll | The control will maintain the same distance to the top, bottom, left and right edges of the parent. This will result in the control's height changing when the parent's does, and its width changing when its parent's does.

Here is a dbView that has been designed to make careful use of anchors:

We would want to change this View so that when it was re-sized, the controls would change size and location intelligently. The decision of which anchors to set is completely dependant upon the contents being displayed. In this example, the Edit control was deemed to be the most important, and so the other anchors are set to reflect this. This is how the peAnchor property was set for these controls:
Name = anTopLeftRight

Edit = anAll

NOK = anBottomLeftRight

Age = anBottomRight

When resized by the end-user, it can look like this:

Note: You will need to ensure that you set the Component's Border_Style property to Border_Thick; otherwise, you will not be able to resize the border, meaning that the Components can never get bigger, which, in turn, also means that none of the child control's anchors will ever be applied.

Minimum and Maximum Sizes

Allowing the end-user to change the sizes of Components (dbView, ModalPanels, etc.) brings a new challenge: specifically, the temptation to see what happens when the Component is shrunk as small as it will go. This is what they will see:

Hardly satisfactory! The solution to this is to limit the minimum size that a Component can be. You achieve this by setting the piMinSize property. The piMinSize property follows the same pattern as Size. That is, you set piMinSize in the same way that you set the Size:

Set Size To 100 250

Set piMinSize To 100 250

In the preceding example, the Component's minimum size will be the same as the size of the object, meaning that it can only get larger. If you wish, you can set it to be smaller, but test it to be certain that it cannot be shrunk to an unacceptable size.

You can control the maximum size to which a Component can be resized by setting the piMaxSize property:

Set Size To 100 100

Set piMinSize To 50 50

Set piMaxSize To 200 600

In Summary

A property, peAnchors, has been added to all controls (any visual objects), which ensures that the control retains the distance to the edges that it is anchored against, whenever the parent's size is changed.

Two properties, piMinSize and piMaxSize, have been added to the Component object (dbView, ModalPanel, etc) that limit the size to which the Component can be resized.

You need to ensure that you set the Component's Border_Style property to Border_Thick (which is not the default); otherwise, Windows will not let the end-user change the size of the Component.

Think carefully about how you want to set the anchors. It is entirely possible to set anchors in such a way that they will not logically work together. For example, if you have two Forms on the same row and you set them both to anchor left-and-right, they will overlap each other, as the Component gets wider (the first Form's right edge will go beyond the left—unmoving—edge of the second Form.). The solution would be to make one anchor left-and-right, but the other to be anchored left (if it was the first control) or right (if it was the second control). Alternatively, if you really want them both to get wider, then put them on different rows.


Classes

Class Changes

The following Classes have been created or changed:

  • TabDialog—improvements to focus and navigation

  • cVersionInfo—retrieve version information from a program

  • cRegistry—encapsulates registry access

  • cHtmlHelp—enables HTML help to be used in programs

  • cApplication—forms the heart of the program. Control of the program's registry usage, help system, Workspace, Command-line etc., is controlled here

  • cIniFile—Used internally (not documented) to read/write INI files used in Workspaces

  • cWorkspace—encapsulates the Workspace in the program

  • cCommandLine—stores the command-line arguments and provides information about the program invocation

  • cHttpTransfer—additions for large file support, etc.

  • cXmlHttpTransfer—permits transfer of XML documents via HTTP. Transferred as XML DOM objects.

  • Grid and DbGrid, dbList —embedded Spinform, prompt buttons and automatic resizing of columns.

Class Hierarchy Changes

A new base class, cObject, has enabled a streamlining of the class hierarchy

FlexCOM 2

Read about the collection of COM-related classes: cComActiveXControl, cComAutomationObject, cComDbActiveXControl, cComDocumentObject, cComStdFont and cComStdPicture.


Class Hierarchy Changes

One of the goals for Visual DataFlex 8.3 is to start the process of reorganizing the class hierarchy. Changes in the class hierarchy will lead to better (and easier) understanding of the class library, cleaner application code that is easier to debug and set the basis for expanded capabilities.

In VDF8.3 we have addressed what is probably the most significant design limitation in the class hierarchy. In all prior versions of Visual DataFlex the base class was based in UIObject, which was a rather complicated user interface class with many messages. This class was a poor choice when creating non-UI classes and objects. To complicate matters, this class could not be used for either sub-classing or instantiation forcing developers to use even higher level and even more inappropriate classes for development.

We have now split the UIObject class and created a very simple, non-UI, base class named cObject. All classes are derived from cObject. The UIObject class, now a sub-class of cObject has been renamed cUIObject. Some of the messages from UIObject were moved to cObject while the rest remained in cUIObject. In addition, both of these classes can now be directly sub-classed and instantiated.

While this class change represents a very low-level change in the runtime, we have designed this to be backward compatible with existing programs. Since all existing programs derive classes and objects from UIObject this change should be completely compatible.

New Base Class: cObject

A new class, cObject, is created that can be used for sub-classing and instantiation. It has the following interface:

Class cObject

Public Properties

Property Integer Child_Count

Property Integer Delegation_mode

Property Handle Object_Id

Property String Object_Label

Property String Name

Property Handle Parent

Property Integer peNeighborhood

Public Methods

Procedure Construct_Object

Procedure End_Construct_Object

Function Create integer iClassId returns Handle

Procedure Destroy

Public Events

Procedure Destroy_Object

Private Properties

Property Integer Class

Property Integer Base_Class

Private Methods

Procedure Init_Instance

Function Object_Size Integer Arg1 Returns Integer

Procedure Set Location integer iHeight integer iWidth

Two other private methods exist in cObject, Set Focus_mode and Set Size. If either of these methods is sent to a cObject, the object will intentionally return an error. This was added for backward compatibility considerations. These are not messages you should ever send to a non-UI object but the old class hierarchy might have required this. These errors are there to provide you with a warning that they should be removed. For final release, we may decide to remove the error and do nothing (i.e., not delegate).

By default, the delegation_mode of cObject is delegate_to_parent, which means that your applications will work as they always did. However, when creating classes and objects based on cObject you may decide to disable delegation by setting the property to no_delegation. If you have created a class or object that does not need automatic delegation (and you will find that this is often the case), setting delegation_mode to no_delegation creates a class that is much easier to debug. Messages that are not understood are reported as errors, instead of delegating where they might be improperly understood. Even if delegation is turned off you can always explicitly delegate a message by using the delegate command. This often makes your intention clearer.

Sub-classes and objects may be created based on cObject using standard syntax. For example:

Class cMyInfoClass is a cObject

Procedure Construct_object

Forward send construct_object

Property string psMyInfo1

Property string psMyInfo2

End_Procedure

End_Class

Object oMyInfo is a cMyInfoClass

Set psMyInfo1 to “hi”

Set psMyInfo2 to “there”

End_object

<or>

Get Create U_cMyInfoClass to hoInfo

Set psMyInfo1 of hoInfo to “hi”

Set psMyInfo2 of hoInfo to “there”

<or>

Object oMyInfo is a cObject

Property String psMyInfo1 “hi”

Property String psMyInfo2 “there”

End_object

If you have existing non-UI classes they are currently either based on Array or on a UI class (DFObject was often used). These classes will continue to work exactly as they did and you do not need to make any changes. If you do choose to change these classes and make them sub-classes of cObject you can do this by:

1. Change the class name to cObject

2. Search for UI messages in the class and remove them. Look for:

• Set Focus_mode (this is the most likely method and it is no longer needed)

• Set Size

• Set Location (leave this one alone – the Studio uses this and cObject ignores it)

3. If you are moving the class from array to cObject note that the default delegation_mode will change. Array objects neither delegate nor report errors. cObject objects delegate. Make sure you set delegation_mode properly.

Changes in cUIObject

cUIObject is now descended from cObject. All methods defined in the old UIObject reside in this class with the exception of the methods that were moved to cObject. This class can now be used for instantiation and subclassing, although such use will probably be rare.

Objects that now derive from cObject

Now that we have a proper base object we have changed the hierarchy of a number of our classes. Previously these non-UI classes were based on UI super-classes (mostly dfObject). They are now placed where they belong.

StatusPanel

StatusdbLog

StatusAsciiLog

ColorDialog

FontDialog

PrintSetupDialog

PrintDialog

AbstractFileDialog

HelpSystem

cHtmlHelp

cApplication

cCommandLine

cImageList

cIniFile

cMapiHandler

cRegistry

cStatusPane

cVersionInfo

cWorkSpace

cToolBarButton

cBusinessProcess

CrystalReport

BaseData_set

Mixin

Report

ErrorSystem

Class Name Changes

The following class names have been changed.

BaseClass to cUiObject

Desktop to cUIObject

dfDesktop to cDesktop

dfPanel to dfBasePanel

dfControl to dfBaseControl

dfSession to dfBaseSession

dfserial to dfBaseSerial

HorzScrollBar to dfBaseHorzScrollBar

VertScrollBar to dfBaseVertScrollBar

SystemMenu to dfBaseSystemMenu

ImageList to dfBaseImageList

Message to cUIObject **

_Data_set     to BaseDataSet (note _Data_set is no longer defined)

The old names are still supported and defined as replacements. However, they will not be documented and our package library no longer uses any of the old names. The exception to this is "_data_set" which was private and is no longer defined.

** The Message class is not really equivalent to the cUIObject class but in all cases where we've used it, we were using it as a base class. If someone is actually using the message class for its get/set value interface they will need to change their program. This is quite unlikely.

Important: You are encouraged to no longer use the class names BaseClass, Desktop and DFDesktop. These could only be used when using the “Procedure Xxxxx FOR cClassName” syntax. Actually we discourage the use of the FOR syntax in general. Defining a method “for Desktop/BaseClass/cObject” is a very risky technique. Placing a method on the desktop (i.e., making it a method for the cDesktop object) should be done by using the new “DESKTOP” syntax which is discussed in the Global/Desktop Methods section.


Compiler Improvements

IF Statements

We have improved the compiler error checking to report invalid "If" statement syntaxes.

DataFlex supports different methods of coding If statement evaluations.

Those are:

Type 1: If var ...... <or> If (var) ......

If bOk ....

If (iError) ....

Type 2: If (var = Xxxx)

If (iValue = iOldValue) ....

If (iMode <> eModeValue) ...

Type 3: If var eq Xxxx

If iValue eq iOldValue ....

If iMode ne eModeValue ...

When a number of new datatypes were added to VDF7 (e.g. datetime, short, currency), we declared that these types would not all work property with Type 3 syntax. This is an old style of syntax usage that has been discouraged since VDF5. You were advised to use type 1 or type 2 syntax. If you used type 3 syntax with certain data-types, the program would compile, it just may not provide you with the evaluation you want. It also turns out that type 1 syntax did not always work properly with some datatypes.

Example

DateTime dtVar

If (dtVar) .......

The above code will probably not work nor does it make much sense as there is no way to know how to declare a datetime value as being false.

Beginning with Visual DataFlex 8.3, all of these invalid datatype usages are reported at compile time.

Now you get an error at compile time if you try to use Type 1 or Type 3 If syntaxes with the following variable types

Float, Decimal, Currency, Time, DateTime, TimeSpan, Variant, BigInt, UBigInt, UInteger

The following data types may be used with all IF statements

Real, Number, Date, String, Integer, Boolean, Short, UShort, Char, UChar, Address

The disallowed types can still be used, but they must be used properly within a boolean expression. For example:

Float fNumber

Move 0 to fNumber

If fNumber .... // This will generate a compiler error

If (fNumber) .... // This will generate a compiler error

If fNumber ne 0 .... // This will generate a compiler error

If (fNumber<>0) .... // This will work as expected. Use this!

You will spot any problems in your code when you compile your application. If you get a compiler error, the code is improper and must be changed. If the code was improper, it was not working properly with your previous Visual DataFlex version - it just didn't tell you there was a problem. The improved compiler error checking will find these problems for you.

Although type 3 syntax (e.g., If Var NE Xxxxx ....) is allowed for a number of datatypes, this usage is till considered obsolete and therefore not recommended.

In improving this error checking we also discovered and fixed a bug with the Address datatype and IF statements. The following never worked properly:

if pData ... <or> "if (pData) .....

Prior to this fix you had use the "if (pData<>0)" syntax. This has been fixed and any of the above syntaxes now works properly.


Data Dictionaries

The enhancement to Data Dictionaries fall into four categories.

  • Zero Suppression with Masks

  • Changes in Retain and Retain-All

  • Dynamic DD Field Options

  • New DD Methods


Zero Suppression with Masks

Masks now support zero-suppression for numbers, dates and strings. Zero-suppression is enabled by inserting a "Z" as the first character of the form mask. The "Z" must be the first character and it must be capitalized. Note that suppression is only applied to forms when they do not have the focus.

When applied to numeric and currency masks the form will be cleared if its value is zero.

set form_datatype to mask_currency_window

set form_mask to "Z$,#######.00;($,#######0.00)"

When applied to a date mask the form will be cleared if its value is zero (previously the form would appear as " / / ").

set form_datatype to mask_date_window

set form_mask to "ZM/d/yyyy"

When applied to a string mask the form is cleared if the value is blank. Technically this is not zero suppression but blank suppression.

set form_datatype to mask_window

set form_mask to "Z(###)###-####"

Zero Suppression in Data Dictionaries

The Zero suppress option in the DataDictionary class (DD_Zero_Suppress) now works with VDF. Normally, zero-suppress will be selected as a field option from Database Builder. When applied all mask forms that use that field will be zero suppressed. Zero suppression can only be applied to windows that have masks. By default, the Database Builder sets mask types for dates and numbers. This can be used to zero suppress dates and blank suppress strings as well as zero suppressing numbers.

When the zero suppress is selected in data-dictionaries any DEO using that field will insert a "Z" in front of its mask. You can also apply zero-suppression to a DataDictionary field by creating a field mask with a "Z" as the first character. This is not recommended. Use the zero-suppress checkbox instead.

Zero Suppression in DDOs is dynamic. If you change a field’s zero-suppression attribute in a DDO, all DEOs using that field will be changed as needed.


Changes in Retain and Retain-All

The behavior of the Retain and Retain-All attributes has been improved in DDOs. They now work as follows:

  • If the DD_Retain attribute option is set for a field, the value of that field will be retained during a DDO clear operation . It will be cleared during a DDO clear_all operation.

  • Retains are applied to the DDO receiving the clear message and all parent DDOs. It is not applied to child DDOs (you would not want it applied to child DDOs).

  • If the DD_RetainAll attribute option is set for a field, the value of the field is always retained. Retain-All is applied to all DDOs. It is not expected that the DD_RetainAll option will be used often.

Important: It is really expected that Retain will be applied at the DD object level and not as part of the class definition. The choice to retain or not retain a value is not something that can usually be applied to all objects in all views using a particular DD class. Instead retains should be applied based on the data entry requirements of a particular view. This means that the DD_Retain field option in Database Builder will rarely be checked. Instead, you should set retained fields directly within the DD object. You can do this using the new 8.1 messages set file_field_option, set file_field_option_clear, and set file_field_option_toggle.

The following lines inside of a DD object will set retain fields for customer.State and Customer.Zip_Code

Set file_field_option file_field Customer.State to DD_Retain

Set file_field_option file_field Customer.Zip_code to DD_Retain

You can clear or toggle the retain option as follows:

Set file_field_option_clear file_field Customer.State to DD_Retain

// make sure retain is cleared

Set file_field_option_toggle file_field Customer.State to DD_Retain

// toggle the current retain state

Changing Retains Dynamically

The ability to clear and toggle fields is useful because the retain attribute (and in 8.1 all DD option attributes) can be changed dynamically. You could create views where the person doing the data-entry can decide which fields in their view to retain. The following sample code could be added to your view object and would let the user toggle the retain state of any field in the view.

// all objects in this view will send ToggleRetain when Ctrl/F4 is pressed.

On_key key_ctrl+key_f4 send ToggleRetain

// When sent from a Ctrl/F4, the message will delegate here to the view

// because ToggleRetain is not directly understood by the other objects

Procedure ToggleRetain handle hoDEO hoServer integer iFile iField

// we want to react to the focus object. The object where ctrl/f4 was pressed

Get Focus to hoDEO

// non deos will understand this message via delegation...see below

Get Data_file of hoDEO to iFile

If (iFile=0) Procedure_return // probably not a DEO that has the focus

Get Server of hoDEO to hoServer

If (hoServer) begin

Get Data_field of hoDEO to iField

set File_field_Option_Toggle of hoServer iFile iField to DD_RETAIN

end

End_procedure

// this augmentation was created to allow non-deos to respond to ctrl/f4

// without an error message. Non-deos will delegate this message to

// here, return 0 and do nothing... look at toggle retain

Function Data_file returns integer

Function_return 0

End_function

Retains and Setting Default Field Values

Database Builder allows you to create default values for fields. For example, the default value for customer.state is "FL". These defaults are applied after field values are retained and consequently they will overwrite the value of the retained data. So, normally you would not use retains and defaults on the same field.

You could use both retains and defaults together to create intelligent defaults. Remember that default values are set within the procedure field_defaults. You can augment the field_defaults method to create custom default values. During that process you can check to see if a value has been retained and respond however you want.

The following code added to the customer DDO would retain the current state value if it exists. If the state is blank or if the state is "CA" (maybe we just don’t like California), the default "FL" will get used.

Set file_field_option file_field Customer.State to DD_Retain // retain customer.state

Procedure Field_defaults string sState

Forward Send Field_Defaults

// get current state value, could be cleared or a retained value

Get File_Field_Current_Value file_field customer.state to sState

// if state is cleared or it is CA, set a default of FL

if (sState="" or sState="CA") Set File_Field_changed_Value file_field customer.state to "FL"

End_Procedure

The Field_defaults method should only be used to set default values for fields from its own DDO. It should not try to change the value of parent or child DDOs and it should not try to perform additional DDO operations (like an autofind).

Retains with Parent (Foreign) Fields

We expect you to assign Retains at the object level. Therefore it is up to you to decide if it makes sense to retain fields from parent DDOs. Normally a parent DDO requires an autofind. Therefore retaining any field other than the key autofind field probably makes little sense. Retaining the key field(s) of a parent file may make a great deal of sense.

If you retain a key parent field, the data will be retained but the record will not be found automatically. This is intentional and it gives you two choices. You can either let the autofind occur when you navigate out of the field or you can force an autofind as part of the clear operation. We will show you examples of both.

Add the following line of code to the OrderHea DDO in the order entry view and compile and run the view.

set file_field_option file_field customer.Customer_number to DD_Retain

When you clear the order view, the customer ID will be retained but the rest of the customer data is blank. When you navigate out of the customer entry field, an autofind will occur. This is one way to use retains with parents. It allows the user to select a different customer id before performing the autofind.

Now try adding the following code to the same OrderHea DDO in the order view.

set file_field_option file_field customer.Customer_number to DD_Retain

// augment clear so that it performs an autofind for the customer. The retained

// data will be used to perform this autofind

procedure clear

forward send clear

send File_field_Default_Autofind file_field customer.Customer_number

end_procedure

When you run this sample you will see that the entire customer appears to be retained. Actually the key field is retained and then after the clear, an autofind is performed. The new DDO message File_field_Default_Autofind is used here to perform the autofind in "default" mode (the DDOs changed state is not set by the find). Note that you should not try doing an autofind as part of the field_defaults process. The Field_Defaults method can only be used to set defaults for the fields that belong to its DDO and you should never perform database activity (and particularly DDO database activity) within this method.


Changes Made to DD Field Options are Now Dynamic

A frequent request has been to make Data Dictionary field settings more dynamic. When a change is made for a field in a DDO (e.g. a retain option, a field mask) the change should be immediately reflected in all data entry objects that use that field. Prior to version 8.1, a DEO acquired DDO field option information when the DEO was activated. If these DDO field options changed after the DEO was activated, the DEO was not notified of this change. Now they are.

Some field settings were already fully dynamic. For example, changes in field prompts, zooms, entry, exit and validate messages were immediately reflected by the DEO (or more accurately, the DEO always handled these by using the DDOs). The areas that were not fully dynamic prior to VDF8.1 were field options, field masks, and field labels.

Dynamic Field Options

Field options such as zero-suppress (dd_zero_suppress) and retain (dd_retain) are now dynamic. If they are changed, the DEOs will immediately use those changes. The options can be set, cleared and toggled by using three new DD messages, file_field_option, file_field_option_clear, and file_field_option_toggle. The old DD message file_field_options is still used to set of the class defaults inside of define_fields (which is usually created for you by Database Builder) but they cannot be used to set options dynamically.

Dynamic options will most often be changed at the object level, and not that the DD class level.

As an example, let’s say that you wanted to change your customer view so you could choose to "lock" the field values for City, State and Zip. The idea here is that you will set these to the values you want, press a key and lock them - the field will be non-enterable and the value will be retained. Then when needed you could unlock those values, change them and if needed lock them again. To do this you add the following code to your dbView object. Assume we will use Ctrl+L to lock and Ctrl+U to unlock.

On_key key_ctrl+key_L send LockFields // these messages will delegate to here

On_key key_ctrl+key_U send UnLockFields

Procedure LockFields Handle hoCustomerDD

Move Customer_DD to hoCustomerDD // object id of customer DD

set File_field_Option of hoCustomerDD file_field customer.state ;

to (DD_RETAINALL IOR DD_NOENTER)

set File_field_Option of hoCustomerDD file_field customer.City ;

to (DD_RETAINALL IOR DD_NOENTER)

set File_field_Option of hoCustomerDD file_field customer.Zip ;

to (DD_RETAINALL IOR DD_NOENTER)

End_procedure

Procedure UnLockFields Handle hoCustomerDD

Move Customer_DD to hoCustomerDD // object id of customer DD

set File_field_Option_Clear of hoCustomerDD file_field customer.state ;

to (DD_RETAINALL IOR DD_NOENTER)

set File_field_Option_Clear of hoCustomerDD file_field customer.City ;

to (DD_RETAINALL IOR DD_NOENTER)

set File_field_Option_Clear of hoCustomerDD file_field customer.Zip ;

to (DD_RETAINALL IOR DD_NOENTER)

End_procedure

The one exception to dynamic changes in DD options is DD_Capslock. While the DEO is notified of the change the control will not reflect this change until it is unpaged and paged. This is a limitation of the windows control.

Dynamic Field Masks

If a DDO field has a mask type assigned to it, changing the mask value with set file_field_mask will be reflected immediately by the DEOs. Also, changing the zero-suppress field option (DD_Zero_Suppress) will also be immediately reflected in your DEOs. Changing the mask value only effects DEOs that have been assigned a mask window type by the DDO (or possibly the DEO). By default, Database Builder assigns the proper masked data-types for fields, which means that dates and numbers will have masks.

You cannot dynamically change the data type of a window (for example you cannot use set file_field_type to change a DEO from a string to a date). Like DD_Capslock, this would require that you "page" and "unpage" a window and it is assumed that there is little need for this feature.

Dynamic Labels

If you change the long or short label of a DDO field using set field_label_long or set field_label_short , your DEOs will be notified of this change. If the DEOs supports auto-labels (auto_label_state in the DEO is true, by default it is false) the label will be automatically updated. Grid objects will update short label names as column labels, while all forms will update long label names.

DEO Notification Process

The following messages are sent to active DEOs to notify them that an option, mask or label has been changed by a DEO.

Procedure File_Field_Option_Changed integer iFile integer iField integer iOptions integer bClear

Procedure File_Field_mask_Changed integer iFile integer iField string sMask

Procedure File_Field_Label_Changed integer iFile integer iField boolean bLong string sLabel

It is not expected that you would ever want or need to augment these messages. If you do, we suggest you look at the package DD_Deomx.pkg and study the existing methods.


New DD Methods

The following methods have been added to the DataDictionary class.

  • File_Field_Index

  • Field_Index

Field_Main_Index is now Obsolete

Prior to VDF8.2, the message Field_Main_Index was used by the DataDictionary and Data_set class to determine the main index for a passed file and field. This method had the drawback that the DDO (or DSO) receiving this message may not be the DDO that owns the file. For example, an Order DD may receive this message asking for the main index for a customer field (where Customer is the parent of Order). When this happens the order DD determines the main-index for this customer without sending a message to the owner customer DD. While this method works properly, it causes confusion when you want go to augment this method. An augmentation in the parent DD (the owner of the field) will not get called. It had been suggested that this be improved. We have implemented this improvement by adding the new messages File_Field_Index and Field_Index.

When sending this message you can use either File_Field_Index or Field_Index. Using File_Field_Index is easier because you do not have to worry that the message is sent to the correct DDO - it will find the owner DDO and send the message Field_Index.

When augmenting this method, always augment Field_Index in the owner DD. You can now be sure that this method will get called.

The packages have all been changed so they no longer call Field_Main_Index. They now call File_Field_Index.

Important Compatibility Note:

If you are using Field_Main_Index anywhere in your code it will still work exactly as before. We strongly encourage you to bring your code up to date. You should search your code and make the following changes:

    1. Search for all occurrences where you call Field_Main_Index and change the name of the call to File_Field_Index.

    2. Search for any occurrences of augmentations of Field_Main_Index (look for function Field_Main_Index) and change it as follows:

// Old pre 8.2 code

function Field_Main_Index integer file integer field returns integer

// should be changed to

function Field_Index integer field returns integer

Notice the name change and the parameter change. Also make sure that the function is located in the owner DDO.

We do not expect that many developers call Field_Main_Index and we expect that even fewer developers have augmented this message. If you have augmented the message you might have already encountered the limitation with Field_Main_Index and, we hope, you will welcome this improvement!

Finally - if you are using data_sets instead of DDOs no change is required. They continue to use the Field_Main_Index method. (Of course, you want to use DDOs.)

  • pbAutoFillFromFirst

  • File_Field_Default_AutoFind

  • File_Field_Option

  • Field_Option

  • File_Field_Option_Clear

  • Field_Option_Clear

  • File_Field_Option_Toggle

  • Field_Option_Toggle


Deprecated Commands

As the Visual DataFlex language evolves, many legacy commands (especially those with their roots in procedural DataFlex) are rendered obsolete. While these commands still compile for compatibility considerations, they are not supported in the same fashion as newer commands and may be removed from the system in future revisions.

How do I know if a command is obsolete?

The Language Reference has a complete section on Obsolete Commands. The online help for all obsolete commands is formatted so that you will clearly see that the command is obsolete and you are provided with a link to the documentation on the replacement command.


Error Number Changes

Errors

In Visual DataFlex 8.2, we cleaned up error reporting in the runtime, compiler and packages. We did this by:

  1. Removing all specific error numbers in packages (e.g. error 42) and replacing them with their symbolic names as defined in errornum.inc (e.g. error Dferr_Find_Past_End_Of_File)

  2. Reassigning all the ad-hoc errors to real errors. Ad-hoc error numbers are the numbers that get assigned that have no association with errors defined in the error data files. Examples of this are error 300, error 999 and error 500.

  3. Providing a method so that the need to create ad-hoc errors will disappear.

  4. Making sure that all errors are reported with the Error command. We removed all message box errors.

  5. Creating new errors entries as needed.

  6. Updating the errornum.inc file in your pkg directory. It now contains symbolic names for all error files. You may wish to review this file.

  7. Changing the runtime so that it provides context information for many of the confusing runtime error messages. For example, when a file a file cannot be opened the runtime will now report the file name.

Most of these changes will not impact your existing applications.

New Generic Errors

The four most important errors added are generic errors.

DfErr_Compile

DfErr_Operator

DfErr_Program

DfErr_Setup

These errors have entries in the error file with error text that is intentionally left blank. You should use these errors when you need to report an error where you wish to provide all of the error message text within your code. These were created so that you will no longer need to generate errors with ad-hoc error numbers. You will use this most frequently to report operator and program errors.

For example, let's say you wish to report an error in your program that reports, "You cannot enter negative inventory quantities". Prior to this change there were two ways to generate this error

Error DfErr_Operator_Error "You cannot enter negative inventory quantities"

Error 300 "You cannot enter negative Inventory quantities"

The first method would generate the error "Operator error. You cannot enter negative inventory quantities". However, you wanted to control the entire error text and you do not want to see "Operator error". This is text provided by flexerrs.dat, the error data file.

The second method would generate the error "You cannot enter negative inventory quantities". This is what you want but to do this you've been forced to use an ad-hoc error number (300).

You would now use the DfErr_Operator. It is a real error but the text provided from the error file is blank. Here are some examples of how you might use these errors.

Error DfErr_Operator "You cannot enter negative inventory quantities"

Error DfErr_Program "Bad data passed in MySpecialProcedure. Contact your programmer."

Error Changes in flexerrs.dat

In Visual DataFlex 8.2 we made two changes in the flexerrs.dat file. We have replaced error 101 with a new error and we have removed error 102. (Actually we moved the original contents of 101 and 102 to dferr001.dat.)

Err# | Error name in errornum.inc | Error Description | Note
101 | Dferr_Cant_Change_Key_Field | Can't change key field | operator
102 |  | <error was removed> | removed

Error numbers in flexerrs.dat between 121 and 4095 are reserved for developer custom errors. If you are maintaining custom errors in this file, you can make these two changes in your custom flexerrs.dat file using the Error Maintenance utility.

In future revisions of Visual DataFlex we will make few and hopefully no changes to this file.

Error Changes in DfErr001.dat

Dferr001.dat (and all error files except for flexerrs.dat) should not be changed by the developer. The exception to this rule will be language translation.

Here is a list of all changes in dferr001.dat. Notice the generic errors (4400-4403) are defined in this file

Err# | Error name in errornum.inc | Error Description | Note
4388 | Dferr_Comp_Illegal_Object_Defintion | Illegal object name definition | Compiler
4389 | Dferr_Comp_Illegal_Property_Defintion | Illegal property name definition | Compiler
4390 | Dferr_Comp_Illegal_Method_Defintion | Illegal method name definition | Compiler
4391 | Dferr_Comp_Illegal_Code_Placement | Illegal code placement | Compiler
4392 | Dferr_Comp_Invalid_Method_Syntax | Invalid method syntax | Compiler
4393 | Dferr_Comp_Invalid_Message_Syntax | Invalid message syntax | Compiler
4394 | Dferr_Comp_Invalid_Object_Reference | Invalid object reference | Compiler
4395 | Dferr_Comp_Obsolete_Unsupported_Feature | Obsolete command is unsupported | Compiler
4396 | Dferr_Comp_Invalid_Argument | Invalid argument | Compiler
4397 | Dferr_Comp_Illegal_External_Function_Definition | Illegal external function definition | Compiler
4400 | Dferr_Compile | <leave blank> | Compiler
4401 | Dferr_Operator | <leave blank> | Operator
4402 | Dferr_Program | <leave blank> | Program
4403 | Dferr_Setup | <leave blank> | Setup
4404 | Dferr_Winprint | Winprint error | Program; Operator
4405 | Dferr_Crystal_Report | Crystal report error | Program; Operator
4406 | Dferr_Dftimer | Timer (dfTimer) error | Program
4407 | Dferr_Flexcom | Flexcom error | Program
4408 | Dferr_Help_System | Help system error | Program
4409 | Dferr_Ini_File | Ini file error | Program
4410 | Dferr_Windows | Windows error | Program
4411 | Dferr_Registry | Windows registry error | Program
4412 | Dferr_Serial_Comm | Serial Communications (SerialComm) error | Program
4413 | Dferr_Windows_Menu | Windows Menu error | Program
4414 | Dferr_Mapi | Mapi error | Program
4415 | Dferr_cApplication | Application/Workspace error | Operator; Setup


Files no Longer Needed

As of Visual DataFlex, a number of files that required in workspaces for previous revisions are no longer needed. You can see the detailed list here.


Grid Changes

A number of enhancements and fixes have been made to the grid classes (Grid, dbList, dbGrid). The information provided here encompasses the enhancements that provide new grid capabilities. You should also review the fixed bug list to see what has been fixed.

Horizontal Scrolling

One of the most frequently made requests for Grid, dbGrid and dbList improvements is to improve their horizontal scrolling behavior. The logic for horizontal scrolling has been completely rewritten and is much improved.

No programming changes are required to use the new horizontal scroll bar behaviors. All changes were made to the Visual DataFlex runtime. Therefore any application running under 8.2, will immediately get these changes.

Prior to 8.2, the horizontal scrolling of grids was tied into the item_change mechanism. This limitation resulted in the following types of reports:

  1. You cannot scroll left and right without causing item focus changes.

  2. You cannot scroll into a shadowed item because it could not become the current item.

  3. If the rightmost columns were shadowed, you could not scroll and see them at all.

  4. If the grid did not have the focus, horizontal scrolling was ignored.

  5. If the grid was read-only or shadowed, horizontal scrolling was impossible

  6. Horizontal scrolling by clicking on the scroll bar arrows would stop if the next column were disabled.

All this made horizontal scroll bar behavior appear quite inconsistent.

Horizontal scrolling now operates without any object focus or item focus change or requirement. Instead of changing items you are now just panning left and right. The minimum scroll increment is a column (which is much the same way spread sheets work). You can view any column you wish without changing the current-item. If you do click on a cell that is not the current item, the grid makes all the needed changes to view and edit that new cell item. If after a scroll, you start typing, the grid will, as needed, snap back and display your changes in the original current cell.

By not binding horizontal scrolling to current-item change you can now scroll and view any column (enabled or disabled) and you can do this if the grid has the focus or some other object has focus. Scrolling the grid does not change the current focus and it does not change the current-item.

This change is best seen by looking at the order entry example. Make the grid column much wider so that horizontal scrolling is required. You should find the newly designed behaviors to be much easier to use and far more intuitive.

All these changes are supported without requiring any changes in your code. We have provided two new messages to allow you to better control horizontal scrolling

IsColumnVisible

Type: Function

Arguments: integer iColumn returns Boolean

This function can be called to determine if a particular column is visible. You can use this to determine if you need to perform a scroll by sending the DoMakeColumnVisible message

Procedure CheckCol integer iCol

Boolean bVisible

Get IsColumnVisible iCol to bVisible

If not bVisible ;

Send DoMakeColumnVisible iCol

End_procedure

DoMakeColumnVisible

Type: Procedure

Arguments: integer iColumn

Sending this messages ensures that the passed column number is fully visible. If the column is not visible, the grid will be scrolled as needed to make it so.

Procedure ScrollLeft

Send DoMakeColumnVisible 0

End_Procedure

Procedure ScrollRight

Send DoMakeColumnVisible (item_limit(self)-0)

End_Procedure

This method is used in objects based on dbGrid and dbList (implemented in the Datalist super-class) to snap the grid back to column zero whenever the message beginning_of_panel is sent to a view. The message beginning_of_panel sends the message OnBeginningOfPanel to all objects. When the dbGrid or dbList receives the message it does the following:

// augment to always force the first column to be visible. This will occur during a

// panels clear or save. It makes sense to restore the column. If you do no want this

// behavior just cancel it.

Procedure OnBeginningofPanel

Send DoMakeColumnVisible 0

end_procedure

Mouse Wheel

The mouse wheel is now supported in grids. No changes are required to make this work. Scrolling the wheel forward or backwards will scroll your list forwards and backwards as expected.

If you wish to create custom mouse wheel behaviors you can augment the event OnMouseWheel. This message is sent every time a mouse wheel is clicked.

OnMouseWheel

Type: event

Arguments: integer iClicks integer iKeys

If iClicks is positive the wheel is being moved forward (away from you). By default this is used to scroll towards the top of the list. If iClicks is negative the wheel is being moved backwards (towards you). By default this is used to scroll down the list.

The iKeys parameter is the windows bit mask that determines what other keys might have been pressed as you move the mouse-wheel. It is not being used in the default implementation. Consult the windows documentation for WM_MouseWheel if you need to use this.

New Colors in Grids

New color properties have been added to Grids, dbGrids and dbLists that allow you to control the color of gridlines and disabled cells. These properties, added to the runtime, were added in response to developer requests.

Note that the default value of all of these properties provides behavior identical to previous versions of VDF.

peGridLineColor

Type: Property

Default=clBtnface

The new property peGridLineColor can be used to change the color of your grid-lines. By default it will appear as clBtnFace, which is the color it has always been. This property is exposed in the Studio.

peDisabledTextColor

Type: Property

Default= clGrayText

This allows you to select a custom color for disabled cell text. Prior to VDF8.2 this color was fixed as clGrayText (which remains the default). You may now change this to any text color you wish. This property is exposed in the Studio although you will not see the effect of the change until you run your application.

pbUseDisabledColor

Type: Property

Default=False

Normally a grid uses the same background color for enabled and disabled cells. In VDF8.2 you can choose to set a different color for disabled cells by setting the peDisabledColor property. This property color is used only if the pbUseDisabledColor is set to True. When pbUseDisabledColor is set to False, the default, a disabled cell's background color is determined by the Color Property - the same property used to set enabled cell background color

The pbUseDisabledColor property also changes the way disabled cells are displayed. When false, the highlight row color will be applied to all cells enabled and disabled. This is how Visual DataFlex has always behaved. When true, the peDisabledColor takes precedence over the highlight row color.

This property is exposed in the Studio although you will not see the effect of the change until you run your application.

peDisabledColor

Type: Property

Default=clWindow

This determines the background color of a disabled cell. This color is only applied if the pbUseDisabledColor property is set to true. When false, the Color property is used to set the background color of all cells in the grid.

Because pbUseDisabledColor defaults to false, this new property will have no affect on existing applications until you change that property.

This property is exposed in the Studio although you will not see the effect of the change until you run your application.


Image Support

Bitmaps can now be displayed transparently, and both bitmaps and icons can be physically embedded in your programs.

Embedded Images

When images are embedded, they don’t need to be physically deployed.

You do not need to make any changes to your programs to use bitmap resources. The Bitmap, Form_Bitmap and Icon properties have been modified to automatically look in the embedded resources, and if it is not found there, to look on disk as it did before. Similarly, the AddTransparentImage and AddImage functions of the cImageList class have been modified in the same way.

Transparency

In the past, when you made bitmaps for your application, you had to know the color of the background so that your image would blend properly into it. However, this was almost impossible, as end-users often changed the background colors of their Windows machines. Usually, if end-users had "standard" colors, the images looked fine but, if they used "non-standard" colors, the images looked unprofessional.

Look at this image as used in the Studio:

The images of arrows were created with a gray background (Color = clLtGray). When displayed against a gray background, the image looks fine

However, if end-users changed the color of 3d objects on their machines, this is what the image would look like.

The gray background of the images looks strange, now that the buttons are green.

This is how the image would look with transparency.

The background color of the image has been converted automatically to the color of the button, producing a much more desirable effect.

Miscellaneous

If you do not specify an icon in your program's panel, it will use the one you selected in the Program Properties dialog in the Studio. If you do not specify an Icon in your Views, the Panel's Icon will be used.

An icon file (often called an Icon Library) can contain both 16x16 and 32x32 pixel icons. Your programs will automatically show the appropriate one. If you do not provide both a 16- and 32-pixel icon, the one you do provide will be either stretched or shrunk if it needs to be displayed in a different size.

Because DataFlex searches for the images in the resources first, you can use an alias technique. This involves providing a different physical name from the logical one. For example, you might add “World.bmp” as a bitmap resource, but later decide it would be smarter to convert this to a RLE compressed bitmap. If you did this, it would only be a matter of changing the physical name (World.bmp to World.rle) in your resource (in the IDE’s Program Properties dialog) and everywhere your program used “World.bmp”, the “World.rle” bitmap would be used instead, without any changes to your code.

Advanced Notes

The bitmap resources are true, native resources that Windows understands. Consequently, you are able to use Windows API functions to load these resources. To see an example of this, look at cImageList's AddTransparentImage function.

Related Topics

Programming for Transparency explains the changes you can make to your code to support transparent images.

Embedded Resources explains what resources are and their advantages.

The Application Properties dialog in the Studio is used to maintain the list of resources that should be linked into your programs.


Programming for Transparency

To support transparency in bitmaps, the Bitmap and Form_Bitmap properties support two optional flags at the end of the bitmap name:

Flag

Description

/t

Will change all white pixels in your bitmap to the "Window" color of the machine displaying the image. Technically, the clWhite color will be changed to clWindow.

/3d

"/3d" will convert the following colors:

Color | To Color
Lt Gray (RGB: 223,223,223) | clBtnHighlight
Gray (RGB: 192,192,192) | clBtnFace
Dark Gray (RGB: 128,128,128) | clBtnShadow

The purpose of this option is to permit bitmaps that use "standard" 3d-colors to look like 3d-objects on the target machine.

Note:

You can use "/t" and "/3d" together. The result is that "/t" will take precedence, but white pixels will be changed to the 3d-object color. Technically, clWhite will be changed to clBtnFace.

Examples:

If you have bitmaps that use gray backgrounds, this is how you would change your code from...

Set Bitmap To "AbUp.bmp"

...to...

Set Bitmap To "AbUp.bmp/3d"

You have a bitmap with a white background. To display it on a Textbox, you would write:

Object oTextbox is a Textbox

Set Color To clWindow

Set Bitmap To "HappyFace.bmp/t"

End_Object

And, to display it on a Button, you would write:

Object oButton is a Button

Set Bitmap To "HappyFace.bmp/t/3d"

End_Object

Important Note

Transparency is only supported for bitmaps that were embedded as Bitmap Resources and will be ignored for disk-based bitmaps.

Limitations

A current limitation of Windows transparency is that bitmaps can only be 256 or fewer colors. So, you may need to reduce the number of colors in your bitmaps if you want to display them transparently.

Related Topics

Image Support explains the enhancements that have been made to support images.

Embedded Resources explains what resources are, and their advantages.

The Application Properties dialog in the Studio is used to maintain the list of resources that should be linked into your programs.


Language Changes

  • Object Referencing now uses simpler and more consistent syntax.

  • Object neighborhoods have been introduced to make accessing all objects within views easier.

  • Delegation - long-standing design limitations in delegation have been corrected:

  • Broadcast - Delegation_mode is not changed during Broadcast messages.

  • Self - when Self (current_object) is passed as a parameter, its value is not changed by delegation.

  • True/False in Application Code is now more consistent

  • Global/Desktop Methods have been simplified

  • No Longer Supported shows which language elements are no longer supported


Easy Object referencing

Object Referencing

Any object name used anywhere within a Visual DataFlex program may now be referenced without the (self) syntax. The object may be referenced just by its name.

In Visual DataFlex 8.2 and higher, an object is referenced as follows:

Get Message of oName to bVar // instead of (oName(self))

Move (Message(oName)) to bVar // instead of (message(oName(self))

Prior to Visual DataFlex 8.2, object names in expressions always required the oName(self) syntax. In Visual DataFlex 8.2 the (self) component is no longer required (though still supported). This builds upon changes made in VDF5 where we allowed simplified object name access outside expressions as shown in the following examples.

Send Message of oName

Get Message of oName to svar

Move oName to hoVar

We now support this inside expressions as well.

Move (Message(oName)) to to svar

Move (oName) to hoVar

This now makes the use of object-referencing names easier and more consistent throughout the language.

This change applies to object names used anywhere within an application

new: Move (Label(oMyObj)) to sVar

was: Move (Label(oMyObj(self))) to sVar

new: Move (value(oMyObj,0)) to sVar

was: Move (value(oMyObj(self),0)) to sVar

new: Set Server to oMyObj // setting property to an object name

was: Set Server to (oMyObj(self))

new: Move (label(oMyObj) + ":" + value(oMyObj,0)) to sVar

was: Move (label(oMyObj(self)) + ":" + value(oMyObj(self),0)) to sVar

new: Send MyMessage of oMyObj oSomeObj // passing object as a parameter

was: Send MyMessage of oMyObj (oSomeObj(self))

new: Move (Value(oMyObj,item_count(oMyObj)-1)) to sLastItem

was: Move (Value(oMyObj(self),item_count(oMyObj(self))-1)) to sLastItem

This only works with actual object names. All other GET expressions (e.g. functions and properties) require the traditional expression syntax. For example the following syntax will not work (and never has).

// these will not work

Set piMyProperty to (piMyProperty+1)

Set Value iItem to (Value(item)*2)

The names, piMyProperty and Value, are not object names; they are the names of properties and functions. When used in an expression you must still pass the object handle of the property of function. In the above case, the proper code is.

Set piMyProperty to (piMyProperty(self)+1)

Set Value iItem to (Value(self,item)*2)

If an object name has the same name as a variable, the name of the variable will be used before the object name. For example:

// a very bad program

Object oMy is an Form

Procedure Foo

Integer oMy

Move oMy to oMy // will move variable value to itself

Set Server to oMy // will set to the variable and not the object

End_procedure

End_object

This is obviously very badly written code. Do not assign variables the same name as objects. Properly written, this program will be:

// a very good program

Object oMy is an Form

Procedure Foo

Integer hoMy

Move oMy to hoMy

Set Server to hoMy

End_procedure

End_object

There will never be any conflict between object names and other function names because you are not allowed to assign the same name to an object and a function/property. If you attempt to do this, you get a compiler error. If you follow our suggested naming convention of inserting an "o" in front of all object names it will be very clear when you are using an object and when you are using something else.

Using the new simpler syntax means less complex expressions with fewer parenthesis. Your programs should be easier to write and much easier to read. When you use this along with the new neighborhood object addressing methods, your programs will be even easier to write.

All these changes are completely backwards compatible. Existing programs will run as before.


Object Neighborhoods

In Visual DataFlex 8.2 we introduced the concept of object neighborhoods. The purpose of this change is to make it easier for objects to communicate with each other. Objects within a neighborhood can talk to other objects within the neighborhood by the first name (their simple object name) without needing to worry about where those objects are located.

Object Neighborhoods make it easier to manage visual objects within views. In a view, visual controls are often nested inside containers for appearance purposes only. As you design an application you may decide that it looks better to place several objects within dbGroups and that it then looks better to place those groups within a dbContainder3d. The Studio makes it very easy to make these kinds of changes. However, every time you move these objects around and put them in different containers you change the way those objects are referenced from other objects within the view. This creates the need to generate complex object reference statements and it may require code change every time you move objects around. This results in code that looks like this:

Get SortOrder of (oSortOrder(oSortParamsGroup(oSetUpContainer(oTabPageReport(oTabDialog(self)))))) to iOrder

And, if you decide to move your oSortOrder object somewhere else in the view, you need to start all over.

With neighborhoods all objects in the view are registered with the view and all objects within the view are visible to all other objects within the view. The same object placed anywhere within the view is now coded as:

Get SortOrder of oSortOrder to iOrder

Views and modal panels are defined as public neighborhoods. You should be able to use object neighborhoods in your views immediately. Old style object syntax will work as it always did. You can start using the new neighborhood syntax in your views and it should just work. If you have duplicate object names within your view your program will still run – you will just have to resolve the object's location the way you always have (the hard way). And if, for some reason, you are having problems with neighborhoods, they can be disabled in the view by setting one property.

Object neighborhoods "flatten" the object structure of visual controls making it possible for all visible controls to see each other. As you will see, objects will still properly encapsulate (hide) non-visual and class-based objects that are clearly not meant to be public. This allows easy access to the objects you want easy access to while still writing properly encapsulated applications.

Expected Use of Neighborhoods

All views and modal dialogs are now defined as being public neighborhoods. All visual objects within this view and any non-visual object created directly within the view or any container will be members of the neighborhood. They can all be accessed from any other object by their short object name.

All views and modal dialogs contain a property named peNeighborhood, which is exposed in the Studio. If you wish to make your view private you can change the value of peNeighborhood from nhPublic to nhPrivate. Making a view private will make it act exactly as it did prior to Visual DataFlex 8.2. We do not expect you will ever want to change this.

The peNeighborhood property is supported in all objects. We only expose this property in the Studio for view and modal panel objects, as we do not expect you to ever need to change this anywhere else.

Some Examples

Object Neighborhoods are designed to be used with the new easy object access referencing. When used together programs become much easier to code and even easier to read. A few examples should make this point.

// OLD

Get select_state of (oCheckBox1(oGroup1(oContainer1(self))) to bChecked

Get Value of (oMyGrid(oGroup1(oContainer1(self))) 7 to sVal

Move (Value(oMyGrid(oGroup1(oContainer1(self))),7) to sVal

// NEW

Get select_state of oCheckBox1 to bChecked

Get Value of oMyGrid 7 to sVal

Move (Value(oMyGrid,7)) to sVal

// OLD

Procedure OnClick

Set Value of (oCalculate(oMathWizard(self)) ;

to ( Value(Customer_number(Address_Tp(CustTD(self))),0) + "*" + ;

value(Customer_number(Address_Tp(Customer_zip(self))),0) )

Send DoTheMath of oMathWizard

End_Procedure // OnClick

// NEW

Procedure OnClick

Set Value of oCalculate to (Value(Customer_number) + "*" + value(Customer_zip))

Send DoTheMath of oMathWizard

End_Procedure // OnClick

How Neighborhood Object Referencing Works

When an object is created it will try to register itself with a neighborhood. It does this by checking with its parent object by looking at the parent property peNeighborhood. That property will either define the parent as being a neighborhood object (nhPublic or nhPrivate) or it will tell the object to delegate and check with its parent (nhNo). This process will continue until a neighborhood object is encountered. Once found that neighborhood object will either be public (nhPublic) or private (nhPrivate). If the object is public, it will register this new object as a member of that neighborhood, allowing all other objects in the neighborhood to access it with its short name. If the object is private, the object is not registered. In either case, the search has ended and the newly created object is either part of a neighborhood or it is not.

This newly created object will have its own peNeighborhood property setting. This does not affect how it is registered but it does determine how its child objects will be registered when those objects are created. In other words, an object's parent determines its membership in a neighborhood.

Prior to Visual DataFlex 8.2 an object name is found using the following rules:

Send Message of oName

  1. First check to see if oName is a child object. If found, use that object

  2. Delegate and see if oName is a child of the parent object. If found use that object

  3. Continue step 2 until there are no more parent objects in which case issue an object not found error

With object neighborhoods this is now changed as follows (new steps are in italics):

  1. First check to see if oName is a child object. If found, use that object

  2. Check if the object is a public neighborhood. If so, mark as " neighborhood found" and see if the object is in the neighbor list. If found use that object

  3. Delegate and see if oName is a child of the parent object. If found use that object

  4. If a neighborhood has not yet been found, check if the object is a public or private neighborhood. If so, mark as "neighborhood found". If public, see if the object is in the neighbor list. If found use that object

  5. Continue step 3 until there are no more parent objects in which case issue an object not found error

These two processes and their order of search are the same except that, when encountered, a check will be made of a public neighborhood for the object name. Only one public neighborhood is ever checked. This means that any object may only belong to one neighborhood. (Supporting membership in multiple nested neighborhoods would only create confusion.)

Property peNeighborhood

As indicated, all neighborhood behavior is controlled by the peNeighborhood property. All objects support this property. Three modes are supported:

  • nhPrivate – If an object is private it means that all descendant objects (all objects within this object) do not support neighborhood referencing. You can think of these descendent objects as not being part of any neighborhood or you can think of them as being part of a private neighborhood where they just don't like to talk to each other. This mode makes objects behave exactly the way they did prior to 8.2. Normal object access methods are still used (check if name exists as a child, if not delegate to parent and repeat search).

    The actual object marked as nhPrivate may be part of a neighborhood, but it descendents are not. An object's neighborhood status is determined by its parent.

    Most objects in Visual DataFlex are private. All visual controls (e.g. form, dbForm) and all non-visual objects (e.g. Array, XML objects) are private. In addition, objects created as part of class construction are usually private.

  • nhPublic – If an object is public it maintains a list of all of its descendents. When an object name is requested of this object either directly or via object access name delegation it will search its list to see if the object exists. Views and Modal panels are defined as being public. Using this value defines a neighborhood.

  • nhNo – This mode indicates that this object does not define a neighborhood either public or private. Instead it allows its descendents (all objects within this object) to ignore this object and check with its parent to see what its neighborhood status might be. This is the mode that is used by visual containers like groups and container3d. It is the mode that flattens the object structure for visual containers.

We don't expect you will ever have to change this property, with perhaps the one exception of changing a view or modal panel from public to private. Even in this case we would encourage you research the need for this change and to see if you can change your application to fully support neighborhoods

Duplicate Object Names and Neighborhoods

Duplicate object name are allowed within a neighborhood, but only one of those names will be found when the neighborhood list is searched. If you are planning on resolving an object name via a neighborhood you should avoid using duplicate names. Typically this will not be a problem. When duplicate names are created within a view, the program rarely needs to find those objects by name – otherwise you would have assigned the object a unique name. Either you are never sending messages to these objects or you are creating them dynamically in which case you should be accessing them by their object handle ID and not their name.

Also note that a number of classes create objects as part of class construction. You would rarely want to make these objects public, and in fact they will not be made public. If the object is private these child objects remain private - this encompasses most objects. Only visual containers are non-private and even in these cases they actually are marked private until the end of construct_object. In this case all the children defined during construction (those objects defined within the class) remain private.

dbTabDialogViews and Object Neighborhoods

In dbTabDialogViews, each dbTabView should be thought of and used like a separate dbView. Each dbTabView may contain object names that are duplicated from other dbTabViews in the same dbTabDialogView. Object neighborhoods and short object referencing inside each dbTabView will work as it would in a dbView. To access objects in other dbTabViews, the complete access method should be used instead of the short object reference.


Improvement in Broadcast Behavior

Broadcast Command

When an object receives a message via a broadcast, the message is supposed to be resolved or ignored. We implemented this behavior by: a) temporarily setting the object's delegation_mode to no_delegate_no_error, b) sending the message and c) restoring the delegation_mode. While this did prevent broadcasted messages from delegating messages it did not understand, it had the unfortunate side effect of changing the object's delegation mode for all other child messages sent to the object as part of the message's normal processing.

Property Integer piSomeProperty 100

Object oObj1 is a form

Procedure Foo

Get piSomeProperty to iVar // since this needs to delegate we will have problems

Send SomeOtherMessage iVar // if this needs to delegate we will have problems

End_procedure

End_Object

Object oObj2 is a form

Procedure Foo

Get piSomeProperty to iVar // Since this needs to delegate we will have problems

Send SomeOtherMessage iVar // if this needs to delegate we will have problems

End_procedure

End_Object

Broadcast Send Foo

For example, assume the message Foo is broadcast to these objects. In both objects the piSomeProperty is supposed to be resolved via delegation. Since delegation_mode will be set to not delegate and not report errors the message will be ignored, therefore the variable value will be wrong and your faulty program will run as if nothing is wrong.

This has been the standard broadcast behavior since DataFlex 3.0 was released and many developers have mentioned over the years that this behavior is not what they expect. In particular it is extremely difficult to debug (when you expect a message to delegate but instead get no delegation and no error - there is no way to know you have a problem).

This has now been changed so that the object delegation_mode is never modified during a broadcast. Instead delegation is temporarily turned off only at the broadcast call level. This means that the broadcasted message will still ignore a non-understood message (which is good) but all messages sent within a broadcast call will delegate as needed (which is also good). Any messages sent during delegation will still delegate according to the original delegation_mode of the object.

We do not expect that this will negatively affect any existing programs; as the old behavior was not something that you would ever want to take specific advantage of. The only possible effect might occur if your program is currently not working properly, and somehow your program bug was neutralized by the old broadcast behavior – this is very unlikely.


self_parameter

Self (as parameter)

The method used for passing parameters containing the current-object (the self or current_object variable) has been changed. In all prior revisions of Visual DataFlex the value of self might change if the message is delegated. This has been changed so that:

The value of "self" passed as any parameter in a message is always the value of the original object that sent the message.

Consider the following structure.

Object oA is a cClassA

Object oB is a cClassB

Object oC is a cClassC

Object oX is a cClassD

Send Foo of oC self

The message Foo is sent from oX passing the variable self to object oC.

Prior to Visual DataFlex 8.2:

If the message is resolved by oC self = oX

If the message is resolved via delegation by oB self = oC

If the message is resolved via delegation by oA self = oB

In Visual DataFlex 8.2 and higher, the value of self will always be oX, the original object that sent the message.

To avoid this behavior we always advised developers to not pass self as a single parameter or within an expression if you were not certain that the object receiving the message could resolve it. This created code that looked like this:

// work around required prior to 8.2

Procedure DoSomething

Handle hoSelf

Move self to hoSelf // move self to a local variable before passing it in a message

Send MyMessage to oSomeObject (value(hoSelf,0))

End_procedure

This type of workaround is no longer required and you can now code this as follows

Procedure DoSomething

Send MyMessage to oSomeObject (value(Self,0))

End_procedure

We do not expect that this will negatively affect any existing programs; as the old behavior was not something that you would ever want to take specific advantage of. The only possibly effect might occur if your program is currently not working properly, and somehow your program bug was neutralized by the old behavior – this is very unlikely.


Using True / False in Application Code

True / False Constants

In VDF8.2, the symbols True and False can be used consistently everywhere in an application. The special symbols DFTrue and DFFalse are now considered obsolete and should no longer be used.

Previous revisions of Visual DataFlex placed restriction on the use of the keywords True and False. These symbols could be used in expressions and they could be in messages but they could not be used in commands. To overcome this the additional symbols DFTrue and DFFalse were created that worked properly in commands. For example:

// prior to 8.2

Move True to bVar // compiler error. You are supposed to use DFTrue

Move (True) to bVar // yet this would compile

Send MyMsg True // would compile

set_Attribute DF_FILE_IS_SYSTEM_FILE of hFile to True // compiler error. You are supposed to use DFTrue

Set Property to True // yet this would compile

This has created considerable confusion over the years. It was not clear when these symbols should be used and it was not clear if DFTrue and DFFalse were actually different than True and False (they were not; True=DFTrue=1, False=DFFalse=0).

As of 8.2, all of the above code will compile properly. True and False can and should be used. Anytime you see a reference to DFTrue or DFFalse you can convert that to True and False.


Desktop Methods

Global / Desktop Methods

Functions and Procedures defined at the Desktop

Visual DataFlex has always supported the placement of methods on the desktop. Even among our most experienced developers the meaning of a "desktop method" has always been a source of great confusion. In Visual DataFlex 8.3, we changed the syntax of desktop methods to make their use and purpose clearer. The older syntaxes for supporting desktop methods are still supported, but they will be phased out in future Visual DataFlex versions.

Visual DataFlex supports two suggested methods for handling methods on the desktop. They should either be marked DESKTOP or GLOBAL.

Global Functions and Procedures

Function MyGlobalFoo Global integer i returns string

The "Global" syntax remains unchanged. If a function or procedure is marked as being Global it is actually not a method at all. It is a function or procedure, which is accessed outside of any object considerations. A global function/procedure can be defined anywhere in an application and it can be defined one time only. Once a name is assigned to a global function or procedure that name can not be used for any other class and object methods. In other words object methods and global functions/procedures can not have the same name and will result in a compiler error.

We recommend that global functions and procedures be used sparingly. In an object-based system, you should use methods. This information, syntax and advice is not new.

Desktop Methods

Function MyDesktopFoo Desktop Integer i returns string

If the word "Desktop" immediately follows the method name, then this method becomes a method of the desktop object. The desktop object is the container object for all other objects in Visual DataFlex. Desktop methods can be resolved either by sending the method directly to the Desktop or by delegation (assuming the objects within the desktop object all support delegation and the method is not understood by some other object).

// the method is resolved by sending directly to the desktop

get MyDesktopFoo of desktop iVal to sString

// the method will be solved by delegating to the desktop

get MyDesktopFoo iVal to sString

Note the Desktop syntax is new. Also note that this syntax is different than using the old "for Desktop", a syntax which is highly discouraged.

Like the Global keyword, the Desktop keyword can be used anywhere to mark a method as a desktop method. Desktop methods are object-based methods. Therefore, you can use this name for a desktop method and use the same name for any other object or class you wish.

We also recommend that desktop methods be used sparingly. If your application contains many desktop methods or many global functions/procedures you may wish to review your program structure. You may be creating code that is not properly encapsulated.

Ambiguous Desktop Methods in Existing Applications

We suggest that all methods that are coded at the desktop level (i.e. methods not contained within any other class or object) be explicitly marked as being GLOBAL or DESKTOP. All other syntaxes are considered either obsolete or not recommended. In particular, previous versions of Visual DataFlex allowed methods placed on the desktop to contain no special marking. We refer to this as an "ambiguous" desktop method:

// if this were on the desktop, it would be considered Ambiguous

Function MyDesktopFoo Integer i returns string

We now discourage the use of ambiguous desktop methods. In 8.3, all old syntaxes were supported including ambiguous desktop methods. They worked exactly as they did in all prior versions of Visual DataFlex. However, using ambiguous desktop methods is now strongly discouraged. Beginning with Visual DataFlex 9, ambiguous desktop methods generate error messages. Therefore, we advise that you start changing your programs now.

A compiler command, "Compiler_Desktop_Method_Warnings On|Off" will allow you to control warnings and will default to off. The default is On and you will either have to adjust your code (strongly recommended) or explicitly relax the compiler by setting this off. This can be turned on and off in sections of code, to allow an orderly migration.

Ambiguous methods on the desktop are ambiguous. No matter how hard everyone tries to understand this, there is still far too much confusion. Some developers think they are (or should be) global methods, some developers think they are (or should be) desktop object methods and some developers who think they are for cObject (and should not be). By forcing developers to actually explicitly define desktop methods as being Global or Desktop, program intent will become clear.

Upgrading Existing Applications

All of the changes remain 100% compatible with existing code. You should plan on upgrading your code. The following rules should make this upgrade simple.

  1. Search for all occurrences of "for DFDesktop" and change to "Desktop"

e.g. Procedure MyFoo for DFDesktop integer iInt ---> Procedure MyFoo Desktop Integer iInt

Using "for DFDesktop" was the old way to define methods for the desktop. You now want to use the newer syntax.

  2. Search for all occurrences of "for Desktop" and "for BaseClass" and change them

If you want these methods to behave exactly as they did prior to VDF8.3 you will want to change all of these to "for cObject". cObject is the new base class that all Visual DataFlex classes derive from. While cObject will give you the exact same behavior, this may not have been your intention. By using cObject you are adding this method to the base class which means that all classes and all objects directly understand this method. We generally advise against the "for ClassName” technique and we are strongly against using this for the ultimate base class, cObject. By doing this you are changing the base behavior of all objects - a technique that can easily create unwanted side-effects. So before changing this to cObject, you may wish to verify that this is really what you want to do. Often, defining this as a desktop method (e.g. change "for Desktop/BaseClass" to "desktop" will yield the desired result.

  3. Turn on compiler warnings for ambiguous desktop methods and make changes as needed.

Add the following command to the top of you program and compile:

Compiler_Desktop_Method_Warnings On

This will cause the compiler to report all ambiguous desktop methods. The Studio will present a list of all of these errors. You can now review each one of these and make them non-ambiguous.

You will either be changing these to "DESKTOP" or "For cObject". Technically an ambiguous method on the desktop is "for cObject". However, this is very often not the intent of the programmer. The programmer probably wanted this to be a method for the desktop. If this was your intention change this to "DESKTOP". If your intention was to create a method that was directly understood by all objects in your application, change this to "for cObject". While using "for cObject" will make your application behave exactly as it did before this may not have been your intention and this is not a recommended technique.

Desktop methods in general should be avoided. When used as "for cObject" it should be highly avoided. When used as "Global" it should be avoided. And even the use "for cDesktop" should be minimized. Bottom line is, if we allow methods on the desktop to remain ambiguous it will cause problems. When reviewing someone else's code you will never be sure what their true intent was. And, even when fully understood they are going to be a source of programming error. They should be avoided.

We advise you to review your code and change all of your ambiguous desktop methods to be explicitly "for cDesktop", "for cObject" or Global. We also encourage you to think about moving these methods somewhere else (maybe inside you panel) and we 1) strongly discourage the use of for cObject, and 2) discourage the overuse of "for cDesktop" just like we discourage the over use of Global. If you follow these recommendations all ambiguity disappears.


No Longer Supported

Obsolete .OBJ Syntax No Longer Supported

A significant change has been made in object creation in 8.3 that improves the efficiency of object creation and destruction. Because of this change, an obsolete method of accessing objects has been removed. This is the ObjName.obj method of accessing global objects. Prior to 8.3 the creation of an object on the desktop resulted in the compiler creating an object access method in the format of the symbol "objName.obj". This symbol is no longer created. If you are using any of these symbols in your program you will receive a compiler error and you must change your application as follows:

From: Send Xxxxx to MyObject.obj // old, obsolete access method

To: Send Xxxxx to (MyObject(desktop)) // suggested access method

Basically change "objName.obj" to "(objName(desktop))".

The .obj method has not been recommended since the very earliest days of DataFlex (well before Visual DataFlex was released) so hopefully you will have few instances of this code.

Not all .obj methods will require changing. If you are using name_object (also an obsolete technique) the .obj method will continue to work as it always did. The best way to find the places you need to change is to compile your application. All errors will be reported.

Technical description

Prior to 8.3, DataFlex used rather complicated dependent lists to keep track of objects. Each object, including the desktop, had a dependent list of child objects. This method had drawbacks. When used with programs that created and destroyed many objects dynamically you could get small memory leaks and, in some cases, noticeable significant performance problems.

The desktop object's list of child objects allowed the compiler to create special access methods for desktop objects - this is the .obj access syntax (e.g. send Foo of oName.obj). This compiler symbol was assigned a dependent list number (a very large negative number). When the runtime saw a very large negative number it would replace the dependent number with a real object id. Now that we do not create a dependent list, this method no longer works. Hence, we have changed fmac so that it no longer creates this .obj symbol. This means you must use the recommended and much clearer method of accessing global objects of "(oName(desktop))".

The .obj name method is also used when you define an object name with Name_object. This type of usage remains unchanged. If you've defined a desktop object or any object with the name_object command, your program will work as it always did. However, note that the use of name_object is strongly discouraged and has been for many years.


Variant Datatype

A new native Variant data type has been added. The Variant type is capable of storing values that change type dynamically. Variables of any other type (Integer, String, Date, etc) are statically bound to that type but a variable of the Variant type can assume values of differing types at runtime.

You would use a Variant type in situations where the actual type of your data is unknown at compile-time. This is a common situation when writing applications that use COM objects.


Passing Arguments "By Reference"

Previously, you could only pass arguments "by value", meaning that a copy of a variable was sent to a function and the function was incapable of modifying the original variable. You can now pass variables "by reference". When a parameter is passed"by reference, the called procedure or function has direct read/write access to the passed variable. Changes made to the reference parameter inside the method are reflected in the passed variable outside the method.

This technique is often applied when there’s a need for multiple return values, or for in-out parameters which pass one value to the called method and are allowed to be modified and contain a different value when the execution returns to the caller. The latter is commonly used in COM event methods, as a means of allowing the client code to cancel or modify a certain action or default behavior. In which case the COM object might pass on a Boolean parameter bCancel, which the program may set to True in order to cancel the pending action.


Navigation Changes

The Visual DataFlex keyboard navigation logic has been overhauled. A number of the inconsistencies that occurred when moving from object to object with a keyboard have been analyzed, restructured, and improved. In some cases this involved fixing bugs. In other cases the logic had to be changed by adding and changing interfaces. These changes should not require any changes in your applications – keyboard navigation in you applications should just start working better.

The major changes you will see are:

  • Tab Dialog keyboard navigation is vastly improved. The various navigation mode properties (rotate mode, skip_button_mode) now work as expected making it easier to navigate in to a tab page, out of a tab page and between tab pages. New messages and properties have been added to allow more control over this process.
  • Beginning and End of Panel logic has been changed. Previously this logic would attempt to give the focus to the first (or last) DEO object skipping focusable non-DEO objects. Now the first (or last) focusable object gets the focus.
  • Forward and backwards keyboard navigation is more consistent. Prior to 8.2, keyboard navigation would be inconsistent when nested containers were encountered and when disabled objects and groups of objects were encountered. We now use better logic to handle these conditions.

To make this all work we have made changes in the navigation program interface. These changes are described below. Most of the message changes were made in low-level messages that you are not likely to be calling or augmenting.

Next_object_id / Prior_object_id

The messages next_object_id and prior_object_id are used to determine what the next or prior object would be for keyboard navigation. They are called by the next and previous messages to change focus. You should also be able to call them to figure out where the focus should be. If a value is returned, that's the object to move to. If zero is returned, no focus change should take place.

Prior to VDF8.2, a return of zero, sometimes meant "no focus change" and it sometimes meant, "a focus change has taken place". This was a bad design. You would send a message to get the next object and instead of getting an object, a focus event might take place. This occurred because these messages sent child_wrapping and our packages used this to occasionally perform focus changes. Child_wrapping would change the focus and return 1, which caused the next_object_id and prior_object_id to return 0. You had no way to determine what that return of 0 actually meant.

In 8.2 we no longer use child_wrapping for this purpose (actually we do not use it at all). Instead we use a new method named OnChildWrapping. OnChildWrapping never changes the focus; it returns an object handle that indicates where the focus should go. The next_object_id / prior_object_id messages use this so it can determine where the next focus will be.

This means that next_object_id / prior_object_id will never change the focus any more. They return a value that indicates where the focus ought to go. If 0 is returned, a focus change is not possible. Therefore you can always safely call these methods without worrying that the focus might get changed without you knowing it.

Additional changes were made to let next_object_id / prior_object_id do a better job. If, while looking for the next object, you get in a ring you cannot get out of (usually all items in ring are disabled), it will send the new function StuckInRing to the ring owner. This method will either jump out of the ring or return a 0, stopping navigation.

IMPORTANT NOTE: Child_wrapping is now obsolete. It still works but you should use the new message OnChildWrapping.

OnChildWrapping

Type: Event/Function

Arguments: handle hoDestination boolean bDown returns handle

This message is sent any time a wrapping event occurs. A wrapping event occurs when navigation would "ring" either from last object to first object or first object to last object. When this occurs the OnChildWrapping event is sent to the owner of the ring (the container object that has ring_state set to true) passing the destination object and a boolean indicating if the ring is moving from last to first (bDown=True) or last to first (false).

The function must return the object handle ID of the object to navigate to. Often this will be hoDestination, the object that was passed. If zero is returned, the navigation event should not take place. By default the runtime returns the passed object. In flex code it would look like this:

By default, this changes nothing. If you return a 0, navigation is stopped and no change takes place. If you return any other value navigation will continue from that object. Tab-dialogs use this extensively to handle all of the custom navigations required.

You should never send this message yourself. It may be augmented. If do you augment this function, never use it to change the focus.

This event is called whenever the old message child_wrapping is sent but it is more flexible.

Changes in Beginning_of_Panel / End_of_Panel

Prior to VDF8.2 the standard behavior for moving to the beginning or end of a panel was to go to the first or last focusable DEO object. So, if your first object in a view was a button or a form, it would get skipped and focus would move to the first DEO object, like a dbForm. This created much confusion, and has been changed. Now beginning_of_panel and end_of_panel move to the first or last focusable object.

In addition, the beginning_of_panel message will send a new event message, OnBeginningOfPanel to all objects inside the panel. This notification can be used to perform custom beginning of panel initializations in objects. This is currently used by dbGrids and dbLists to make sure the first column of a grid is visible. It is used by tab-dialogs to make sure that a default tab page is brought forward.

New UI_Object Messages

The following messages are added to the UI_Object class, which is the ancestor of all user interface objects.

OnBeginningOfPanel

Type: Event

When a beginning_of_panel message is executed the panel will notify all its objects by sending the message OnBeginningOfPanel. By default this does nothing. DbGrids, dbLists and Tab Dialogs use this message to perform special changes.

You can augment this method to perform any desired initialization required by a beginning of panel event.

ContainsFocus

Type: Function

Arguments: returns Boolean

This function returns true if the object or one of its descendents contains the focus.

This method is used by Visual DataFlex to control navigation logic and will probably not be used by developers. If used this would most often be sent to a container object to determine if the container contains the focus.

RingParent

Type: Function

Arguments: returns Handle

This function returns the object handle of the object that is its ring parent – this will be a parent or some ancestor whose ring_state is set to true. If 0 is returned, there is no ring parent.

This method is used by Visual DataFlex to control navigation logic and will probably not be used by developers.


Registry Changes

Our use of the Registry in programs has been completely redesigned to eliminate access problems associated with using HKEY_LOCAL_MACHINE at deployment sites where the Administrator wouldn't grant access to the necessary Keys:

  • Deployed environment changes to HKEY_CURRENT_USER (program size/location, etc)—including ability to disable writing/reading of registry settings in the program.

  • "Current Workspace" held in HEY_CURRENT_USER, so is now per user

  • New cRegistry class that encapsulates registry usage, deprecates collection of commands/global functions, provides more complete control of access (read-only, etc), and enables enumeration of Keys/Values.


Resources

See Embedded Resources for an explanation of what resources are.

See Using Embedded Resources to understand how to use resources.


Embedded Resources

You can embed native Windows resources into your compiled programs.

Embedding resources is the process of physically including the information in your compiled programs, so that it does not require to be installed separately.

Types of resource that can be embedded into your program
Resource Type | Description
Bitmaps | BMP and RLE bitmap images
Icons | ICO icon files
Version Information | Version number, and text describing your program

Embedding resources provides many benefits, including:

Speed—resources can be loaded faster than they could be from disk

Deployment—by embedding the resources, you will simplify the installation of your applications by deploying fewer files and thereby eliminating the problem of forgetting to deploy all your bitmaps.

Transparency—bitmaps that are loaded from resources can be automatically transformed to have a transparent appearance

Your compiled EXE program can show an icon resource in Windows Explorer and also when the user Alt-Tabs through Windows tasks.

To add resources to your program, use the Application Properties dialog on the Studio. Read about Using Embedded Resources.

Bitmap Resources

Bitmap resources are bitmap files (*.bmp) that are embedded within the EXE. Once a bitmap file is embedded, you will not need to deploy it. Your program will automatically use bitmap resources in preference to bitmap files.

Icon Resources

Icon resources are bitmap files (*.ico) that are embedded within the EXE. Once an icon file is embedded, you will not need to deploy it. Your program will automatically use icon resources in preference to icon files

Version Information

Version information allows you to add revision numbers and descriptive text (such as company name and copyright information) to your program. This information can be viewed by displaying your program’s Properties dialog in Windows Explorer

Limitations

A chained-to program cannot use bitmap resources, even if it has some embedded that it uses when being run stand-alone. This is because technically, from a Windows perspective, a chained-to program is the same program as the one being chained from (that is, Windows will return the same value for the API function, GetModuleHandle). This is one good reason why you should try to use RunProgram in preference to Chain. If you start a second program with RunProgram, rather than Chain, you can use the bitmap resources in it. One possible solution to this limitation is not to embed the resources for the child process in the child program, but in the master program.

Related Topics

Using Embedded Resources explains how to use embedded resources.

Image Support explains the enhancements that have been made to support images.


Using Embedded Resources

Adding Resources to your Program

Embedding Resources into your programs involves two steps:

  • Use the Application Properties dialog in the Studio to itemize which resources you want

  • Compile your program

When the compiler has successfully compiled your program, it executes a program called a linker that creates the EXE and embeds the resources into it. This process is known as linking. The linker reads the configuration file that the Studio created.

Using Resources In Programs

The use of Bitmap and Icon Resources in your programs is mostly automatic, as many properties have been modified to automatically use resources, if available. For example, if you had a line of code, Set Bitmap to “World.bmp”, DataFlex will automatically look in the embedded resources for a bitmap called World.bmp, and use it. If it doesn’t find one there, it will look on the disk, as it did previously. Other enhancements have been made to images to support transparency.

You can read the version numbers from your program at runtime. To see an example of this, read about the cVersionInfo class.

Related Topics

Embedded Resources explains what resources are and their advantages.

Enhancements to Image Support explains the enhancements that have been made to support images.


Status Panel Changes

The old external sentinel based status-panel has been replaced by a new internal panel. This panel should be faster and should be much easier to customize. In most cases, the new panel should serve as no change required replacement for the old one. The old status panel is still available, it's just been renamed.

See Status Panel Changes in the What's New book for more detailed information.


Studio Meta-Data Tags

As of Visual DataFlex 12.0, Visual DataFlex source code may meta-data tags, which are used by the Studio to obtain information about your source code. When migrating code from older revisions, the Workspace Migration Wizard automatically adds any required or desired meta-data tags to your code.

For designing Library Workspaces, you may want to learn more about Studio Meta-Data.


Web Application Changes

Visual DataFlex support for web applications is largely unchanged, but the Studio’s handling of web applications has changed.

See Web Applications in Visual DataFlex 12 in the What's New book for more detailed information.


Workspace Changes

With the new 12.0 release we have made a number of workspace changes:

  • Introduction of a new Studio Workspace (SWS) file.

  • Studio workspace related settings (like subclasses) are moved from the registry to the SWS file.

  • Central list of registered workspaces (workspaces.ini) is no longer used.

See Summary of Workspace Changes in the What's New book for more detailed information.


Compatibility and Upgrade Guide

  • Upgrading from Prior Revisions of Visual DataFlex

  • Improvements that May Impact Compatibility