Farcalling rundialog with parameters


#1

I have the following code will farcall a procedure that contains “rundialog”. The form that is used has a search field, a text list object that has data coming from another database of contacts with the option of selected value set to a variable called vSelected.

Works great when I was not using farcall but just running the procedure from one database. When using farcall I can not get the vSelected variable to show a value. I have tried so many different tweaks to the procedure I think I have lost all of my ability to think clearly about why it is not working. I have tried passing parameter to the “SearchStuff” procedure, then using the setparameter to return a value of vSelected. Nothing, I have reversed the order, Nothing, I have put messages at every step and I just can’t figure this out.

With all that said, I am sure it will be something simple and obvious. Here is the basic code. I have left off all the iterations. The end result I am looking for is to have one place to store about 12 different search forms, call those from many different databases and return the selected record to the calling database.

farcall "Database","SearchStuff"

The “SearchStuff” procedure runs this code

   fileglobal myDialogButton
   myDialogButton=""
loop
    rundialog {Form="Search People" Sheet=TRUE Height=525 Width=425 OkButton="Select" OkButton="Select" autoedit="SearchField" variabletype=fileglobal}
    stoploopif info("trigger")="Dialog.Close"

    if info("trigger") = "TextDisplay.NewProspect"
        myDialogButton=info("trigger")
        closeactiveobject
        settrigger "Dialog.OK"
    endif

endloop

#2

You’re not showing the part with vSelected, but I’d immediately suspect the fact that you’re using FileGlobals while working with multiple files.

Your use of OkButton=“Select” OkButton=“Select” probably doesn’t help help either.


#3

Hi James,

vSelected is the variable associated with a text list object, Object Properties Panel, Selected value - variable. I just changed all the variables that I have control over to local, when I do that I get the “variable does not exist” message.


#4

vSelected was probably automatically set as a fileglobal variable when you created the text list object. Jim was suggesting that you change this variable to a global for use across databases.

