Communication between views and dialogs
by
, 18-Aug-2009 at 11:09 AM (4899 Views)
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:
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.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
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
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.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
We will create our dialog in a file named MyFileSelector.dg:
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.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
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:
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: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
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.