Try OpenEdge Now
skip to main content
GUI for .NET Programming
Creating and Using Forms and Controls : ABL support for managing .NET forms and controls : Blocking on modal dialog boxes
 

Blocking on modal dialog boxes

When you display a form as a modal dialog box, the user cannot give focus to any other form in your application until they somehow close the dialog box. You can create modal forms from one of the following dialog box classes:
*System.Windows.Forms.Form (or a derived class), especially Progress.Windows.Form) — OpenEdge provides the derived .NET class, Progress.Windows.Form, which is specifically designed to work within an ABL session, allowing you either to create modal dialog boxes directly from it or to create modal dialog boxes using ABL classes that you further derive from it. This class provides the same features for forms displayed as modal dialog boxes that are provided for forms displayed as non-modal forms. In addition, it provides features especially for use by dialog box forms.
*.NET forms derived from the abstract class, System.Windows.Forms.CommonDialogSystem.Windows.Forms.CommonDialog is the base class for a set of special-purpose dialog boxes provided by the .NET Framework, including among others, the System.Windows.Forms.FileDialog class to implement a file selection dialog box, and the System.Windows.Forms.ColorDialog class to implement a color selection dialog box. These classes have features oriented around a specialized use as compared to the richer set of general-purpose features available with the System.Windows.Forms.Form class and its subclasses.
To display and block on a modal dialog box, execute a .NET WAIT-FOR statement that calls the ShowDialog( ) method on the dialog box class instance. This is the syntax for using the .NET WAIT-FOR statement to call the ShowDialog( ) method:

Syntax

WAIT-FOR dialog-object-ref:ShowDialog( [owning-form-ref] )
  [ SET return-value] .
The dialog-object-ref is an object reference to the form class instance you want displayed as a modal dialog box. The owning-form-ref is an object reference to a form that owns the dialog box (that is, a form over which the dialog box displays). Note that you cannot set the dialog-object-ref to be visible by calling its Show( ) method prior to executing this statement, as you might with a non-modal form. If you do, .NET raises an error when you execute this WAIT-FOR statement. The ShowDialog( ) method available on any dialog-object-ref returns a value of type System.Windows.Forms.DialogResult. This is an enumeration type that indicates the result from closing the dialog box. If you want to check this result, you can use the SET option, which sets a variable (return-value) to the value returned by the method. On some dialog form classes, you can also check the DialogResult property (with the same name as its type), which contains the same value.
For Progress.Windows.Form objects, you can set the DialogResult property in an event handler or rely on .NET to set the value from one or more button controls that you add to the dialog box. Every button control also has a DialogResult property, and .NET automatically sets the dialog box property to the value of the DialogResult property of the button that the user clicks in the dialog box. Either way, setting the value of DialogResult on the dialog box form object causes .NET to close the dialog box. You can then check the property value after the dialog box closes following termination of the WAIT-FOR statement.
Caution: Unlike for non-modal forms, when the user clicks the Close (X) 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 on the form. Instead, .NET hides the form so it can be shown again without having to create a new instance of the dialog box. Thus, when a modal form is no longer needed by your application, you must explicitly call the Dispose( ) method on dialog-object-ref (and call it before any invocation of the DELETE OBJECT statement on dialog-object-ref) to ensure that the form and all the .NET controls that it contains are garbage collected. Otherwise, your .NET dialog boxes can create memory leaks in your application.
If you use a Progress.Windows.Form (or another System.Windows.Forms.Form class) instance to create a dialog box, you have access to the full range of methods, properties, and events that are available for this class, including the DialogResult property. This property is not available on System.Windows.Forms.CommonDialog objects. However, you can check the DialogResult value for dialog boxes displayed for a CommonDialog form by using the SET option.
Note: Unlike the single WAIT-FOR statement for displaying all non-modal .NET forms and ABL windows, you can execute a WAIT-FOR statement to display a dialog box (.NET or ABL) anywhere in your application, and in any order that you choose. For more information, see Using .NET Forms with ABL Windows.
The following MixedForms.p procedure defines two non-modal forms (rForm1 and rForm2) and one modal dialog box (rDialog1) that displays when you click on either one of the forms. For information on locating and running this sample, see Example procedures.
USING System.Windows.Forms.* FROM ASSEMBLY.
USING Progress.Util.* FROM ASSEMBLY.

DEFINE VARIABLE rForm1 AS CLASS Progress.Windows.Form NO-UNDO.
DEFINE VARIABLE rForm2 AS CLASS Progress.Windows.Form NO-UNDO.
DEFINE VARIABLE rLabel1 AS CLASS Label NO-UNDO.
DEFINE VARIABLE rLabel2 AS CLASS Label NO-UNDO.

rForm1 = NEW Progress.Windows.Form( ).
rForm2 = NEW Progress.Windows.Form( ).
rLabel1 = NEW Label( ).
rLabel2 = NEW Label( ).

rLabel1:Text = "Click in form to display dialog box...".
rLabel1:Size = TextRenderer:MeasureText( INPUT rLabel1:Text,
                                         INPUT rLabel1:Font ).
rLabel1:Width = rLabel1:Width * 1.1.
rLabel1:Top = 12.
rLabel2:Text = rLabel1:Text.
rLabel2:Size = rLabel1:Size.
rLabel2:Width = rLabel1:Width.
rLabel2:Top = rLabel1:Top.

rForm1:Text = "Form1".
rForm1:Size = NEW System.Drawing.Size( INPUT 300, INPUT 300 ).
rForm1:Controls:Add( INPUT rLabel1 ).
rLabel1:Left = INTEGER((rForm1:ClientSize:Width - rLabel1:Width) / 2).

rForm2:Text = "Form2".
rForm2:Size = rForm1:Size.
rForm2:Controls:Add( INPUT rLabel2 ).
rLabel2:Left = rLabel1:Left.