undefine vSelected
global vSelected```

This will remove the vSelected fileglobal and create a vSelected global variable in place of it. Be sure to also add code defining vSelected a global variable in your .Initialize procedure as well or you will recreate it as a fileglobal next time you open the file containing the text list object.


#5

Hi Gary,

It is interesting that it all works fine when the it is all in the same procedure. It only starts the weirdness when farcalling. I will try your suggestion and let you know.


#6

Opening a dialog across multiple databases is certainly possible, pretty much all of Panorama’s dialogs work that way. But handling variable scope is tricky.

One mistake that is commonly made is thinking that farcall switches the active database. It doesn’t.

With the code snippet you have published, I don’t see how the dialog can appear at all. Panorama is still running with the original database active. I assume that database doesn’t contain a form named Search People. So it should stop without even opening the dialog. Since you are not claiming that, I assume you have left out some key element of the code. You also talk about “return a value of vSelected”, but there is no reference to vSelected in your code.

There are several ways to make cross database code like this work. Here is one possibility. (From your code, it appears that the database that contains the procedure SearchStuff is called Database, which is an odd name to choose, but I’ll do the same.

setactivedatabase "Database"
fileglobal vSelect
vSelect=somelocalvariable
call "SearchStuff"

Keep in mind that in a single statement, you can only reference fileglobal variables from the current database. So if variable is a database in one database, and vb is a variable in another database, you cannot say

va=vb

or

va+vb

There are special functions that allow this, for example

va=fileglobalvalue("databaseB",vb)

This topic was covered in depth in one of the sessions, you should probably carefully review it.

Accessing Fields & Variables


#7

You have definitely identified an area I really struggle with. Scope always confuddles my brain. I have moments, rare moments of clarity, then the demands of my real job, the one that pays the bills, will distract me and when I come back it, the clarity is just gone. :slight_smile:

Basically what I am doing is have a single database hole the code for 12 sheet search forms. Like how your search interface works but with a single search field and predetermined search criteria. Here is the fully disclosed code that works with out far calls.

fileglobal myDialogButton
myDialogButton=""

loop
rundialog {Form=“Search People” Sheet=TRUE Height=525 Width=425 OkButton=“Select” OkButton=“Select” autoedit=“SearchField” variabletype=fileglobal}
stoploopif info(“trigger”)=“Dialog.Close”

if info("trigger") = "TextDisplay.NewProspect"
    myDialogButton=info("trigger")
    closeactiveobject
    settrigger "Dialog.OK"
endif

endloop

if dlgResult=“Ok” and myDialogButton notcontains "TextDisplay"
ContactIDNumber=Arraylast(vSelectedPerson,tab())
First=lookup(“mPeople”, ID, val(Arraylast(vSelectedPerson,tab())), First, “”)
Last=lookupmoredata(Last, “”)
OrgName=lookupmoredata(OrgName, “”)
eMail=lookupmoredata(eMail, “”)
PhoneDesc1=lookupmoredata(PhoneDesc1, “”)
Phone1=lookupmoredata(Phone1, “”)
PhoneDesc2=lookupmoredata(PhoneDesc1, “”)
Phone2=lookupmoredata(Phone1, “”)
endif

if myDialogButton contains "TextDisplay"
ContactIDNumber="New Prospect"
endif


#8

Your form is very nice :slight_smile: It’s cool to see people do stylish things with Panorama X.

I still don’t see how your dialog can ever open if you call it with a farcall. You need to add a database= option to the parameters of the rundialog. Also, as someone mentioned above, you are defining OKButton= twice, which won’t hurt anything but might confuse things if you ever change one of them.

The big problem is that the code below the bottom of the loop runs after the dialog window has closed. So if you are calling from another database, the vSelectedPerson variable is no longer in scope. It belongs to the database that contains the dialog, not the calling database.

One solution is to use the fileglobalvalue( function. This will work as long as the database that contains the dialog is still open, even though the dialog window has closed.

if dlgResult="Ok" and myDialogButton notcontains "TextDisplay"
    ContactIDNumber=Arraylast(fileglobalvalue("DialogDatabase",vSelectedPerson),tab())
    First=lookup("mPeople", ID, val(Arraylast(fileglobalvalue("DialogDatabase",vSelectedPerson),tab())), First, "")

Instead of using this function over and over, you might want to copy the value into a local variable once, and then use that, like this:

if dlgResult="Ok" and myDialogButton notcontains "TextDisplay"
    local targetPerson
    targetPerson=fileglobalvalue("DialogDatabase",vSelectedPerson)
    ContactIDNumber=Arraylast(targetPerson,tab())
    First=lookup("mPeople", ID, val(Arraylast(targetPerson,tab())), First, "")

There is another way to do this by checking for the Ok button in the loop and stashing the vSelectedPerson value when the button is pressed, in either a local or a global variable. But because the dialog window closes, you cannot access the same fileglobal variables inside the loop as you do outside the loop.


#9

The one I posted was not designed for a far call. It is just a procedure in the same database it displays.


#10

Well I was explaining why it wouldn’t work as a farcall, and how it would need to be changed to make that work. Isn’t that what your original question was?


#11

Actually yes you are totally correct I mis read your email.


#12

Hi Jim,

I tried your suggestion, I put the dialog form in a database called mNet along with this rundialog code.

fileglobal myDialogButton
myDialogButton=""

loop
    rundialog {Form="Search People" Sheet=TRUE Height=525 Width=425 OkButton="Select" autoedit="SearchField" variabletype=fileglobal}
    stoploopif info("trigger")="Dialog.Close"

    if info("trigger") = "TextDisplay.NewProspect"
        myDialogButton=info("trigger")
        closeactiveobject
        settrigger "Dialog.OK"
    endif
endloop

I put this code in the calling database.

farcall "mNet","SearchPeople"

if dlgResult="Ok" and myDialogButton notcontains "TextDisplay"
    local targetPerson
    targetPerson=fileglobalvalue("mNet",vSelectedPerson)
    ContactIDNumber=Arraylast(targetPerson,tab())    
    First=lookup("mPeople", ID, val(Arraylast(vSelectedPerson,tab())), First, "")
endif

This is generating the following error "Variable [Steve Middleton Middleton Printing 1667] does not exist."
It should return the value “1667”


#13

If you indent your code by 4 spaces when putting it in a post, it will format as code and be 100% more readable.

You are continuing to make the same mistake. You can’t use vSelectedPerson in this section of the code without using the fileglobalvalue( function.

First=lookup("mPeople", ID, val(Arraylast(vSelectedPerson,tab())), First, "")

However, since you’ve already extracted this into the targetPerson variable, you should be able to simply change this line to use that variable.

First=lookup("mPeople", ID, val(Arraylast(targetPerson,tab())), First, "")

Now, a couple of other notes about your code. On this line, why do you have the second parameter of "SearchPeople"? This parameter is never getting used, so it is pointless.

farcall "mNet","SearchPeople"

Maybe you wanted to specify the form to use. In that case, you should change the code in the called procedure to use the parameter, perhaps like this:

rundialog {Form=}+quoted(parameter(1)+{ Sheet=TRUE Height=525 Width=425 OkButton="Select" autoedit="SearchField" variabletype=fileglobal}

Oh wait – SearchPeople must be the name of your procedure. Sorry – I’ll leave this in since I think it is useful.


Ok, I see more problems. This still isn’t going to work as a farcall. First of all, rundialog is going to try to open a form named “Search People” in the original database, when I assume that form is in the mNet database. Secondly, you are using the fileglobal myDialogButton in two databases – you can’t do that!! So if you ever click on the TextDisplay object, you’ll get an error. Well, you would, except that the dialog will never open!!

Also, I think there is no reason to put the dlgResult=“OK” code in the original database.


Ok, here is a rewrite of your code. I haven’t tested it obviously, but it does compile without errors. I used the info(“proceduredatabase”) function instead of a constant value like "mNet", that way if you change the database name later it will still work.

setactivedatabase info("proceduredatabase")
fileglobal myDialogButton
myDialogButton=""

loop
rundialog {Form=}+quoted(parameter(1))+{ Sheet=TRUE Height=525 Width=425 OkButton="Select" autoedit="SearchField" variabletype=fileglobal}
stoploopif info("trigger")="Dialog.Close"

if info("trigger") = "TextDisplay.NewProspect"
	myDialogButton=info("trigger")
	closeactiveobject
	settrigger "Dialog.OK"
endif
endloop

if dlgResult="Ok" and fileglobalvalue(info("proceduredatabase"),"myDialogButton") notcontains "TextDisplay"
	local targetPerson
	targetPerson=fileglobalvalue(info("proceduredatabase"),"vSelectedPerson")
	ContactIDNumber=Arraylast(targetPerson,tab()) 
	First=lookup("mPeople", ID, val(Arraylast(targetPerson,tab())), First, "")
endif

You can call this from any database like this:

farcall "mNet","SearchPeople","SearchPeople"

If you have different dialog forms you could use them by changing the third parameter.


#14

I’m confused.

Why can a identically named fileGlobal not be used in 2 databases? In each case, isn’t its scope the single database that it is being declared in?

Robert Ameeti


#15

I was making a shortcut because that was already a long post.

Of course what @RAmeeti says is correct – in fact, Robert has succinctly summarized the purpose of scope and fileglobal variables. But @goMiddleton was declaring the fileglobal in one database, but then using it in two databases, and the way he was using it it was clear he meant for the value to be set when one database was active but then accessed when another database was active, which is a no-no. In other words, Steve’s code was written such that the scope of his myDialogButton variable would have to extend across 2 databases.


#16

Hi Jim,

Thank you for the indent suggestion. I used your code as is and it works, but I am not sure why.

setactivedatabase info(“proceduredatabase”)

This code switches to the database that contains the form and the procedure but not the fields that the lookups fill in.

if dlgResult=“Ok” and fileglobalvalue(info(“proceduredatabase”),“myDialogButton”) notcontains "TextDisplay"
local targetPerson
targetPerson=fileglobalvalue(info(“proceduredatabase”),“vSelectedPerson”)
ContactIDNumber=Arraylast(targetPerson,tab())
First=lookup(“mPeople”, ID, val(Arraylast(targetPerson,tab())), First, “”)
endif

How does this code know to switch back to the calling database and fill in the fields with the lookups?


#17

SetActiveDatabase is basically the same as using a secret window. It lasts until a real window is brought to the front. Jim used it prior to the RunDialog statement, because he wanted to define his FileGlobal in the procedure database, before he opened the dialog. Before the dialog is opened, the calling database would normally still be active.

When the dialog does open, SetActiveDatabase is no longer in effect, but the procedure database is still active because it has the active window. When the dialog window closes, the window belonging to your calling database is now the one in front, and now that database is the active database. vSelectedPerson is now out of scope, and that is why he had to use the fileglobalvalue( function to get its value, when he assigned it to targetPerson.


#18

Thank you Dave, that makes sense, mostly.

First question, what if the procedure database, “mNet” is already secret?

Here is what I understand, I think.
“Estimates” is where the procedure starts and “mNet” is the target of the farcall.

1 - Database “Estimates” initiates the farcall, so it is the “calling database” and is still the active database
2 - setactivedatabase info(“proceduredatabase”) makes the database “mNet” active
3 - With the mNet database still active the fileglobals myDialogButton is defined and glued to mNet.
4 - When the rundialog code is executed it magically switches back to “Estimates” for display but the code running is still from mNet.
5 - When the dialog closes Estimates is still the display database and some how the rest of the code in mNet now finishes just like if it all was done in Estimates. Thats why the lookups work correctly.

How does rundialog, in essence, do a SetActiveDatabase(“Estimates”)? Is this a side effect of rundialog?
Also, it seems that after the code runs, after the farcall from Estimates, I can’t run any other code. I put a simple message “Here” after the farcall and it does not run.


#19

SetActiveDatabase doesn’t make the database secret. It just secretly makes it active. In other words, it makes it active without visibly putting one of its windows in front. There may or may not be any windows open, but the database will have to be open, possibly from an OpenSecret.

At this point, mNet will still be active, because that dialog window belongs to it, and that window is in front.

When the dialog window closes, mNet no longer has the active window, because it just closed. The window that was behind it is now active, and that window belongs to Estimates, so Estimates is now active. The lookups work correctly, because they don’t take place until Estimates is active.

It doesn’t really matter what database is used to store the code of a procedure. The code always runs in the active database. If a procedure opens windows in one database, and then another, the code will be running in one database, and then another. It will run in whichever database is active at any given moment.