How You Can Communicate Between Frames Using User Events
You can utilize user events to communicate between two frames by performing the following basic steps:
1. Write the event block for the user event in one frame script.
The event block for the user event contains the code that is executed when the user event is triggered.
2. Include in the other frame script the SendUserEvent method to trigger the user event in the first frame.
When the SendUserEvent method is executed, OpenROAD places the specified user event in the native event queue. From there, OpenROAD dispatches the user event to the appropriate frame. The code for the user event is executed when the event reaches the top of the frame's event queue. The following illustration demonstrates this:
You can also have one frame send a user event to itself. For example, a frame that displays a clock can send an event to itself at regular intervals to update the clock. For an example of this, see
Delay Parameter.
Because the events in event queues are processed one at a time, the event block that places the user event in the native event queue (that is, the event block that contains the SendUserEvent method) completes before the user event is processed. If you want to force the event block that sends the user event to complete after a specified user event, use the WaitFor method. The WaitFor method prevents the current event block from being executed until the current frame receives the specified user event. For more information, see
WaitFor Method.
If necessary, you can remove a user event from an event queue with the PurgeUserEvent method. For more information, see
PurgeUserEvent Method.
UserEvent Event Block
In the UserEvent event block, you provide the code that is executed when the event is triggered. The syntax is:
on userevent ['eventname'] =
declare
declarations
end declare
{
statement list
}
eventname
Specifies any string of up to 32 characters enclosed in quotes. If you specify the event name, OpenROAD executes the event block whenever the frame receives that particular user event. If you do not specify an event name, OpenROAD executes the block whenever the frame receives a user event for which the frame does not have an event block specified by name.
For example, assume that a frame has event blocks for two user events, Event1 and Event2, and a general user event block that does not specify a user event name. When the frame receives Event1, OpenROAD executes the event block specified for Event1. Similarly, if the frame receives Event2, OpenROAD executes the event block specified for Event2. OpenROAD does not execute the generic event block in either case. However, if the frame receives Event3, for which the frame has no specific block, it executes the generic block.
You can specify any number of user event blocks in the frame script, although each must have a unique event name, or no event name at all.
Because the SendUserEvent method and external user events both trigger user event blocks, it is best to use appropriate naming conventions for your user events. (There is no way to tell within the event block whether the event was triggered by OpenROAD or by an external program.)
The event block for the user event can contain any OpenROAD statements. The following event block is for the DeleteEntry user event:
on userevent 'DeleteEntry' =
begin
delete_details_frame =
FrameExec(CurFrame.MessageObject);
/* Determine which row in table contains
** video. */
if find_video_row
(video_list = vlist, details_frame
= delete_details_frame, row = byref(i))
= TRUE
then
/* Now delete it */
vlist.RemoveRow(rownumber = i);
endif;
/* Now add it to new details frames list,
** since it is now in insert mode */
i = new_details_frames.LastRow() + 1;
new_details_frames[i] = delete_details_frame;
end;
How You Can Access the SendUserEvent Parameters
The SendUserEvent method has four message parameters, messageobject, messageinteger, messagefloat, and messagevarchar that you can use to pass values to the receiving frame. For information about these parameters, see
SendUserEvent Method.
If the SendUserEvent call activates a userevent event block in the receiving frame, OpenROAD stores these values in four corresponding FrameExec attributes, MessageObject, MessageInteger, MessageFloat, and MessageVarchar, that are accessible only in the event block. To access this data, use the CurFrame system variable to reference the current FrameExec.
In the following sample from a frame script, the UpdateTitle event block sets the window title to the value of the MessageVarchar attribute that is passed to the frame by the messagevarchar parameter of the SendUserEvent method:
on userevent 'UpdateTitle' =
begin
CurFrame.WindowTitle =
CurFrame.MessageVarchar;
end;
Because the MessageObject attribute stores an object of the generic Object class, you must cast the object to the appropriate system or user class in order to work with it in your user event block. (Casting is described in
How You Can Work with Attributes.)
In the following example, the MessageObject attribute contains a bitmap, so it is cast to the BitmapObject system class:
on userevent 'UpdateGraphic' =
begin
vid_graphic_bitmap =
BitmapObject(CurFrame.MessageObject);
CurFrame.Flush();
CurFrame.IsAutoSized = TRUE;
CurFrame.WindowVisibility = WV_VISIBLE;
CurFrame.BringToFront();
end;
In the next example, the MessageObject attribute contains an object of type video_row. The UpdateEntry event block casts the MessageObject to the video_row user class.
on userevent 'UpdateEntry' =
begin
video = VIDEO_ROW(CurFrame.MessageObject);
Note: If the SendUserEvent call completes a WaitFor method call, the message parameters are stored in the Event object returned by the WaitFor call. The Event object has five attributes. One attribute holds the name of the event and each of the other four holds one message parameter. You can access the parameter values in the Event object attributes in the event block that contains the WaitFor call.
SendUserEvent Method
Use the SendUserEvent method to trigger the user event in a frame. When the user event is triggered, OpenROAD adds it to the application's native event queue. If the frame is active, OpenROAD queues the user event immediately. If the frame is inactive because it has called another frame or procedure, OpenROAD waits until the frame becomes active again before it queues the user event. The message parameters of the SendUserEvent method let you send data to the receiving frame.
The syntax of the SendUserEvent method is:
FrameExec_var.SendUserEvent(eventname = varchar,
messageobject = object, messageinteger = integer,
messagefloat = float, messagevarchar = varchar,
delay = float, focusbehavior = integer,
errorevent = varchar)
The following example uses the SendUserEvent method to send the Flushvalues event to a child frame.
...
begin
...
childframe.SendUserEvent
(eventname = 'Flushvalues',
messageobject = CurFrame,
errorevent='WaitForEvent');
end;
To specify the frame that contains the user event you want to trigger, you must reference the FrameExec variable for the running instance of the frame. (You cannot use the frame name because OpenROAD allows you to open the same frame more than once.) There are four ways to reference the FrameExec variable:
• Use the CurFrame system variable name to reference the current frame.
Use this variable when the frame needs to send a user event to it. The following frame script example illustrates this concept:
CurFrame.SendUserEvent(eventname =
'LoadBalanceTable');
• Use the ParentFrame attribute to reference the parent of the current frame.
Because the ParentFrame attribute represents the ProcExec object for the parent frame, you must cast it as a FrameExec object in order to use it as a FrameExec reference. The following frame script example illustrates this concept:
on windowclose =
begin
FrameExec(CurFrame.ParentFrame).SendUserEvent
(eventname = 'GraphicClosed');
return;
end;
• Use the reference variable returned by the openframe statement that you used to open the frame.
In the following example, graphic_frame is a variable of type FrameExec:
graphic_frame.SendUserEvent
(eventname = 'UpdateTitle',
messagevarchar = video.title);
• Store a FrameExec reference obtained through any of the previously mentioned methods in a global variable so you can access it from other frames.
Message Parameters
The four optional message parameters of the SendUserEvent method, messageinteger, messagefloat, messagevarchar, and messageobject, let you pass values to the receiving frame. If the SendUserEvent method triggers a user event block, OpenROAD stores the values in the corresponding message attributes of the receiving frame's FrameExec object, MessageInteger, MessageFloat, MessageVarchar, and MessageObject. If the SendUserEvent method completes a WaitFor call in the receiving frame, OpenROAD stores the values in the Event object returned by the WaitFor call when it completes.
Use the three simple-variable parameters, messageinteger, messagefloat, and messagevarchar, to pass integer, float, and varchar values, respectively. The following example uses the messagevarchar parameter to send the title of the video to a frame:
on setvalue video.title =
begin
CurFrame.WindowTitle = video.title;
if graphic_frame is not null then
graphic_frame.SendUserEvent
(eventname= 'UpdateTitle',
messagevarchar = video.title);
endif;
end;
Use the messageobject parameter to pass any object. Because the object can be any system or user class, this parameter enables you to send any set of information that you want. This is especially useful with user classes. Simply create a user class that specifies the set of data you want to pass and then use the messageobject parameter to pass an object of that class. The following example uses this technique to pass information about a new video to the video_list frame. New_video is a reference variable of the video_row user class.
parent_frame.SendUserEvent(eventname =
'InsertEntry',messageobject = new_video);
In the receiving frame, you can access the object with the MessageObject attribute for CurFrame (described in
UserEvent Event Block).
Focusbehavior Parameter
Every frame has its own input focus. When the user leaves a window, the frame does not lose its input focus. The input focus stays on the same field, even though the user has selected another field in another window.
For example, if the user is in the process of entering data into a field on Window 1 and then selects Window 2, the input focus for Window 1 stays on the field where the user was entering the data. Because the field on the first frame still has the input focus, the data validation code for the field is not triggered, even though the user has left the window. (Typically, the data validation code for a field is contained in the SetValue or Exit event blocks for the field, so data validation takes place when the user changes or leaves the field.) The SendUserEvent method provides a focusbehavior parameter to enable you to deal with this situation.
The focusbehavior parameter of the SendUserEvent method lets you trigger the SetValue event or both SetValue and Exit events for the field with the input focus before the user event is triggered. The values for the focusbehavior parameter are:
FT_SETVALUE
Triggers the SetValue event for current field
FT_TAKEFOCUS
Triggers the SetValue and Exit events for current field
FT_NOSETVALUE
Specifies no forced processing
Default: FT_NOSETVALUE
Note: Setting the focusbehavior parameter to FT_SETVALUE or FT_TAKEFOCUS can cause the receiving frame to discard the user event that you sent. This happens when the SetValue or Exit event block for the current field in the receiving frame executes a resume statement. (For more information about the resume statement, see the Language Reference Guide). The user event is also discarded if a data type conversion error occurs when OpenROAD attempts to execute the SetValue event.
The following example uses the focusbehavior parameter to trigger the SetValue event in its child frames before they close:
vlist[i].details_frame.SendUserEvent(eventname=
'Cleanup',focusbehavior = FT_SETVALUE);
Delay Parameter
When you want to repeat a task at regular intervals, such as polling the database for changes or updating the time on a clock, you can trigger a user event after a specified amount of time. The delay parameter of the SendUserEvent lets you specify a number of seconds to wait before triggering the user event.
OpenROAD triggers the user event after the specified time has elapsed. In the meantime, OpenROAD continues executing the current event block, and events can continue to be triggered in any of the currently active frames. The default delay is 0.0 seconds (in other words, the event is triggered immediately).
In the following example, we use the delay parameter to update a block every 10 seconds:
initialize =
begin
CurFrame.SendUserEvent (eventname= 'clock');
end;
on userevent 'clock' =
begin
current_time = date('now');
CurFrame.SendUserEvent(eventname= 'clock',
delay = 10.0);
end
Errorevent Parameter
The errorevent parameter specifies a user event that is returned to the sending frame by the system if the current user event cannot be delivered. The following examples demonstrate how to use this parameter.
The following code would be in the sending frame:
declare
response = Event;
enddeclare
begin
targetframe.SendUserEvent (
eventname = 'originalevent',
errorevent = 'replyevent');
response = CurFrame.WaitFor (
eventname = 'replyevent');
if response.MessageErrorCode = 0 then
/*
** The target frame received
** and replied.
*/
...
else
/*
** The target frame never got the
** event.
*/
...
endif;
end;
In the target frame:
on userevent 'originalevent' =
{
/* Acknowledge that the event is received */
sendingframe.SendUserEvent
(eventname = 'replyevent');
/* Now process the event */
...
}
IMPORTANT! The target frame must reply with the replyevent event. Otherwise, the WaitFor method will not return, providing the event was delivered.
The following examples show another way of using the errorevent parameter without using the WaitFor method.
The following code would be in the sending frame:
begin
targetframe.SendUserEvent
(eventname = 'originalevent',
errorevent = 'errorevent');
end;
The following code would be in the target frame:
on userevent 'originalevent' =
{
/* Process the event */
...
}
on userevent 'errorevent' =
{
/* Error handling */
...
}
For more information about MessageErrorCode, see the Language Reference Guide.
WaitFor Method
Usually, when you use the SendUserEvent method, OpenROAD queues the user event and then continues executing the current event block. The WaitFor method lets you prevent the rest of the current event block from being executed until you tell it to stop waiting.
When you invoke the WaitFor method, you specify the name of a user event that the event block must wait for. When the current frame receives that user event, control returns to the statement immediately following the WaitFor call.
The syntax of the WaitFor method is:
event = CurFrame.WaitFor(eventname = varchar)
You can use the WaitFor method for the current frame only with the CurFrame reference variable. The method returns an Event object that contains the eventname, messageobject, messageinteger, messagefloat, and message varchar parameters sent with the SendUserEvent call that completes the method. (When the SendUserEvent call completes a WaitFor call, the SendUserEvent focusbehavior parameter is ignored in the receiving frame.)
WaitFor makes the executing frame ignore any other events, both from the user and other SendUserEvent methods. Window-level events, such as button clicks, are disabled. User events are delivered to the frame after it receives the user event that it is waiting for.
IMPORTANT! Do not use the WaitFor method when you are sending the user event from the current frame to itself. The user event is never delivered to the frame and the event block is never continued. This results in the frame remaining in an unending wait state.
The most common use of the WaitFor method is immediately following a SendUserEvent method to wait for acknowledgment that the user event was processed. This is especially useful when coordination of frames is important.
The following sample frame script uses the WaitFor method to wait until all child frames have closed themselves before executing the return statement to close itself:
on click close_button,
on windowclose =
begin
/* If there are any detail frames open,
** tell them to clean up themselves. */
i = 1;
while i <= vlist.LastRow() do
if vlist[i].details_frame is not null then
vlist[i].details_frame.SendUserEvent
(eventname ='Cleanup',
focusbehavior = FT_SETVALUE);
CurFrame.WaitFor(eventname = 'Done');
endif;
i = i + 1;
endwhile;
/* If there are any detail frames for
** new videos, tell them to clean up. */
i = 1;
while i <= new_details_frames.LastRow() do
new_details_frames[i].SendUserEvent
(eventname ='Cleanup',
focusbehavior = FT_SETVALUE);
CurFrame.WaitFor(eventname = 'Done');
i = i + 1;
endwhile;
return;
end;
Be sure to use the SendUserEvent method to send the user event back to the frame that contains the WaitFor call. The following example sends the Done user event back to the parent frame:
if cleaning_up = TRUE then
parent_frame.SendUserEvent(eventname = 'Done');
PurgeUserEvent Method
The PurgeUserEvent method lets you remove user events from the event queue. If you trigger a series of related user events and one of the events fails for some reason, you can use this method to remove the remaining user events from the event queue.
In addition, when you use the resume statement to prevent the user from leaving a field that contains invalid data, OpenROAD does not remove the user events from the event queue (although it does remove the other frame events for the current frame). Use the PurgeUserEvent method to remove them if necessary. The PurgeUserEvent method removes one or all the user events from the event queue.
The syntax of this method is:
integer_var = FrameExec_var.PurgeUserEvent
(eventname = varchar(256))
The PurgeUserEvent method removes the specified user event from the event queue. If you do not specify the eventname parameter, it removes all user events for the specified frame. The method returns the number of events removed from the queue.
You can use this method for any running version of a frame. To specify the FrameExec variable for the running version of the frame, use any of the methods described in
SendUserEvent Method.