Returning values from dialog-like forms

  • Thread starter Thread starter Maury Markowitz
  • Start date Start date
M

Maury Markowitz

I have a number of instances where I'm using InputBox or MsgBox to display a
modal question/answer dialog. However I'd really like to make these much more
customized, using Access forms that I can control with code in the form. But
I'm at a loss how to set up and return values from such a form.

For instance, my app stores e-mail addresses for our suppliers in
"tblContacts". Currently when someone sends an order to the back office for
processing, it looks to see if there's an address in tblContacts for that
supplier, and if so, displays an InputBox with that address asking if the
user would like to send a confirm e-mail to them.

But what I'd really like to do is have more than one address, and let the
user pick one or more (check boxes would be good). That would make the system
much easier to use, and I can even do things like apply logic to select the
proper default address on a per-order basis (ie, orders for widgets go to
Bob, thingamabobs to Jim).

But there's two problems:

1) when you use Modal you loose control over the code and can't poke values
into the form from the opener (I think)

2) I can't figure out how to easily return values

Ideally I'd like to do something as simple as...

ret = OpenMyBox("Bongo Inc.")

Maury
 
Maury:

Taking your example as the start point you can include an unbound
multi-select list box on the dialogue form which will all the email addresses
for the contact in question. The list box can be restricted to the addresses
for contact in question in a number of ways:

1. You could bind the dialogue form to the Contacts table and include a
hidden text box in it bound to the Email column. The list box's RowSource
property would then reference this control as a parameter, e.g.

SELECT EmailAddress FROM EmailAddresses WHERE ContactID =
Forms!YourDialogueForm!txtContactID;

You'd then simply open the dialogue form filtered to the relevant contact:

Dim strFilter As String
Dim lngContactID As Long

lngContactID = <get contact id from somewhere>

strFilter = "ContactID= " & lngContactID
DoCmd.OpenForm "YourDialogueForm", WhereCondition:=strFilter,
WindowMode:=acDialog

Or if you are using the contact's text name rather than a numeric ID:

