This WAIT-FOR statement instructs the AVM to stop executing the current block and remain in a wait state (blocking) until a .NET method that it calls returns. The AVM continues to respond to all incoming ABL events (see the WAIT-FOR statement (ABL only) reference entry) as well as .NET events, and it executes any associated triggers, event procedures, or .NET event handlers while in this wait state.
Note: ABL supports a separate version of the WAIT-FOR statement (ABL WAIT-FOR) that blocks only for ABL events. For more information, see the WAIT-FOR statement (ABL only) reference entry.
Syntax
WAIT-FOR {object-reference|type-name} : method-name ( [parameters] )
[ SET return-value]
object-reference
A reference to an object that generally inherits from the .NET class System.Windows.Forms.Form or System.Windows.Forms.CommonDialog. OpenEdge provides a particular subclass of System.Windows.Forms.Form—the Progress.Windows.Form class—which you can use to create .NET forms in an ABL session that co-exist more naturally with ABL windows.
type-name
The type name of a .NET class that provides a static blocking method, most commonly System.Windows.Forms.Application. With an appropriate USING statement, you can also specify the type by its unqualified class name (Application).
method-name
The name of a .NET input-blocking method that the WAIT-FOR statement calls, which is typically Run (a static method on System.Windows.Forms.Application), or by .NET convention, ShowDialog (an instance method on System.Windows.Forms.Form or System.Windows.Forms.CommonDialog).
parameters
Parameters for the method specified by method-name.
SET return-value
Provides the return value from the method, method-name( ), which is set when the WAIT-FOR statement completes execution. The return-value can be a variable, property, or field that has the same data type as the method-name( ) return value, typically System.Windows.Forms.DialogResult.
To use this option, method-name( ) must be a non-VOID method. If you specify this option for a VOID method, such as System.Windows.Forms.Application:Run( ), ABL raises a compile-time error.
For more information on .NET input-blocking methods that you can call in the WAIT-FOR statement, see the notes of this reference entry.
Example
The ABL-derived .NET class, r-WaitForms, inherits the Progress.Windows.Form class to implement a non-modal .NET form. When you try to close the displayed form, a dialog box appears that prompts if you want the form to complete closing or not. If you choose to complete closing, the form closes. If you choose to cancel the closing, the form remains displayed, and you can try to close the form, again.
When you instantiate r-WaitForms, it initializes and subscribes a handler (the Form_Closing( ) method) to the FormClosing event of the form. You can then display the form by calling the DoWait( ) method on the r-WaitForms instance. This method executes the WAIT-FOR statement, which calls the .NET input-blocking method System.Windows.Forms.Application:Run( ). (For more information on this method, see the notes.) When you try to close the displayed form, this causes the non-modal form to publish its FormClosing event, which executes the Form_Closing( ) method to handle the event.
r-WaitForms.cls
USING System.Windows.Forms.* FROM ASSEMBLY.
USING Progress.Util.* FROM ASSEMBLY.
CLASS r-WaitForms INHERITS Progress.Windows.Form:
DEFINE VARIABLE rFormDescr AS CLASS Label NO-UNDO.
METHOD PUBLIC VOID DoWait( ).
/* Display and wait for the non-modal form to close */
WAIT-FOR Application:Run( INPUT THIS-OBJECT ). END METHOD.
CONSTRUCTOR PUBLIC r-WaitForms( ):
/* Initialize and subscribe to events */
InitializeComponent( ).
THIS-OBJECT:FormClosing:Subscribe(Form_Closing). END CONSTRUCTOR.
METHOD PRIVATE VOID InitializeComponent( ):
/* Initialize the non-modal form class and components */
rFormDescr = NEW Label( ).
/* Initialize the form description label */
rFormDescr:Text = "Click the Close (X) button of this form to
pop-up
a dialog box ...".
rFormDescr:Size = NEW System.Drawing.Size( INPUT 330, INPUT 13 ).
rFormDescr:Location = NEW System.Drawing.Point( INPUT 4, INPUT 6 ).
/* Initialize the non-modal form */
THIS-OBJECT:FormBorderStyle = FormBorderStyle:FixedSingle.
THIS-OBJECT:Text = "This is my form.".
THIS-OBJECT:Controls:Add( INPUT rFormDescr ).
THIS-OBJECT:Size = NEW System.Drawing.Size( INPUT rFormDescr:Width, INPUT 60 ).
END METHOD.
METHOD PRIVATE VOID Form_Closing ( INPUT sender AS System.Object, INPUT e AS FormClosingEventArgs ):
DEFINE VARIABLE rDialog AS CLASS Progress.Windows.Form NO-UNDO. DEFINE VARIABLE rDialogDescr AS CLASS Label NO-UNDO.
DEFINE VARIABLE rOKButton AS CLASS Button NO-UNDO. DEFINE VARIABLE rCancelButton AS CLASS Button NO-UNDO. DEFINE VARIABLE enDialogResult AS CLASS DialogResult NO-UNDO.
/* Create dialog box components */
ASSIGN
rDialog = NEW Progress.Windows.Form( ) rDialogDescr = NEW Label( )
rOKButton = NEW Button( ) rCancelButton = NEW Button( ).
/* Initialize the dialog description label */
rDialogDescr:Text = "Click OK to close form or click Cancel to
leave form open.".
rDialogDescr:Size = NEW System.Drawing.Size( INPUT 306, INPUT 13).
rDialogDescr:Location = NEW System.Drawing.Point( INPUT 4, INPUT 6 ).
/* Initialize the modal dialog box with label and buttons */
rDialog:FormBorderStyle = FormBorderStyle:FixedDialog.
rDialog:Controls:Add( INPUT rDialogDescr ).
rDialog:Controls:Add( INPUT rOKButton ).
rDialog:Controls:Add( INPUT rCancelButton ).
rDialog:Text = "My form is closing ...".
rDialog:Size = NEW System.Drawing.Size( INPUT 306, INPUT 106 ).
/* Display dialog box to handle FormClosing event and the results */
WAIT-FOR rDialog:ShowDialog( ) SET enDialogResult.
IF EnumHelper:AreEqual ( INPUT enDialogResult, INPUT DialogResult:Cancel ) THEN DO: MessageBox:Show( INPUT "My form closing was canceled." ). e:Cancel = TRUE. /* Cancel FormClosing; leave the main form open */ END. ELSE DO: MessageBox:Show( INPUT "My form is closing OK." ). e:Cancel = FALSE. /* Continue FormClosing; close the main form */ END.
END METHOD. /* Form_Closing */
END CLASS.
The Form_Closing( ) method passes INPUT parameters from .NET for the FormClosing event. One of these parameters (e) is a System.Windows.Forms.FormClosingEventArgs object, which contains a Cancel property whose setting allows the event handler to either complete the FormClosing event or interrupt and cancel the FormClosing event. To determine how to set this property, the event handler instantiates, initializes, and displays another Progress.Windows.Form class (rDialog) as a modal dialog box.
The dialog box contains two buttons, rOKButton and rCancelButton, whose DialogResult properties are set to the System.Windows.Forms.DialogResult enumeration values OK and Cancel, respectively. The event handler displays rDialog as a modal form by executing the WAIT-FOR statement, which calls the modal input-blocking method System.Windows.Forms.Form:ShowDialog( ). (For more information on this method, see the notes.)
When you click one of the two dialog buttons, this causes the dialog box to close and the ShowDialog( ) method to return. This automatically sets the DialogResult property on rDialog to the value of the DialogResult property on the button that you have clicked and also returns the same property value as the value of ShowDialog( ), which the WAIT-FOR statement assigns to the variable, enDialogResult. The event handler then uses the static AreEqual( ) method on the Progress.Util.EnumHelper class to test the value of enDialogResult and set the e:Cancel property to either complete the FormClosing event or cancel the FormClosing event and leave the non-modal form open for further input.
Note: The calls to System.Windows.Forms.MessageBox:Show( ) display a message box similar to the ABL MESSAGE statement with the VIEW-AS ALERT-BOX option.
To instantiate r-WaitForms and display the non-modal form, you can thus run a procedure with code like this:
DEFINE VARIABLE rWaitForms AS CLASS r-WaitForms NO-UNDO.
rWaitForms = NEW r-WaitForms( ).
rWaitForms:DoWait( ).
Notes
A non-GUI ABL session cannot visualize .NET form and control objects and cannot block for GUI or non-GUI .NET events. So, you cannot invoke this WAIT-FOR statement (calling a .NET input-blocking method) within a non-GUI, Windows ABL session, including a:
Character mode (CHUI) client
Batch-mode client
AppServer agent session
WebSpeed agent session
If you use any .NET forms in an ABL session, you can execute only one .NET WAIT-FOR statement that processes events for all .NET non-modal forms and their controls. This statement must be the first WAIT-FOR statement for processing non-modal events in your application. Following this statement, from event handlers and trigger blocks, you can execute multiple input-blocking statements to process any modal .NET form or ABL dialog box. ABL events for non-modal windows and their child widgets, or for non-GUI ABL features, such as asynchronous remote procedure calls and socket operations, all work in the context of this single non-modal .NET WAIT-FOR statement.
Caution: Using stacked input-blocking statements other than for dialog boxes is not recommended. If you do so, you must take special care to ensure that any WAIT-FOR statements that you simultaneously execute to process non-modal events complete their execution in reverse order of invocation. Otherwise, your application will have unpredictable behavior. Also, in certain contexts (such as within a user-defined function or non-VOID method), if you execute an additional input-blocking or event-processing statement while displaying an ABL non-modal window together with .NET non-modal forms, the AVM raises the STOP condition.
To block on any number of non-modal .NET forms (and ABL windows), you must use a WAIT-FOR statement that executes the System.Windows.Forms.Application:Run( ) input-blocking method (as shown for the example class, r-WaitForms):
You can specify form-object-ref as an object reference to a single .NET non-modal form object, on which the WAIT-FOR statement blocks, displays, and waits to close. If you specify form-object-ref, the statement also displays any additional non-modal forms that you have previously initialized by setting their Visible properties to TRUE or by invoking their Show( ) methods. However, .NET automatically displays form-object-ref, itself, without having to set its Visible property or run its Show( ) method. You can also use triggers, event procedures, and .NET event handlers to create and display additional non-modal .NET forms (or ABL windows) after the WAIT-FOR statement blocks for events.
The conditions for unblocking this WAIT-FOR statement differ, depending on whether you specify form-object-ref. However, whatever conditions unblock the WAIT-FOR statement also automatically close any open .NET forms. This is different from ABL windows, which you must explicitly open and close using ABL statements or widget attributes without regard to the execution of a WAIT-FOR statement.
With form-object-ref, the WAIT-FOR statement unblocks and continues execution with the following statement if one of the following actions occurs:
Your application explicitly calls the form-object-ref:Close( ) method, which automatically calls the Dispose( ) method to garbage collect the form.
The user clicks the form Close (X) button in the upper right corner of the form-object-ref form, and you do not cancel the action in a handler for the FormClosing event, as in the example. This action also automatically calls the form-object-ref:Close( ) method.
Your application calls the System.Windows.Forms.Application:Exit( ) method.
When you use any of these techniques to unblock the WAIT-FOR statement, all currently displayed non-modal forms close, in addition to the form specified by form-object-ref, and including any non-modal .NET forms that were created and displayed after the WAIT-FOR statement blocked.
If you are executing the READKEY statement within a trigger or event handler while blocking on a form-object-ref, and the user clicks the form Close (X) button, the ABL application shuts down unconditionally. For example, the following READKEY loop can cause this shutdown to occur:
DO WHILE LASTKEY != KEYCODE("F3"):
READKEY.
IF LASTKEY = KEYCODE("F3") THEN RETURN.
END.
This shutdown occurs because .NET generates a WM_QUIT message in response to clicking the Close (X) button that READKEY interprets (by design) as a message to shut down the application.
Caution: You might have a problem displaying message boxes after invoking Application:Exit( ) or Application:ExitThread( ). One way this can happen: if you use any two of the techniques for unblocking the WAIT-FOR, such as by calling Close( ), then calling Application:Exit( ), any subsequent executions of the MESSAGE statement with the VIEW-AS ALERT-BOX option or any subsequent calls to System.Windows.Forms.MessageBox:Show( ) might not display the specified message box and the user will hear a beep sound instead.
If you do not specify form-object-ref, the statement displays and blocks for input on any non-modal forms that you have previously initialized by setting their Visible properties to TRUE or by invoking their Show( ) methods. Without form-object-ref, you also do not need to have a .NET form instantiated before you execute the WAIT-FOR statement. Without any non-modal .NET form created, this statement processes ABL events until you create and initialize your first .NET non-modal form for display in an associated event handler or trigger, at which point the same WAIT-FOR statement processes both .NET and ABL events.
Also, if you do not specify form-object-ref, the WAIT-FOR statement unblocks and continues execution with the following statement only when you invoke the System.Windows.Forms.Application:Exit( ) method at some point in the ABL session. This method closes all non-modal .NET forms that are currently open before unblocking the WAIT-FOR statement. Note that using this technique, you must be sure to create and initialize at least one .NET form or ABL window (non-modal or modal) so there are active components to work with during the input-blocking state. Otherwise, the blocking WAIT-FOR statement blocks indefinitely or until the user presses CTRL+BREAK.
Note: When working with any displayed non-modal form, except the form specified by form-object-ref, the user (using the Close (X) button) or the application (using the Close( ) method) can close the form. However, this does not by itself unblock the blocking WAIT-FOR statement. If you want the WAIT-FOR to unblock other than by closing the form specified by form-object-ref, your application must call Application:Exit( ).
To block on a .NET modal dialog box, you must use a WAIT-FOR statement that executes the ShowDialog( ) input-blocking method on the specified form object (as shown for rDialog in the example class, r-WaitForms.cls):
This causes the WAIT-FOR statement to display the form specified by the dialog-object-ref object reference, and block for input on that form as a dialog box. You can also specify the object reference of a form (parent-form) that becomes the parent of the dialog box referenced by dialog-object-ref. (This allows the .NET dialog box to display centered over the parent form.)
Note that by convention, every .NET class that can implement a modal dialog box has a ShowDialog( ) method, such as System.Windows.Forms.Form and System.Windows.Forms.CommonDialog, as well as classes that derive from them, like System.Windows.Forms.OpenFileDialog. Therefore, this syntax for the WAIT-FOR statement allows you to open all these different types of dialog boxes.
Note: The System.Windows.Forms.MessageBox class does not work this way. Instead, it is similar to the MESSAGE statement with the VIEW-AS ALERT-BOX option, which handles its own input without events. Similarly, you can only open a MessageBox by invoking its static Show( ) method outside of a WAIT-FOR statement.
With dialog-object-ref:ShowDialog( ), the WAIT-FOR statement unblocks and continues execution with the following statement if one of the following actions occurs:
The user clicks the dialog box Close (X) button in the upper right corner of the dialog box.
You set the dialog-object-ref:DialogResult property (if the form class supports it) to a valid System.Windows.Forms.DialogResult enumeration value.
You call the dialog-object-ref:Close( ) method. Note that calling this method also automatically calls the Dispose( ) method on the form, making the form object available for garbage collection.
For any of these actions, the FormClosing and FormClosed events are also published on dialog-object-ref, and you can handle the FormClosing event in order to prevent the form from being closed by cancelling the action, as shown in the example.
Caution: .NET automatically calls Dispose( ) on a non-modal form when it is closed, unless the form is shown as a dialog box. .NET simply hides the dialog when it is closed. However, the AVM calls Dispose( ) by default for dialogs that inherit from Progress.Windows.Form. The AVM does not do this if the DisposeDialogOnClose property of the form is set to FALSE. This can be set on a particular instance or globally using the No Dialog Dispose (-nodialogdispose) startup parameter. One use case for not calling Dispose( ) is if you want to re-use the form at a later time.
At this point, you can check the user response to the dialog box. Note that ShowDialog( ) returns a DialogResult (enumeration) value with the result of the dialog box. You can access this value using the SET option (as shown in the r-WaitForms.cls example) or by checking the dialog-object-ref:DialogResult property (if the form object is still available and the class supports it).
Note: Not all .NET form classes provide a public DialogResult property—for example, System.Windows.Forms.FileDialog. For a form that does not provide this property, you can handle dialog box results in the following ways: 1) by using the SET option to return the form's ShowDialog( ) method value, 2) by using event handlers subscribed to the events that the form provides, for example, the FileOk or HelpRequest event, or 3) by testing the values of properties that might otherwise be set depending on input to the dialog box, such as the FileName property of the System.Windows.Forms.OpenFileDialog object.
.NET does not set the dialog-object-ref:DialogResult property automatically except in two cases:
The user clicks the dialog box Close (X) button in the upper right corner, which sets the property to the DialogResult:Cancel enumeration value.
The user clicks a button (or any control that implements the System.Windows.Forms.IButtonControl interface) contained by the dialog box whose DialogResult property you have set with a valid DialogResult enumeration value. In this case, .NET automatically sets the dialog-object-ref:DialogResult property to the value of the button property.
Otherwise, your application must set the value of dialog-object-ref:DialogResult directly, typically in an event handler. Note that if you want .NET to automatically set the dialog-object-ref:DialogResult property from a button DialogResult property, your application must initialize the DialogResult value for the button property before the user clicks a given button.
You can detect that a specific form is closing by handling its FormClosing event; you can detect that a specific form has already closed by handling its FormClosed event. However, note that the Closed event does not fire for non-modal .NET forms that you close by calling Application:Exit( ) and that are not specified by the form-object-ref parameter passed to the Application:Run( ) method.
Caution: Do not delete the ABL object reference to the object (sender) that publishes a FormClosing event from within its FormClosing event handler. This causes the FormClosing event to be published a second time. If you need to delete the sender for an event that is associated with closing a form before the AVM garbage collects it, execute the DELETE OBJECT statement for the sender within a handler for its FormClosed event.
Note: .NET supports a Closing and Closed event on forms. However, use the FormClosing and FormClosed events, instead, because they work better.
If you specify a non-blocking method in a WAIT-FOR statement, the method executes and the WAIT-FOR immediately returns, resuming execution with the next available statement. ABL does not prevent this. However, the WAIT-FOR statement, in this case, works no differently than invoking the method as a statement by itself.
Note: Progress Software Corporation recommends that you donot invoke non-blocking methods using the WAIT-FOR statement.
The one WAIT-FOR statement that you execute for non-modal .NET forms after setting their Visible properties to TRUE or by invoking their Show( ) methods, must call the Application:Run( ) method to make the forms visible and usable. If you execute any other form of the WAIT-FOR statement after making non-modal .NET forms visible, such as one that blocks for an ABL event (even a developer event, such as U1 OF THIS-PROCEDURE), the ABL virtual machine (AVM) raises STOP on this WAIT-FOR statement.
You cannot set the Visible property or call the Show( ) method on a modal .NET form before executing a WAIT-FOR statement on the ShowDialog( ) method. If you execute a WAIT-FOR statement that calls the ShowDialog( ) method on a modal .NET form that you have previously made visible, .NET raises a run-time exception.
Once you execute a non-modal WAIT-FOR statement that calls the Application:Run( ) method, the statement goes into a wait state that allows any event handlers or triggers to run in response to all types of ABL-supported events, including .NET events, ABL UI events, and ABL non-UI events (such as socket events). In the associated event handlers or triggers, you can then create and display additional .NET non-modal forms (by setting their Visible properties or invoking their Show( ) methods) or ABL non-modal windows (for example, by setting their VISIBLE attributes to TRUE, executing DISPLAY statements, or executing a VIEW statement), and the existing WAIT-FOR statement processes events associated with these new non-modal .NET forms or ABL windows in addition to any other events it is already processing. To open modal dialog boxes (.NET or ABL) from an event handler or trigger, you must execute an additional WAIT-FOR statement for each dialog box that you open. Each such WAIT-FOR statement then blocks until its associated modal dialog box is closed, allowing the event handler or trigger that invoked the statement to resume execution.
.NET does not support blocking for multiple non-modal forms simultaneously at more than one point in an application. If you attempt to execute more than one non-modal WAIT-FOR statement that calls Application:Run( ) by invoking a second such WAIT-FOR statement in an event handler or trigger, .NET raises an exception, which in turn raises STOP on this second WAIT-FOR statement.
Caution: After you execute an additional WAIT-FOR statement that calls Application:Run( ), even if you trap the STOP condition with ON STOP, .NET does not allow another call to Applicaton:Run( ) in the same session. You must exit the ABL session and fix the application to avoid such simultaneous calls to Applicaton:Run( ).
You must execute a separate WAIT-FOR statement for each modal .NET dialog box or ABL dialog box that you open. The modal nature of each dialog box guarantees that the corresponding WAIT-FOR statements unblock and resume execution with the statements that follow them in order of the most recently opened dialog box. However, while blocking on any .NET or ABL modal dialog box, you cannot then execute a non-modal WAIT-FOR statement calling Application:Run( ), even if no previous non-modal WAIT-FOR statement is still in a wait state. Attempting to call a non-modal WAIT-FOR statement while any modal .NET dialog box or ABL dialog box is open raises a run-time error.
If you add non-modal .NET forms to an existing ABL application that already contains WAIT-FOR statements to process non-modal ABL GUI and non-GUI events, replace these WAIT-FOR statements (except those that block on ABL modal dialog boxes) with a single WAIT-FOR statement that calls Application:Run( ). In order to have an active .NET form in your application, you must execute your latest (and usually only) WAIT-FOR statement in the format that calls a .NET input-blocking method (for example, waiting on Application:Run( )) so that it is in effect to process .NET events. This allows the application to handle the same ABL events after adding .NET forms that it handled before you added the .NET forms.
You cannot use the APPLY statement to force a WAIT-FOR statement that is blocking on .NET forms to unblock and continue execution with the statement following the WAIT-FOR statement. Instead, call the appropriate .NET method (for example, Close( ) or Application:Exit( )) to unblock the WAIT-FOR statement and continue with the next statement.
To handle a particular .NET event while the WAIT-FOR statement that processes the event is executing, you must subscribe a .NET event handler to the specified event using the Subscribe( ) event methodbefore the event is raised. For more information on event methods and event handlers for .NET events, see the ClassEvents Reference.
If an ABL handler for a .NET event raises an unhandled error condition or throws an error object out of the handler, the AVM does not throw a .NET Exception back to the .NET Common Language Runtime (CLR), but displays a message to the default output device and continues blocking for events.
You cannot invoke the non-modal .NET System.Windows.Forms.Application:Run( ) method or the .NET ShowDialog( ) method used to display a modal dialog box in any ABL context other than in a WAIT-FOR statement. Any attempt to do so raises a run-time error.