What is the ENDDIALOG event?

If you’ve been using OpenInsight 10 one of the new features you may have noticed is that forms now have a new event called ENDDIALOG, and in this post we’ll take a brief look at how to use it.

The downsides of modal Dialog_Box programming

There are two primary issues with the classic Dialog_Box/End_Dialog style of programming:

  1. The system has to wait in a loop until the dialog is dismissed, and
  2. This forms a “stack” where the dialogs can only be closed in the order in which they were opened.

Historically the first issue was a bigger problem due to the fact that it tied up the engine thread and maxed out the CPU core it was running on.  This was resolved by moving the dialog “wait loop” into the Presentation Server in version 8, where it could be managed better and the engine thread would effectively “sleep” without consuming resources (prior to this it was a simple Basic+ For/Next loop – very processor intensive).

The second issue still remained however. While it may initially seem logical that modal dialogs should form a “stack” this actually breaks down when they are used from different top-level windows.

For example, suppose I have two instances of the IDE, and I load a modal dialog from the first instance and then another from the second instance.  I can’t close the first before I close the second as the Dialog_Box calls are stacked “inside” the engine.  This is potentially confusing behavior and likely to become a bigger issue if your development moves away from single MDI frames and multiple-monitor setups become more prevalent.

Dialog_Box programming without stacking

The only real way to solve this stacking problem is to break away from the current model of “synchronous” Dialog_Box programming and adopt an “asynchronous” callback model instead.  With this methodology there is no looping at all so the CPU usage stays low and code is only executed as needed, and because the loop is removed there is no stacking either.

The drawback of this approach, however, is that it complicates programming because your code has to be split up into two different sections; one to execute the dialog, and another to respond to the return value.

E.g. Consider a CLICK event handler using the normal approach:

OnClick:
   ... <prepare dialog args> ...
   RetVal = Dialog_Box( "MYDIALOG", DlgParent, DlgParam )
   ... <process RetVal> ...    
Return

… all nice and simple. To do this in an asynchronous fashion would take something like this:

   OnClick:
      ... <prepare dialog args> ...
      Call Dialog_Box( "MYDIALOG", DlgParent, DlgParam  )
   return
   

   OnSomeCallbackEvent:
      // param1 -> DialogID
      // param2 -> RetVal
      Begin Case
         Case ( Param1 == "MYDIALOG" )
            ... <process param2 (RetVal) > ...  
      End Case
   Return

… which is a little more complex.

This approach can actually be taken in any version of OpenInsight, but there’s no framework in place to enforce it and so it ends up needing even more work as the developer needs to find a way to define and target the callback process (E.g. implementing a custom event, using OMNIEVENT, calling a commuter module directly, and so on).

For version 10 we decided to provide that framework so that your code could be structured in a standardized fashion, resulting in some updates to the Dialog_Box and End_Dialog stored procedures and the addition of a new WINDOW event called ENDDIALOG.

Changes to Dialog_Box

Dialog_Box now accepts a new dynamic array argument called “AsyncParams”:

RetVal = Dialog_Box( DialogID, DlgParent, CreateParam, DlgOptions, AsyncParams )

Where AsyncParams is:

<1> An Async flag, to denote it needs to send it's return value to 
    the parent window's ENDDIALOG event

<2> A "cookie" value, which is a simple string passed to the ENDDIALOG 
    event that can be used to identify the returning dialog

e.g.

   AsyncParams    = TRUE$
   AsyncParams<2> = "wibble"

   RetVal = Dialog_Box( "MYDIALOG", DlgParent, DlgParam, "", AsyncParams )

Changes to End_Dialog

This procedure now checks to see if the dialog is in asynchronous mode, and if so it takes the dialog ID, the return value, and the cookie, and sends them to the parent’s ENDDIALOG event, thereby giving us a properly defined framework to implement the callback process.

(Note that the “parent” is considered to be the parent as specified in the originating Dialog_Box call, not the actual runtime PARENT, as these may not be the same thing!).

The new ENDDIALOG event

This event is called from the End_Dialog procedure and accepts three arguments:

DialogID  - name of the dialog that triggered the callback

ReturnVal - the dialog return value passed to End_Dialog

AsyncID   - the cookie value that was passed to Dialog_Box

Example

For the purposes of this example we assume that we are going to launch a simple form called “MY_DIALOG_BOX” using the Dialog_Box function.  The form contains a single EDITLINE control called “EDL_NAME”, and a button called “BTN_OK”.  When BTN_OK is clicked it will get the text from EDL_NAME and return it to the owner with an End_Dialog call like so:

   // CLICK event script for MY_DIALOG_BOX.BTN_OK

   // Get the data the user entered
   Name = Get_Property( @Window : ".EDL_NAME", "TEXT" )
   
   // Return it to the owner window
   Call End_Dialog( @Window, Name )

To launch MY_DIALOG_BOX in asynchronous fashion, do the following from an event on the owner window:

// Launches MY_DIALOG_BOX as a modal Dialog_Box in asynchronous fashion 
// passing it the contents of a variable called CurrName as the CreateParam.
   
AsyncParams = ""
AsyncParams<1> = TRUE$      ; // Async mode
AsyncParams<2> = "GetName"  ; // Optional “AsyncID” param for the 
                            ; // ENDDIALOG event
   
// This code does not halt here - anything the user selects in the dialog
// will be passed back in the ENDDIALOG event 
DlgID = Dialog_Box( "MY_DIALOG_BOX", @Window, CurrName, "", AsyncParams )

In this mode the calling program will NOT halt at the Dialog_Box call and wait for the user to close it. Instead any data returned from the End_Dialog call on MY_DIALOG_BOX.BTN_OK will be passed as an argument to the calling window’s ENDDIALOG event.

The ENDDIALOG event on the parent form would look something like this:

Function ENDDIALOG( CtrlEntID, CtrlClassID, DialogID, DialogValue, AsyncID )
 
// This is an ENDDIALOG event that will be triggered by the End_Dialog 
// call on MY_DIALOG_BOX.BTN_OK when launched in asynchronous mode,
//
// ENDDIALOG is passed three parameters:
//
//   DialogID 
//   DialogValue
//   AsyncID
   
Begin Case
   Case ( DialogID = "MY_DIALOG_BOX" )
      // This is optional but we can check AsyncID if we wanted to have
      //  more fine grain control over how this event is processed.
      If ( AsyncID == "GetName" ) Then
         Call Do_Something_With_This_Name( NewName )
      End
End Case 

Return 0

More details on using the ENDDIALOG event can be found in the online help for the Dialog_Box and End_Dialog procedures in version 10.

2 thoughts on “What is the ENDDIALOG event?

  1. Don Bakke

    Two things. First, I remember this being explained in a conference and I’m glad that this is baked into the framework of OI 10. It should help OI developers design with better C/S architecture and help them with other projects, like web/mobile systems. Second, did End_Dialog actually become a function (versus subroutine) or are you using the term function in a broader sense?

    Reply
    1. Captain C Post author

      The term “Function” is used in a broader sense – End_Dialog is still a Basic+ subroutine. I’ll change it “Stored Procedure” for clarity.

      Reply

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s