Creating a Reuseable Calendar Control using SmartDataFields

Chris Kelleher

Administrator
Staff member
A SmartDataField is an ADM field-class SmartObject that displays a
single data field. It is intended for inclusion only in a
SmartDataViewer. You typically use a SmartDataField to provide a
specially formatted display of the data in a selected field in the
SmartDataViewer. To do this, you replace the field on the
SmartDataViewer with your SmartDataField. For example, you might
develop a SmartDataField that appears as a calendar and use it in
a SmartDataViewer to display a date field.

Because there is no standard representation for a SmartDataField,
it does not have a Wizard and opens initially as a frame. You, as
the application developer, determine how to represent your
SmartDataField; for example, as a pick list or an ActiveX Control.
You can create any user interface as long as it does the following:

* Retrieves the containing SmartDataViewer's DataValue property
value for the associated field and assigns it to the
SmartDataField's visualization of the field

* Accepts a modified value from the visualization and assigns it to
the DataValue property for the associated field

Here are the general steps for creating a SmartDataField, which you
perform in the AppBuilder:

1. Define the visualization of the SmartDataField; for example, by
dropping an ActiveX Control onto it.

2. Write code to set the DataModified property; for example, when
the field's value is changed in an AfterUpdate ActiveX event
trigger.

3. Implement the setDataValue and getDataValue functions (the
template includes stubs for these functions):

* The setDataValue function, which is run from displayFields in
the SmartDataViewer, sets the value of the field's visualization
by passing in the SmartDataObject field value to be assigned to
the visualization.

* The getDataValue function returns the current value of the
field's visualization to the caller (typically collectChanges
in the SmartDataViewer).

4. Implement the disableField and enableField procedures to enable
and disable the visualization (the template includes stubs for
these procedures). There are also DisplayField and EnableField
properties:

* The displayField property specifies whether the data value for
the field is assigned to the SmartDataField automatically.

* The enableField property specifies whether the SmartDataField
is updateable.

By default, these properties are set based on the corresponding
properties for the original SmartDataObject field, but they can
be set in the instance property dialog box.

You add SmartDataFields to a SmartDataViewer after you pick all of
the fields that will appear in the viewer (using the Wizard). To
add a SmartDataField, select it from the AppBuilder palette and drop
it onto the SmartDataObject field that it is to replace. As when you
add SmartObjects to other SmartContainers, the AppBuilder then
generates an adm-create-objects procedure to start the
SmartDataField, including a FieldName property whose value is the
name of the original SmartDataObject field.

So, how can you create a SmartDataField containing an Office-style
ActiveX Calendar control? Just follow these steps:

(There is a link to the finished code for this object at the end)

First, from the AppBuilder, create a new SmartDataField.

Then drop a Calendar OCX onto the design window. I will use
ActiveCalendar for this example. If you don't have this control,
you can download a free evaluation version from http://www.TheBusterGroup.com

I needed to add a variable to track when the date in the OCX
control was changed by the user. This was because the "Change"
event for this control was fired when the Calendar is first
displayed, even though this really wasn't a change from the user.

Add the follow code to your definations block:

<BLOCKQUOTE><font size="1" face="Arial, Verdana">code:</font><HR><pre>
DEFINE VARIABLE lSet AS LOGICAL NO-UNDO.
[/code]

I then added procedures for two of the control's events, when
it is first displayed, and another when it was changed. When
the date value was changed by the user, we need to call the
setDataModified dynamic-function to let the SmartDataViewer
know the field was modified.

Here are the procedures to add:

<BLOCKQUOTE><font size="1" face="Arial, Verdana">code:</font><HR><pre>
PROCEDURE Calendar.ActiveCalendar.Change:
DEFINE INPUT-OUTPUT PARAMETER p-newDate AS CHARACTER NO-UNDO.
IF NOT(lSet) THEN
DYNAMIC-FUNCTION('setDataModified':U,TRUE).
ELSE
ASSIGN
lSet = FALSE.
END PROCEDURE.

PROCEDURE Calendar.ActiveCalendar.DisplayCalendar:
ASSIGN
lSet = FALSE.
END PROCEDURE.
[/code]

The template already contains the disableField and enableField
procedures, which don't need to be modified:

<BLOCKQUOTE><font size="1" face="Arial, Verdana">code:</font><HR><pre>
PROCEDURE disableField:
{set FieldEnabled FALSE}.
END PROCEDURE.

PROCEDURE enableField:
{set FieldEnabled TRUE}.
END PROCEDURE.
[/code]

Finially I added two functions, getDataValue and setDataValue.
These are used to communicate the field's value to and from your
SmartViewer.

The getDataValue retrieves the date value from the control (which
is returned as a character string). I then format the string
properly in 99/99/9999 format to return to my actual date field:

<BLOCKQUOTE><font size="1" face="Arial, Verdana">code:</font><HR><pre>
FUNCTION getDataValue RETURNS CHARACTER
( ) :
DEFINE VARIABLE cDate AS CHARACTER NO-UNDO.
DEFINE VARIABLE cMonthName AS CHARACTER NO-UNDO
INITIAL "January,February,March,April,May,June,July,August,~
September,October,November,December".
DEFINE VARIABLE iMonth AS INTEGER NO-UNDO.

ASSIGN
cDate = chCalendar:ActiveCalendar:acDate.

REPEAT iMonth = 1 TO 12:
cDate = REPLACE(cDate,ENTRY(iMonth,cMonthName),STRING(iMonth)).
END.

ASSIGN
cDate = REPLACE(cDate," ","/")
cDate = REPLACE(cDate,",","").

RETURN cDate.
END FUNCTION.
[/code]

The setDataValue has a character input parameter which is then
simply assigned to the control for its display:

<BLOCKQUOTE><font size="1" face="Arial, Verdana">code:</font><HR><pre>
FUNCTION setDataValue RETURNS LOGICAL
( pcComments AS CHARACTER ) :
ASSIGN
chCalendar:ActiveCalendar:acDate = pcComments
lSet = TRUE.
RETURN TRUE.
END FUNCTION.
[/code]

And that's it! You can now drop this new SmartObject on any
date field in your SmartViewers. You have just one calendar
object to maintain, but can use it on all your date fields in
your application for a nice consistent look and feel.


------------------
Chris Schreiber
ProgressTalk.com Manager
chris@fast4gl.com
 
Top