strFilter = "Contact = """ & strContact & """"

2. Another approach would be to use an unbound dialogue form and pass the
contact id to it via the OpenArgs mechanism:

DoCmd.OpenForm "YourDialogueForm", WindowMode:=acDialog,
OpenArgs:=lngContactID

In the dialogue form's Load event procedure you'd then assign the contact id
to the hidden text box, which would be unbound of course, and then requery
the list box with:

Me.txtContactID = Me.OpenArgs
Me.lstEmailAddresses.Requery

BTW for more flexible ways of using the OpenArgs mechanism so that multiple
and/or named arguments can be passed see:


http://community.netscape.com/n/pfx...yMessages&tsn=1&tid=24091&webtag=ws-msdevapps


To return values from the dialog form you can do so by assigning them to
public variables or to controls (possibly hidden) on the calling form. This
can be done in the dialogue form's Close event procedure. With you example,
to return multiple email addresses as a string with the addresses separated
by semi-colons, the code would iterate through the list box's ItemsSelected
collection, e.g.

Dim varItem As Variant
Dim strEmails As String
Dim ctrl As Control

Set ctrl = Me.lstEmailAdresses

If ctrl.ItemsSelected.Count > 0 Then
For Each varItem In ctrl.ItemsSelected
strEmails = strEmails & ";" & ctrl.ItemData(varItem)
Next varItem

' remove leading semicolon
strEmails = Mid(strEmailList, 2)

' assign email list to a public variable
strEmailList = strEmails

Else
MsgBox "No email addresses selected", vbInformation, "Warning"
End If

The alternative of course is to use the values selected in the dialogue form
directly in code in its module, e.g. in the Click event procedure of a button
on the form which sends the emails to the addresses in question. Rather than
building the list of addresses as a string expression an email to each
selected address could be generated at each iteration through the
ItemsSelected collection.

To wrap everything in a function you'd open the form in the function. As
opening a form in dialogue mode causes code execution to stop you can assign
the value to a public variable in the dialogue form's Close event procedure
and then set the return value of the function to the variable's value, so a
module including such a function would go something like this:

Option Compare Database
Option Explicit

Public strEmailList As String

Function GetEmailList(strContact As String) As String

Dim strFilter As String

' first assign a zero length string to the public
' variable to replace any existing value
strEmailList = ""
strFilter = strFilter = "Contact = """ & strContact & """"
DoCmd.OpenForm "YourDialogueForm", _
WhereCondition:=strFilter, _
WindowMode:=acDialog

' the code will pause execution here until the
' dialogue form is closed, so you can next set
' the value assigned to the public variable in the
' dialogue form's module as the return value of
' the function

GetEmailList = strEmailList

End Function

Ken Sheridan
Stafford, England
 
Albert D. Kallal said:
I explain in detail how to use forms to "return" values here:

Ok, but how do I get a value into it? Since the acDialog goes modal, I can't
poke values into form fields. Can I force it to go modal after opening?

Maury
 
Ahhh, openargs, I keep forgetting about that.

The only downside here is that you have to agree to make all of your forms
return data in the same field, but that's not much of a limitation. I'm going
to start converting all of my forms to work this way, using a field called
"dialogReturn".

Maury
 
Maury Markowitz said:
Ok, but how do I get a value into it? Since the acDialog goes modal, I
can't
poke values into form fields. Can I force it to go modal after opening?

Maury

OK, that's a new detail, and as always when additional details, Ford, a
different design might be considered here.

first read off the bat is I as a general rule to not like dialog forms.
while I just poses a nice article on how to use dialog forms, I should
really pick a Vietnam pepper on that article, because once you commit to
dialog formed a good number of problems arise as a result.

when good example of course is that you can use your custom menu bars
anymore, and you also cannot have multiple instances of that dialog form
opened. it seems to me that if we build a generalized search form for
example, we might have several places in our application or that form would
be needed to be open to more than once. in fact I have such a custom search
form, and I do allow multiple instances of that to be opened, and they also
have code that calls that formed many times, and then has to return values
from the form. (when I have some extra time all posted article as to how I
do this approach).

and in fact your question also shows another limitation of using dialog
forms.

there are two solutions to this problem, but one is simply to pass
parameters, and parse out values.

I Usually use a "/", or better yet a ~ as a delimiters

eg:

strPassParms = "frmCustomers" & "/" & lngBookingID

docmd.OpenForm "abc",,,,,,strPassParms

Then, in the forms open event, we go:

Dim strFromForm As String
Dim lngBookingID as long

strFromForm = split(Me.OpenArgs, "/")(0)
lngBookingId = split(Me.OpenArgs, "/")(1)

So taking a few parameters and parsing them out does work quite well.
However there still are to a good number of limitations that will constrain
you as a developer over time when you use dialog forms. I actually recommend
that if you can ignore or avoid dialog forums you should. I wrote that
article on dialog forms about three to four years ago and in the past three
to four years, I now as a design ruled rarely use that dialog trick.

What I do now is simply open up a form, and on the close event of the form
execute code from the *calling* form. This makes your code a little bit more
difficult because you don't simply program in a linear and fashion were you
call a form your other form code, wait, and then the code continues. You
simply have to break up your code into two parts the part that calls the
form, and then the code that runs on the return of that form.

eg:

Public Function SeachName

' get a name

Set frmSearch = New Form_frmContactsSearch
frmSearch.Visible = True
frmSearch.bolSelectOnly = True
frmSearch.Caption = "Select contact to book to"
Set frmSearch.frmPrevious = Me

' Note how the above sets a instance of a form to the current form (this
is a coding standard I adopted)
frmSearch.cmdAdd.SetFocus

' return code continues on below seldone

End Function

Public Function SelDone()

Dim bolOk As Boolean
Dim strSql As String
Dim lngResID As Long
Dim rstResMain As dao.Recordset
Dim lngContactID As Long

' if we return here from form search..then a selection was made

lngContactID = Nz(frmSearch!ContactID, 0)
Set rstResMain = CurrentDb.OpenRecordset("ResMain")
rstResMain.AddNew
rstResMain!ContactID = frmSearch!ContactID

clsTour.lngResID = lngResID
clsTour.AddFriends False
bolOk = clsTour.BookToTour(clsBookInfo)

DoCmd.OpenForm "Contacts", , , "ContactID = " & lngContactID

I cut out a lot of code, but you simply have the form *call* a differnt
routine. That means you code changes from


--->>> calll form
wait
<<<--- grab values
close form

to

routinte 1
-->>> call form


routine2 <<<---- when close...call routine 2 from calling form

eg, the "ok" buttion on that for looks like:

Me.Visible = False
frmPrevious.SelDone


So, I *always* execute a routine called Seldone from the callin form. this
means that I can use custom menus, use multiple instances of the form, and
not run some kind of repeating loop that tests to see if the form is still
open or in use. This approach is about the cleanest code compromise that I
can come up with, and it also means you can open a form, and set as many
values (and not have to use the above parameters trick).

The added bonus of course is that you actually don't lock up or freeze your
code as dialog forms do....
 
Back
Top