View RSS Feed

Development Team Blog

Communication between views and dialogs

Rate this Entry
A question was asked on the forum about the best way for a view and a dialog to communicate.
What is the best way to pass data back and forth between views and dialogs?

Lets say I have a custom dialog (not a lookup) that I want to use to get information from the user. Maybe there is more than one item of data that need to be returned, like an X and a Y coordinate.

How do I make it so that multiple views in my program can query this dialog and get the information back from it? Or make it so that it sends the data back to the view that called it?

How would you make it so that it could be used as a Prompt object for a form, without having to do any extra coding each time it is used?

If you’ve looked at our selection-list dialogs you know this can be done and this might seem like the logical place to start for creating your own custom dialogs. Before doing this, I’d suggest taking a different approach. If you are willing to write a little bit of extra code each time you use it, you can create a solution that is easier to design, build and use.

Any time I need to call a modal dialog I tend do the same things.

1. I call some method in my object that performs the task and I associate that method with a button or key press. For example, I might associate a prompt button and the F4 key with the Prompt message.

2. Inside the method, I gather all of the information I need to perform the task and make a function call to a well-named method in some other dialog object that performs the task.

3. The function within the dialog object will do whatever it needs to do and return with whatever information I need.

4. I will update my view as needed with this returned information.

At this stage of my planning, I don’t know how this other dialog object will work. It may not even be a dialog object. At this stage I don’t care.

For example, let’s say I wanted to use a prompt button to pop up a dialog that allows me to select a file name. I want to pass the current file name (the value in the form) and return the new file name plus a flag that tells me if the dialog was canceled. Finally I want to update my form with the new name. Since Form objects automatically support a Prompt event our code might look something like this:
Code:
Object oMyFileName is a Form
   Set Size to 13 100
   Set Location to 26 16
   Set Label to "FileName:"
   Set Prompt_Button_Mode to PB_PromptOn
 
   Procedure Prompt
       Boolean bOk
       String sOldName sNewName
       Get Value to sOldName
       Get SelectFileName of oMyFileSelector sOldName (&sNewName) to bOk
       If bOk Begin
            Set Value to sNewName
       End
   End_Procedure
 
End_Object
Notice that the second parameter is passed by-reference and it is used only as a return value. From a design point of view, I am passing one parameter (the data required for the process to do its job) and returning two parameters (the data required to update the view and a Boolean indicating if the process was successful). The code inside the Prompt method is the code you might have to duplicate each time you need call the dialog. Notice that the code is easy to understand - nice. Notice that the code is easy to debug – even nicer.

What do you do if you need to send or receive more complicated data? Create a struct and pass or return struct variables as needed. In the following example, we pass a struct containing three pieces of data to the dialog
Code:
Struct tFileInfo
   String sFile
   String sDirectory
   String sDrive
End_Struct
 
:
 
Procedure Prompt
   Boolean bOk
   tFileInfo FileData
   String sNewName
   Get Value to FileData.sFile
   Get Value of oDirectory  to FileData.sDirectory
   Get Value of oDrive to FileData.sDrive
   Get SelectFileName of oMyFileSelector FileData (&sNewName) to bOk
   If bOk Begin
        Set Value to sNewName
   End
End_Procedure
From the standpoint of our view, we are done. We really don’t care how the dialog works as long as it does what it needs to do. In fact, we could continue designing our view and worry about the minor detail of creating the dialog later. At some point, however, we will need to build the dialog, so let’s do it now.

We will create our dialog in a file named MyFileSelector.dg:
Code:
Use Windows.pkg
Use File_dlg.pkg
 
Object oMyFileSelector is a cObject
 
   Function SelectFileName String sOldName String ByRef sName Returns Boolean
       Handle hoDialog
       Boolean bOk
 
       Get Create U_OpenDialog to hoDialog
       Set File_Title of hoDialog to sOldName
       Set ShowFileTitle_State of hoDialog to True
       Get Show_Dialog of hoDialog to bOk
       If bOk Begin
            Get File_Name of hoDialog to sName
       End
       Send Destroy of hoDialog
       Function_Return bOk             
   End_Function
 