rForm1:Click:Subscribe("Form_Click").
rForm2:Click:Subscribe("Form_Click").

rForm2:Show( ).

WAIT-FOR Application:Run( rForm1 ).

MESSAGE "End of Application" VIEW-AS ALERT-BOX INFORMATION.

PROCEDURE Form_Click:
  
  DEFINE INPUT PARAMETER sender AS CLASS System.Object NO-UNDO.
  DEFINE INPUT PARAMETER e AS CLASS System.EventArgs NO-UNDO.
  
  DEFINE VARIABLE rDialog1 AS CLASS Progress.Windows.Form NO-UNDO.
  DEFINE VARIABLE rCloseButton AS CLASS Button NO-UNDO.
  DEFINE VARIABLE rCancelButton AS CLASS Button NO-UNDO.
  DEFINE VARIABLE enDialogResult AS CLASS DialogResult NO-UNDO.
  
  rDialog1 = NEW Progress.Windows.Form( ).
  rCloseButton = NEW Button( ).
  rCancelButton = NEW Button( ).
  
  rCloseButton:Text = "Close Form1".
  rCloseButton:Size = TextRenderer:MeasureText
                      ( INPUT "Close Form1", INPUT rCloseButton:Font ).
  rCloseButton:Width = INTEGER(rCloseButton:Width * 1.5).
  rCloseButton:Height = INTEGER(rCloseButton:Height * 1.5).
  rCloseButton:DialogResult = DialogResult:OK.

  rCancelButton:Text = "Cancel".
  rCancelButton:Size = TextRenderer:MeasureText
                       ( INPUT "Cancel", INPUT rCancelButton:Font ).
  rCancelButton:Width = INTEGER(rCancelButton:Width * 1.5).
  rCancelButton:Height = INTEGER(rCancelButton:Height * 1.5).
  rCancelButton:DialogResult = DialogResult:Cancel.
  
  rDialog1:Text = "Dialog1".
  rDialog1:FormBorderStyle = FormBorderStyle:FixedDialog.  
rDialog1:ClientSize = NEW System.Drawing.Size
    ( INPUT (rCloseButton:Width + rCancelButton:Width) * 2,
      INPUT (rCloseButton:Height + rCancelButton:Height) * 2 ).
  rDialog1:Controls:Add( INPUT rCloseButton ).
  rDialog1:Controls:Add( INPUT rCancelButton ).
  
  rCloseButton:Location = NEW System.Drawing.Point
    ( INPUT INTEGER( ( rDialog1:ClientSize:Width
                       - (rCloseButton:Width + rCancelButton:Width + 4) )
                     / 2 ),
      INPUT INTEGER( (rDialog1:ClientSize:Height - rCloseButton:Height)
                     / 2 ) ).
  rCancelButton:Location = NEW System.Drawing.Point
    ( INPUT rCloseButton:Location:X + rCloseButton:Width + 4,
      INPUT rCloseButton:Location:Y ).
  
  WAIT-FOR rDialog1:ShowDialog( ) SET enDialogResult.  
  IF EnumHelper:AreEqual( INPUT enDialogResult,
                          INPUT DialogResult:OK )
  THEN rForm1:Close( ).

  rDialog1:Dispose( ).
  
END PROCEDURE.
In MixedForms.p, the Form_Click event handler defines and manages the dialog box and its System.Windows.Forms.Button controls (rCloseButton and rCancelButton). The handler sets the DialogResult property of each button to a different DialogResult enumeration value. It also sizes the client area (ClientSize property) of the dialog box based on the sizes of these buttons. It then adds and centers the buttons in the dialog box client area based on these dimensions. Note also the use of the FormBorderStyle property to specify a fixed dialog style from the System.Windows.Forms.FormBorderStyle enumeration.
The ShowDialog( ) method called in the WAIT-FOR statement then blocks and displays the dialog box. If the user clicks the rCloseButton control, the rCancelButton control, or the Close (X) button on the title bar, the dialog box closes with the DialogResult property on rDialog1 set to the clicked button's DialogResult property value or to DialogResult:Cancel for the Close (X) button.
After ShowDialog( ) returns, the WAIT-FOR statement terminates and sets enDialogResult with the method return value (which is also the value of the DialogResult property). The event handler then checks the value of enDialogResult using the static AreEqual( ) method of the Progress.Util.EnumHelper class. If the value is DialogResult:OK, the handler calls the Close( ) method on Form1. This causes the Application:Run( ) method to return from the non-modal WAIT-FOR statement in the main block, terminating the procedure. If the value is other than DialogResult:OK, the event handler assumes that the dialog box was cancelled, and closes, allowing the non-modal forms to continue being displayed. Note also that the handler calls Dispose( ) on rDialog1 to ensure that the dialog box is garbage collected.
For the System.Windows.Forms.CommonDialog classes, you can use the ShowDialog( ) method in much the same way as for Progress.Windows.Form. However, each CommonDialog class also provides its own set of class members built on an inheritance hierarchy based on CommonDialog. These dialog boxes do not provide DialogResult and many other properties provided by System.Windows.Forms.Form, including a Controls property to add controls. Befitting their application orientation, these dialog boxes are standardized for special-purpose use and manage all of the controls that they use as a built-in feature of each CommonDialog class.
Because there is no DialogResult property on these dialog boxes, to test the result of a CommonDialog object, you must use one of the following mechanisms:
*Use the SET option on the WAIT-FOR statement, as noted previously, to return the DialogResult value from ShowDialog( ).
*Query a property on the given CommonDialog object that changes based on the result.
*Use an event handler on a suitable subclass event, such as the FileOk event on the OpenFileDialog subclass, to handle the results.