(Windows only; GUI for .NET only)
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.
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.
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).
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 for the method specified by method-name.
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.
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 buttons */ rOKButton:Text = "OK". rOKButton:Size = NEW System.Drawing.Size( INPUT 60, INPUT 20). rOKButton:Location = NEW System.Drawing.Point ( INPUT INTEGER( ( rDialogDescr:Width - 124 ) / 2 ), INPUT rDialogDescr:Top + rDialogDescr:Height + 8 ). rOKButton:DialogResult = DialogResult:OK. rCancelButton:Text = "Cancel". rCancelButton:Size = NEW System.Drawing.Size( INPUT 60, INPUT 20 ). rCancelButton:Location = NEW System.Drawing.Point ( INPUT rOKButton:Left + rOKButton:Width + 4, INPUT rDialogDescr:Top + rDialogDescr:Height + 8 ). rCancelButton:DialogResult = DialogResult:Cancel. /* 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. rDialog:Dispose( ). /* Dispose modal form object */ 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. The Dispose( ) method call at the end of the event handler is required to allow the modal form object to be garbage collected (see the notes).
To instantiate r-WaitForms and display the non-modal form, you can thus run a procedure with code like this:
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:
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.
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.
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.
With dialog-object-ref:ShowDialog( ), the WAIT-FOR statement unblocks and continues execution with the following statement if one of the following actions occurs:
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.
Unlike for non-modal forms, when the user clicks the CloseX) button on a dialog box, or when you set the value of the dialog-object-ref:DialogResult property, the .NET Framework does not automatically call the Close( ) method on dialog-object-ref and therefore does not also call the Dispose( ) method. Instead, .NET hides the form so it can be shown again without creating a new instance of the dialog box. Because of this behavior, when the form is no longer needed by your application, you must call the dialog-object-ref:Dispose( ) method to enable garbage collection for the form and all the .NET controls that it contains.
If the form contains any ABL-derived controls (including any ABL-derived control containers, such as user controls), those controls will also not be garbage collected until you call Dispose( ), because the form itself is still holding a reference to them. Thus, calling Dispose( ) on the modal form also causes Dispose( ) to be called on these ABL-derived controls, which enables them for garbage collection, again, as long as there are no other references to them in the ABL session.
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).
.NET does not set the dialog-object-ref:DialogResult property automatically except in two cases:
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.