This chapter describes how to programmatically work with forms and dialogs. It covers:
Specifying Constraints
Displaying a Form
Initializing a Form
Setting a Form's Event Handler
Laying Out a Form or Dialog
Opening a Form
Drawing or Updating a Form
Closing a Form
Displaying Dialogs
Working With UI Elements
A form is a container for your application's user interface (see Figure 2.1). An application must have at least one form, and most applications have more than one.

A dialog or modal dialog is a special type of form (see Figure 2.2). Dialogs look different from forms in that they are often shorter and use a different title bar; however, the main difference between a dialog and a form is in the way it handles events.

In either case, the main purposes of a form or dialog are to:
Specifying Constraints
As mentioned in "Display Layout", when you create a form resource, you should also specify a its constraints. Palm OS® uses the constraints to determine how to create and size windows for your form. Because the way you write code for a form depends significantly on how you set its constraints, this section provides guidelines for setting the constraints resource. The rest of this chapter focuses on how to write code to work with forms.
See your resource editor's documentation for specific information on how to set the constraints resource and all other resources.
The resource file that defines your form should contain a WINDOW_CONSTRAINTS_RESOURCE
with the same ID as the form or dialog. See Listing 2.1.
Listing 2.1 Sample constraints resource
<WINDOW_CONSTRAINTS_RESOURCE RESOURCE_ID="1800"> <VERSION> 1 </VERSION> <WINDOW_CREATE_FLAGS> 0x00000004 </WINDOW_CREATE_FLAGS> <WINDOW_X_POS> 0 </WINDOW_X_POS> <WINDOW_Y_POS> 0 </WINDOW_Y_POS> <WINDOW_WIDTH_MIN> 160 </WINDOW_WIDTH_MIN> <WINDOW_WIDTH_MAX> 0x7fff </WINDOW_WIDTH_MAX> <WINDOW_WIDTH_PREF> 160 </WINDOW_WIDTH_PREF> <WINDOW_HEIGHT_MIN> 160 </WINDOW_HEIGHT_MIN> <WINDOW_HEIGHT_MAX> 0x7fff </WINDOW_HEIGHT_MAX> <WINDOW_HEIGHT_PREF> 240 </WINDOW_HEIGHT_PREF> </WINDOW_CONSTRAINTS_RESOURCE>
The constraints resource contains two types of information: the window type (which includes the layer) and the size constraints.
Window Type
The WINDOW_CREATE_FLAGS
attribute is a bit mask that contains the window type and the window layer. The WinFlagsType
enum specifies the values that this attribute can have. In most cases, you'll only need to set one flag:
You don't need to set the modal flag in a constraints resource. The window's modal flag is set according to the form resource's modal flag. You set the window layer only very rarely. All application windows appear in the normal layer, which is the default layer. If you're creating a resource for a pinlet, the window layer in the WINDOW_CREATE_FLAGS
attribute is ignored. The system automatically displays the pinlet's window in the system layer.
The back-buffer flag controls which type of window is created for the form. The window type is important because it controls whether the form follows the Palm OS Cobalt rules for drawing or the legacy rules. In Palm OS Cobalt, drawing is not done directly to the screen as it was in previous releases. Instead, you draw to a graphics context, which is described in more detail in Chapter 8, "Drawing." The window type determines when this graphics context is available.
Palm OS Cobalt supports three window types:
- Legacy windows. Legacy windows are used for forms that do not have a constraints resource. They work just like windows on previous Palm OS releases. Because they do not have constraints, they do not resize or move, and users cannot open or close the dynamic input area when a legacy window is displayed.
Legacy windows only exist if an application's resources aren't changed when it is ported to Palm OS Cobalt. Because the dynamic input area is disabled when legacy windows are displayed, PalmSource does not recommend that you leave your application in this state.
- Update-based windows. Update-based windows are created for forms that use the default window creation flags (specifically, that don't set the back-buffer bit). Update-based windows follow the Palm OS Cobalt drawing rules.
For update-based windows, the graphics context is only available at certain times. The system notifies you that the graphics context is available by sending the
frmUpdateEvent
. Therefore, if you use update-based windows, you must only draw in response to this event.
IMPORTANT: A pinlet or any form displayed outside of the application process must use an update-based window.
- Transitional windows. Transitional windows are created if you set the back-buffer bit in
WINDOW_CREATE_FLAGS
. The graphics context is created once and is then stored in the back buffer shared between the application process and the system process. (The system process is where rendering is performed). For a transitional window, you must wait to draw until you receive the firstwinResizedEvent
, which specifies the size of the form. After that, you may draw at any time.
You'll probably want to use transitional windows for ported applications, at least until you've discovered all of the places where your application draws to the screen.
For new applications, you may choose to use either transitional windows or update-based windows. Transitional windows are more efficient when rendering is done entirely in software, but update-based windows are more efficient on devices that use graphics accelerators. Note that the back buffer is allocated once and shared by all applications, so there is no size penalty for using transitional windows.
Regardless of which style of window you use, it's a good idea to try to follow the drawing rules used for update-based windows.
Size Constraints
Follow these guidelines when setting the size constraints:
- Set
WINDOW_X_POS
andWINDOW_Y_POS
to the location of the top-left corner of the window. In most cases, you can usewinUndefConstraint
(-32768
), which has the system place the window for you. This is particularly important for modal dialogs that are shorter than the available space. - The constraints you specify should be the ideal size without taking into consideration the device size.
- For the minimum, specify the minimum space that is realistically necessary to use the form and read its contents. Keep in mind that some pinlets, such as Asian handwriting recognition pinlets, may need more space than the Latin-based ones do. It is strongly suggested that you set the minimum height to between 100 and 160 coordinates.
- For the preferred size, specify the amount of space needed to read the entire contents of the form. For example, if the main element on the form is a list that always contains ten items, the preferred size would be to show all ten items. For a form whose main element is a large text field, the preferred size would be large enough for the text field to show its maximum number of characters.
Of course, the amount of space needed to display the entire contents of the form may not be known until runtime. If so, you can either set the preferred size to the constant
winMaxConstraint
(32767
), which means make the window as large as possible, or set new size constraints at runtime usingWinSetConstraints()
. - The maximum size is often the same as the preferred size. If the form does not contain any variable length items such as tables or editable text fields, the maximum size should be the size required to read the entire contents of the form. If the form contains variable length items, set the maximum to the constant
winMaxConstraint
(32767
) because the user can use that extra space for editing. - A dialog is often already shorter than the available space, so the minimum, maximum, and preferred sizes are often the same.
Resizing only happens on devices that use a dynamic input area. Devices that use a static input area do not resize application windows.
Displaying a Form
Displaying a form involves several steps:
- Call
FrmGotoForm()
. Pass it the resource ID of the form to be loaded and a pointer to the database that contains the resource. For example:if ((error = SysGetModuleDatabase(SysGetRefNum(), &gDbID, &gDbP)) < errNone) return error; FrmGotoForm(gDbP, MainForm);
This function adds a
frmLoadEvent
to the event queue followed by afrmOpenEvent
. - Respond to the
frmLoadEvent
by initializing the form and settings its event handler. See "Initializing a Form" and "Setting a Form's Event Handler." - Respond to the
winResizedEvent
by rearranging the elements on the form. See "Laying Out a Form or Dialog." - Respond to the
frmOpenEvent
by performing any initialization that must occur before the form is displayed. See "Opening a Form." - Respond to the
frmUpdateEvent
by drawing the form. See "Drawing or Updating a Form."
Steps 3 through 5 may not require you to write any code. The default behavior is sufficient for many forms.
Displaying a dialog is very different from displaying a form. See "Displaying Dialogs."
Initializing a Form
Initialize the form in response to a frmLoadEvent
by doing the following:
- Call
FrmInitForm()
to initialize the form's internal data structure and obtain a pointer to it. - Call
FrmSetActiveForm()
make the form become the active form.
These steps are typically performed in the AppHandleEvent()
function as shown in Listing 2.2.
Listing 2.2 Initializing a form
static Boolean AppHandleEvent(EventType *eventP) { uint16_t formId; FormType *frmP; if (eventP->eType == frmLoadEvent) { formId = eventP->data.frmLoad.formID; frmP = FrmInitForm(gDbID, formId); FrmSetActiveForm(frmP); ... return true; } return false; }
The FrmInitForm()
function initializes the internal FormType
data structure for the form. This data structure is passed to all form functions.
The FrmSetActiveForm()
function requests that the form be granted input focus. The request is granted asynchronously and can fail if a process with a window in a higher layer requests the input focus at the same time. If the form receives the input focus as a result of this request when initializing a form, it receives these events:
See "Input Focus" for more information about these events.
Setting a Form's Event Handler
You typically assign a custom event handler to a form in response to the frmLoadEvent
. As you learned in Exploring Palm OS: Programming Basics, the standard application event loop calls the system function FrmDispatchEvent()
to send events to a form. FrmDispatchEvent()
does the following:
- If your form has an event handler set, it calls that event handler.
- If your form's event handler returns
false
, it then callsFrmHandleEvent()
to let the built-in code provide default behavior.
To set the form's event handler, call FrmSetEventHandler()
in the AppHandleEvent()
function as shown in Listing 2.3.
Listing 2.3 Setting the event handler
static Boolean AppHandleEvent(EventPtr eventP) { uint16_t formId; FormType *frmP; if (eventP->eType == frmLoadEvent) { ... case MainForm: FrmSetEventHandler(frmP, MainFormHandleEvent); break; case EditForm: FrmSetEventHandler(frmP, EditFormHandleEvent); break; // one case statement per form typically. return true; } return false; }
Laying Out a Form or Dialog
Before your form is first displayed or when the user performs an action that causes the form to be resized, the application receives a winResizedEvent
. The event structure contains the bounds into which the form should be drawn.
If the form is a dialog that remains a constant size, the system takes care of the winResizedEvent
for you as long as you have specified winUndefConstraint
for the position of the dialog. The system moves the dialog so that it is always at the bottom of the area allocated to the application.
For forms and full-screen dialogs, the system can take care of much of the resizing for you if you set up the form properly, but you may need to adjust it afterwards. To set up the form for automatic resizing, create an array of FormLayoutType
structures that defines a layout rule for each element within the form. Pass the array to FrmInitLayout()
in response to the frmLoadEvent
. See Listing 2.4.
Listing 2.4 Automatic form layout
struct FormLayoutType editFormLayout[] = { { sizeof(FormLayoutType), EditFormSelectorTrigger, 0, frmFollowTop }, { sizeof(FormLayoutType), EditFormTextField, 0, frmFollowTopBottom }, { sizeof(FormLayoutType), EditFormScrollBar, 0, frmFollowTopBottom }, { sizeof(FormLayoutType), EditFormDoneButton, 0, frmFollowBottom }, { sizeof(FormLayoutType), EditFormDetailsButton, 0, frmFollowBottom }, // NOTE: Don't forget the shift indicator! Use the special // value frmLayoutGraffitiShiftID as its ID. { sizeof(FormLayoutType), frmLayoutGraffitiShiftID, 0, frmFollowBottom }, { 0, 0, 0, 0 } // always end the array with a 0 element. }; if (eventP->eType == frmLoadEvent) { formId = eventP->data.frmLoad.formID; frmP = FrmInitForm(gDbID, formId); FrmSetActiveForm(frmP); switch (formId) { case EditForm: FrmSetEventHandler(frmP, EditFormHandleEvent); FrmInitLayout(frmP, editFormLayout); break; return true; }
The layout rule specifies which sides of the form the element is anchored to. If the element is anchored to one side, it moves. If the element is anchored to opposite sides, it resizes. For example, frmFollowBottom
specifies that the element is always at a fixed location relative to the bottom of the form. If the form's bottom changes (for example, if the form resizes because the user closes the dynamic input area), the element moves. If the element is anchored to both the top and bottom with frmFollowTopBottom
, the element resizes. Similarly, if you anchor an element to the left or right side of the form with frmFollowLeft
or frmFollowRight
, the element moves if the form is resized horizontally. If you use frmFollowLeftRight
, the element resizes with the form. Note that the horizontal resizing rule is independent of the vertical resizing rule. For example, you can have a command button use the following rule:
frmFollowBottom | frmFollowLeftRight
Such a button resizes horizontally but moves if the form is resized vertically.
Figure 2.3 shows how Listing 2.4 resizes its form.

For some forms, you do not need to respond to winResizedEvent
at all if you call FrmInitLayout()
. FrmHandleEvent()
by default calls FrmPerformLayout()
, which applies the layout rules to all of the elements in the form. However, FrmPerformLayout()
only changes an element's bounds. Specifically, you will need to handle the following:
- Scroll bars attached to tables or fields. You need to call
SclSetScrollBar()
with the new scroll values for the field or table so that the scroll bar automatically appears or disappears as necessary. - Tables. You may want to increase the number of rows a table displays.
- In any element that scrolls (such as a field, a list, or a table), you may need to make sure that the current selection is still visible when the form shrinks.
In each of these cases, call FrmPerformLayout()
first, then adjust the elements, and then return true
to specify that you've handled the event. Listing 2.5 shows an example of adjusting the number of table rows in response to winResizedEvent
.
Listing 2.5 Responding to winResizedEvent
FormType *frmP; WinHandle winH; TableType *tableP; int16_t rowHeight; int16_t numRows; RectangleType *tableBounds; case winResizedEvent: frmP = FrmGetActiveForm(); winH = FrmGetWindowHandle( frmP ); if( winH == eventP->data.winResized.window ) { FrmPerformLayout(frmP, &(eventP->data.winResized.newBounds)); // Fix up the resized table tableP = FrmGetObjectPtr(frmP, FrmGetObjectIndex(frmP, MyTable)); TblGetBounds(tableP, &tableBounds); rowHeight = TblGetRowHeight(tableP, 0); numRows = bounds.extent.y / rowHeight; // keep track of visible rows in a global gNumRowsVisibleInTable = numRows; // Load records into the table LoadRecordsIntoTable(); handled = true; } break;
If you ever need to reposition an element on the form using FrmSetObjectBounds()
, you cannot use FrmInitLayout()
or FrmPerformLayout()
to resize or reposition that element. Instead, you must leave it out of the array that you pass to FrmInitLayout()
, respond to winResizedEvent
as shown above, and manually reposition and resize that element.
Opening a Form
When the system is ready to open a form, it sends the frmOpenEvent
. This is a request for the application to perform any initialization that needs to happen before a form is displayed. You might, for example, retrieve a database record that the form should display or set the initial selection for a group of push buttons on the form.
Drawing or Updating a Form
Palm OS Cobalt sends a frmUpdateEvent
when it is ready for the application to draw. This event contains a rectangle specifying the region that needs to be drawn. All drawing is clipped to this rectangle.
Most applications do not need to respond to frmUpdateEvent
. The default event handler redraws all user interface elements that a form contains. You only need to respond to this event if you are performing custom drawing. Respond by doing the following:
- Call
FrmDrawForm()
. - Draw using the functions described in Chapter 8, "Drawing."
Note that because the clipping rectangle is set to the invalidated region when frmUpdateEvent
is sent, FrmDrawForm()
only redraws what is necessary to redraw.
If you are performing custom drawing, always respond to frmUpdateEvent
for any type of window because a window from another process can obscure and then expose your window at any time. The frmUpdateEvent
is the signal that your window needs to be redrawn.
Invalidating a Form
If you want to refresh the contents of the form, do not directly redraw. Instead, invalidate the form by calling WinInvalidateWindow()
or WinInvalidateRect()
. These two functions mark all or a portion of the form as dirty. The next time drawing occurs, the form receives the frmUpdateEvent
specifying the area that needs to be redrawn.
As an optimization, you can use WinInvalidateRectFunc()
instead. This function takes a pointer to a callback function, which the system calls instead of sending a frmUpdateEvent
.
It is not an error to directly redraw into a transitional or legacy window. It is an error to directly redraw into an update-based window. Because the update-based window does not have a graphics context until it receives a frmUpdateEvent
, redrawing directly into an update-based windows causes an application crash.
Closing a Form
A form is closed by one of two functions:
-
FrmGotoForm()
closes the current form by adding afrmCloseEvent
to the event queue before adding thefrmLoadEvent
andfrmOpenEvent
for the new form. -
FrmCloseAllForms()
posts afrmCloseEvent
for each open form. It should be called before an application shuts down.
Respond to frmCloseEvent
by undoing anything you did in response to frmOpenEvent
. Deallocate memory you allocated, write changes to a database record to the database, and so on.
TIP: Return
false
from your frmCloseEvent
handler to make sure that the default event handler deletes the form.
Displaying Dialogs
Displaying a dialog is different from displaying a form. Depending on the dialog, it can be much simpler than displaying a form. Specifically, if you display an alert, a progress dialog, or a tips dialog, there are special functions that you may use for convenience.
This section first describes how to display modal dialogs in general and then describes how to display special types of dialogs.
Displaying Any Modal Dialog
A dialog displays on top of another form. You do not call FrmGotoForm()
to display a dialog because FrmGotoForm()
closes the current form before it opens the new one. Instead, you should call FrmDoDialog()
to display a dialog.
The differences between displaying a form and displaying a dialog are:
- You do not receive
frmLoadEvent
orfrmOpenEvent
if you callFrmDoDialog()
. Perform any necessary initialization, including callingFrmInitForm()
and setting the form's event handler, before you call that function.FrmDoDialog()
callsFrmSetActiveForm()
for you. - You also do not receive the
frmCloseEvent
. Instead,FrmDoDialog()
returns when the dialog is closed. The return value is the value of the button that was tapped to dismiss the dialog. Therefore, you must callFrmDeleteForm()
when you are done displaying the dialog. -
FrmDoDialog()
runs its own event loop. You can still set an event handler for the dialog, andFrmDoDialog()
will call it before callingFrmHandleEvent()
.
frmP = FrmInitForm(gDbP, MyDialogForm); FrmSetEventHandler(frmP, MyDialogEventHandler); /* initialize the dialog's controls here if necessary */ /* open the dialog, and wait until a button is pressed to close it. */ whichButton = FrmDoDialog(frmP); if (whichButton == DetailsOKButton) { /* get data from controls on the form to save/apply changes */ } FrmDeleteForm(frmP);
If you need more control over the dialog and how it behaves, call FrmPopupForm()
instead of FrmDoDialog()
. FrmPopupForm()
is like FrmGotoForm()
except that it does not close the current form. Your dialog will receive the frmLoadEvent
and frmOpenEvent
and you should otherwise follow the instructions in "Displaying a Form." When your dialog should be dismissed, perform any necessary cleanup and then call FrmReturnToForm()
with a form ID of 0 to return to the previously displayed form.
Alert Dialogs
An alert dialog is a modal dialog that is primarily informational (see Figure 2.4). It might prompt the user to confirm an action, or it might display an error message.

The following functions display alerts:
-
FrmAlert()
displays a simple alert that contains a message string and has several command buttons. -
FrmCustomAlert()
displays an alert with a customized string. You create the string using the strings ^1, ^2, and ^3 as placeholders. When you callFrmCustomAlert()
, you pass strings to substitute for those placeholders. -
FrmCustomResponseAlert()
displays an alert with a text field as well as command buttons. You pass a pointer to a callback function that verifies the text the user enters in the text field. This is typically used for password dialogs.
All of these functions return the item number of the button that the user tapped to dismiss the alert. Buttons are numbered from left to right starting with button 0. The functions handle all steps for you, from calling FrmInitForm()
to calling FrmDeleteForm()
.
Help Dialogs
A help or tips dialog displays when the user taps the "i" button on a modal dialog. You typically only provide a string resource containing the help text and the system handles displaying the dialog for you.
If you want to display a help dialog through some other means, use the FrmHelp()
function. As with the alert functions, FrmHelp()
initializes the form for you before it opens and deletes it when the user closes it. You only need to make the one function call.
Progress Dialogs
If your application performs a lengthy process, such as data transfer during a communications session, consider displaying a progress dialog to inform the user of the status of the process. The Progress Manager provides the mechanism to display progress dialogs.
You display a progress dialog by calling PrgStartDialog()
. Then, as your process progresses, you call PrgUpdateDialog()
to update the dialog with new information for the user. In your event loop you call PrgHandleEvent()
to handle the progress dialog update events queued by PrgUpdateDialog()
. The PrgHandleEvent()
function makes a callback to a PrgCallbackFunc()
function that you supply to get the latest progress information.
Note that whatever operation you are doing that is the lengthy process, you do the work either inside your normal event loop or in a separate thread, not in the callback function. That is, you call EvtGetEvent()
and do work when you get a nilEvent
. Each time you get a nilEvent
, do a chunk of work, but be sure to continue to call EvtGetEvent()
frequently (like every half second), so that pen taps and other events get noticed quickly enough.
The dialog can display a few lines of text that are automatically centered and formatted. You can also specify an icon that identifies the operation in progress. The dialog has two optional buttons:
- A Cancel or OK button. The label of this button is automatically controlled by the Progress Manager and depends on the current progress state (no error, error, or user canceled operation).
- An optional skip button. You can enable the use of a skip button by setting a flag in the structure returned by
PrgCallbackFunc()
. If the user taps the skip button, you should skip the current stage of the process and move on to the next stage. Of course, you only enable the button if stages in the process can be skipped.
Progress Callback Function
When you want to update the progress dialog with new information, you call the function PrgUpdateDialog()
. To get the current progress information to display in the progress dialog, PrgHandleEvent()
calls a function that you supplied in your call to PrgStartDialog()
.
The system passes the callback function one parameter, a pointer to a PrgCallbackData
structure. To learn what type of information is passed in this structure, see Chapter 26, "Progress Manager Reference."
Your callback function should return a Boolean. Return true
if the progress dialog should be updated using the values you specified in the PrgCallbackData
structure. If you specify false
, the dialog is still updated, but with default status messages. (Returning false
is not recommended.)
In the callback function, you should set the value of the PrgCallbackData
's textP
buffer to the string you want to display in the progress dialog when it is updated. You can use the value in the stage
field to look up a message in a string resource. You also might want to append the text in the message
field to your base string. Typically, the message
field would contain more dynamic information that depends on a user selection, such as a phone number, device name, or network identifier, etc.
For example, the PrgUpdateDialog()
function might have been called with a stage
of 1 and a messageP
parameter value of a phone number string, "555-1212". Based on the stage, you might find the string "Dialing" in a string resource, and append the phone number, to form the final text "Dialing 555-1212" that you place in the text buffer textP
.
Keeping the static strings corresponding to various stages in a resource makes it easier to localize your application. More dynamic information can be passed in using the messageP
parameter to PrgUpdateDialog()
.
NOTE: The callback function is called only if the parameters passed to
PrgUpdateDialog()
have changed from the last time it was called. If PrgUpdateDialog()
is called twice with exactly the same parameters, the callback function is called only once.
Working With UI Elements
One of the main purposes of a form is to display other user interface elements (sometimes called objects) such as command buttons, text fields, and tables. Specifics on programmatically working with each type of element are given in most of the remaining chapters of this book.
You use the form to gain initial access to an element on that form. You also use form functions to control when the element is displayed. Both of these topics are described in this section.
Accessing an Element
The elements that a form contains are accessed through the form. Functions tend to reference a UI element in one of the following ways:
- By the resource ID that you specified when you created the element in a resource file.
- By the element's index, which is the index into the form's internal list of elements. If you have a resource ID, you can obtain the element's index using the function
FrmGetObjectIndex()
. - By a pointer to the element's internal data structure. If you have an element's index, you can obtain its pointer using the function
FrmGetObjectPtr()
.
Most functions use the pointer reference to an element. For this reason, many applications define a function similar to the one shown in Listing 2.7.
Listing 2.7 Obtaining a pointer to a UI element
void *GetObjectPtr(uint16_t objectID) { FormType *frmP; frmP = FrmGetActiveForm(); return FrmGetObjectPtr(frmP, FrmGetObjectIndex(frmP, objectID)); } void SomeOtherFunction(void) { ControlType *controlP = (ControlType *)GetObjectPtr(MainPopupTrigger); ListType *listP = (ListType *)GetObjectPtr(MainPopupList); ... }
Dynamically Displaying Elements
If you need to have a user interface element appear or disappear at runtime, do the following:
- When creating your resource file, define all of the elements that may conceivably be displayed on the form at any time during the life of the application.
- If an element should not be displayed when the form is first displayed, set its usable attribute to
false
in the resource file. - When an event occurs that should change the form's display, use
FrmShowObject()
to show any previously undisplayed element. UseFrmHideObject()
to remove any element that should no longer be displayed.
For update-based windows, both FrmShowObject()
and FrmHideObject()
invalidate the area of the form where that element appears. That is, they asynchronously trigger a frmUpdateEvent
. When your application receives the frmUpdateEvent
, the new show and hide settings take effect. For all other windows, FrmHideObject()
and FrmShowObject()
are immediate.
TIP: In Palm OS Cobalt, hiding an element means filling its region with the color set for
UIFormFill
. This is true regardless of the type of window used for the form (legacy, transitional, or update-based).
Some applications must create their displays at runtime. These applications can use the dynamic UI APIs:
-
CtlNewControl()
-
CtlValidatePointer()
-
FldNewField()
-
FrmNewBitmap()
-
FrmNewForm()
-
FrmNewFormWithConstraints()
-
FrmNewGadget()
-
FrmNewGsi()
-
FrmNewLabel()
-
FrmRemoveObject()
-
FrmValidatePtr()
-
LstNewList()
You can use the FrmNewForm()
function to create new forms dynamically. Palm's UI guidelines encourage you to keep modal dialogs at the bottom of the screen, using the entire screen width. This isn't enforced by the function, but is strongly encouraged in order to maintain a look and feel that is consistent with the built-in applications.
The FrmNewLabel()
, FrmNewBitmap()
, FrmNewGadget()
, LstNewList()
, FldNewField()
and CtlNewControl()
functions can be used to create new elements on forms.
It is fine to add new items to an active form, but doing so is very likely to move the form structure in memory; therefore, any pointers to the form or to controls on the form might change. Make sure to update any variables or pointers that you are using so that they refer to the form's new memory location, which is returned when you create the element.
If you're not absolutely sure that you need to change your UI dynamically, don't do it—unexpected changes to an application's interface are likely to confuse or frustrate the end user.
The FrmRemoveObject()
function removes a dynamically created element from a form. This function doesn't free memory referenced by the element (if any) but it does shrink the form chunk. For best efficiency when removing items from forms, remove items in order of decreasing index values, beginning with the item having the highest index value. When removing items from a form, you need to be mindful of the same concerns as when adding items: the form pointer and pointers to controls on the form may change as a result of any call that moves the form structure in memory.
When creating forms dynamically, or just to make your application more robust, use the FrmValidatePtr()
function to ensure that your form pointer is valid and the form it points to is valid. This routine can catch lots of bugs for you—use it!