End_Object
I'll bet that this was not what you were expecting. You were expecting (and maybe hoping) that I would show how to use this to call a custom modal dialog. This sample just invokes the windows open file dialog. I will show how to create an actual custom modal dialog but it is important to note that from the view's point of view, it doesn't matter how the dialog works.

Now we will create our own custom modal dialog. It will do something completely different but as long as the interface is maintained it does not matter. So let's change MyFileSelector.dg as follows:
Code:
Use Windows.pkg
 
Object oMyFileSelector2 is a ModalPanel
   Set Size to 89 211
   Set Location to 2 2
   Set Label to "A Truly Useless File Selector"
   // this is used to determine if the modal panel was approved or Canceled.
   Property Boolean pbOk False
 
   Object oName is a Form
       Set Size to 13 131
       Set Location to 29 76
       Set Label to "Enter a File name:"
   End_Object
 
   Object oOK_Btn is a Button
       Set Label    to "&OK"
       Set Location to 71 102
 
       Procedure OnClick
           // before closing the panel, mark this as a success
           Set pbOk to True
           Send Close_Panel
       End_Procedure
 
   End_Object
 
   Object oCancel_Btn is a Button
       Set Label    to "&Cancel"
       Set Location to 71 157
 
       Procedure OnClick
           Send Close_Panel
       End_Procedure
 
   End_Object
 
   On_Key Key_Alt+Key_O Send KeyAction of oOK_Btn
   On_Key Key_Alt+Key_C Send KeyAction of oCancel_Btn
 
   // SelectFileName is the public access method used to invoke and process this dialog
 
   Function SelectFileName String sOldName String ByRef sName Returns Boolean
       Boolean bOk
 
       Set pbOk to False
       Set Value of oName to sOldName
       // this will invoke the modal dialog
       Send Popup
       // when the dialog is complete control will return here
       Get pbOk to bOk
       If bOk Begin
           Get Value of oName to sName
       End
       Function_Return bOk
   End_Function
 
End_Object
Ok, so maybe this dialog is not all that useful because you are asked to enter the same name you could have typed in the view's form. As an example however it is very useful. It contains the basic pieces you will use over and over when creating custom modal dialogs. Here are the pieces that will be common to all dialogs:

1. The dialog is accessed using a high level custom message like SelectFileName and not a low level message like Popup. From an outsider’s perspective this is easy to use because the name and parameters tell you exactly how this is used. Often a dialog will only have one external access point but you could create as many as you want. This makes it easy to extend this dialog while maintaining the existing interface for compatibility reasons.

2. A property (pbOk) is created that determines if the modal dialog was canceled. Before activating the dialog this will be set to false. If the dialog completes properly, in this case by pressing the Ok button, the property must be set to True.

3. It is the job of the access method to initialize the object (set pbOk to false and set any data based on the data passed into the function), invoke the dialog, and to assemble and return the appropriate status and data.

We use this technique all the time in the Studio and I would expect that you should find the same technique very useful in your applications. It may mean that you need to write a little extra code in each object that uses the dialog but realistically that usually involves copy and paste. If you try to create a more automatic technique like we did with our lookup lists you will probably end up using callbacks where you would write the same amount of code anyway. If I were designing this, I’d start with the simpler technique described here.
Categories
Uncategorized

Comments

  1. Bob Worsley's Avatar
    John - Interesting way of doing this, but I note that you didn't address deferred creation of the dialog. Is there a reason other than KISS for the blog? I'm wondering if with today's speedy hardware and gobs of memory is it becomming obsolete? Or you are maybe reserving that for a future blog? Having cut my OOP teeth back when we were doing create/destroy with objects inside procedures, I almost always make my dialogs and views CD and I think that might have been one of the points of the original question - how to pass stuff when it's deferred.
  2. Peter Crook's Avatar
    Of course, you can't use a deferred object when using this technique.

    Peter
  3. Jake Moffatt's Avatar
    John,

    Thank you for responding in such a thorough way. This answer really makes sense to me and gets to the why and not just the how.

    Thanks,

    Jake