Managing the flow of forms...

  • Thread starter Thread starter Fred Boer
  • Start date Start date
F

Fred Boer

Hello!

Recently, I have been busily fiddling with some of the forms in my little
library application. I decided it would be useful to be able to call the
same form from more than one other form (i.e. call "FormA" from either
"Form1" or "Form2"). However, this has brought up a problem in how to manage
the "flow" of forms and opening, closing, minimizing, etc.

Problems crept in because a user could (possibly...) open a number of forms,
minimize the last form opened, close the preceding forms, and then, close
the open form. This would cause the application to break. (i.e. open
"Form1"; then from "Form1" open "Form2"; then from "Form2" open "Form3";
then minimize "Form3"; then close "Form1" and "Form2"; then maximize
"Form3"; and then close "Form3", leaving the user in the empty database
window...)

Now, I know that I could, if I wished, tightly lock down my forms,
preventing the use of the minimize, maximize, and close buttons, and forcing
users to use my own "custom" close buttons. I could, therefore, be in
complete control of the flow of forms in the application. I even did it this
way once, but, in the end I decided that I would prefer that the forms
behave the way users would expect the UI to work.

My latest attempt involved making almost all of the forms modal. This seemed
to work, and I was happy. (Well, reasonably happy, at least..... stress
about the Toronto Maple Leafs in the Stanley Cup playoffs is preventing me
from achieving true happiness at this time, but I digress...<g>) This
morning, I tried to break it, and I did (sigh)...

If I opened a chain of modal forms, and then minimized the active form, I
found that I was trapped... The active form was minimized and then hidden by
the "frozen" form previously opened... I couldn't move or minimize the
"frozen" form to "get at" the active, but minimized form. So now I'm back
looking at how to disable the minimize button...(which I don't want to
do...)

I would love some suggestions about how to manage the flow of forms. Am I on
the right track using modal forms, but have just screwed up somehow? Is
there a reference somewhere that can help with this kind of design problem?

I would love to hear suggestions about how to manage forms, while still
maintaining the standard Windows UI for my forms...

Thanks for reading this rather involved post!

Fred Boer
 
Fred, this is a good question, and hopefully you will get some good answers
with divergent opinions.

The interface needs to be suited to the target audience. Someone who uses
the software rarely may appreciate the wizard-like approach of the interface
you describe, where you hold their hand and they walk through an unvariable
sequence of steps to get a job done. That's easy to learn, and the
step-by-step approach means the user does not have to think about the
process or remember what to do next.

However, someone who will be using the database every day is likely to find
that approach inflexible and frustrating. Having to walk through every step,
and cancel the process if you forgot to perform another sequence first will
really slow down a good data entry person.

The alternative is the totally unstructured approach, where the user can
open anything anywhere, hopping from form to form and opening/closing
whatever they need. For example, if they forgot to add an item to a lookup
list, they just double-click the combo. Previous form stays open and dirty,
but the form for the combo's RowSource table opens in front and lets them
modify the lookup data. In the AfterUpdate and AfterDelConfirm of that form,
any other forms that could depend on it are brought up to date. Nothing is
modal (except a process that cannot continue without an immediate window).
Nothing is procedural.

It takes a little longer to learn this type of software, because there is no
sequence to follow. But users *love* that kind of flexibility, and the end
result is that (assuming a creative data structure also), people end up
using the database way beyond anything you dreamt of when you set it up.
This is harder to do right, because the sequences are completely
unpredictable, but I find these a real buzz, and I always enjoy seeing the
imaginative processes that users develop for themselves when you leave the
options open for them.

In reality, there is a complete spectrum to choose from between the
modal/procedural/wizard-style approach, and the totally flexible,
event-driven mindset where the developer has no idea what is likely to
happen next.
 
Hi Fred,

As Allen said, there are probably lots of different opinions on this subject.

I'll offer my own thoughts, but please remember my experience level!

I think the "flow through the forms" has a lot to do with the application itself. Some types of data
entry necessitate a logical step from point A to point B. Skipping around could cause all kinds of
problems. Your library application may require a specific series of steps for checking out a book.
Guiding the user through this process seems logical to me. Adding a new book entry into the database
also would require a specific set of steps to follow.

Problems can arise sometimes if the directed "path" is not followed. You may recall a recent post of
my own concerning entering child records on a subform before the main record was completed.
Naturally, Access coughed up a hairball because it needed the main record created and saved first to
enforce RI. So I needed a way to *graciously* inform the user to fill in the main tab first. Users
can't always be expected to follow the set path needed to maintain data integrity! And I can tell
you with a lot of certainty that most of them could care less WHY they need to follow the set path!
I spend an enormous amount of time testing (to the best of my ability) every possible conceivable
thing a user could do while navigating through the forms. It takes a lot of time, but I'D rather be
surprised with an unexpected problem, rather than the user!

Our payroll system has a main menu that somewhat guides you through the payroll cycle. The different
buttons on the main form are grayed out depending upon what step you are in the process. Naturally
you can't transmit the payroll if you have not created it yet, so that option only becomes available
when *appropriate*.

I use a main Switchboard-type form to launch all my procedures. This form is always open and I think
of it as my FILO form (First In, Last Out). This is the first form you see when you come in and it
should be the last one when you leave. It is set as Modal and all other forms just appear right on
top of it. In fact, you can't even see it beneath the other forms so it is not intrusive at all.
From this starting point the user can go anywhere they darn well please since there are lots of
different functions. As they continue to open forms they just sit right on top of each other (there
are a few exceptions to this rule). When they close all the forms they will eventually be back to
square 1: the main form where they can exit when finished. There are many places, however, where I
specifically need to control what the user does and I work with mostly low-tech people.

A good example would be the invoice entry screen. The main tab holds the "One Side" of the
relationship and the second tab holds the invoice details, "Many Side" of the relationship. When the
form opens the second tab is completely blank. Zip, nada, nothing. Problem solved for keeping data
integrity. No error messages for the user just an obvious "can't do anything here yet feeling." You
first need to select a vendor from the list, on the main form, but what if it's a new vendor? No
problem, trigger the Not In List event OR a button on the tab to create a new vendor. The vendor
entry screen pops up right on top completely covering the screen. They enter the vendor info, close
the form, and poof they are right back on the invoice screen with the new vendor in the list. It
flows very easily for the users. Well, I think it does.

I would suggest watching different people use your library application. Do you see any patterns,
weird stuff they do? You may be surprised at what you find. I had a friend play with a personal
application I'm working on and right from the start they did something completely unexpected to me!
Nothing bad or any error messages, but they EXPECTED something to happen when they clicked in a
certain spot. It's a long story, but it was something very easy for me to add/change. No sense
fighting against their expectations.

Last comment I'll make is the water can get very murky with custom menu bars/toolbars. If you have
custom menu bars/toolbars that allow your users "free reign" to go anywhere at any time you need to
make sure it is OK for them to do that! What if the user suddenly decides to go elsewhere in the
application without closing and/or finishing an important form! you need to carefully account for
that. You may need to enable/disable certain items on the menu bars/toolbars at various times.

I guess the best answer I can give is the flow of the forms depends on the application data
constraints AND the type of users who will use the database.

--
Jeff Conrad
Access Junkie
Bend, Oregon
Install the latest Windows Updates:
http://www.microsoft.com/security/security_bulletins/200404_windows.asp
 
Dear Allan:

Thanks for your comments. I would *like* to move to as flexible an interface
as possible... I do expect, however, that this will entail a lot more
testing and debugging! :)

Fred
 
Dear Jeff:

Thanks for your comments! I think you are correct that there would be some
processes that require "specific steps", and some that don't. I also think
that a more careful consideration of where to use modal forms might help. I
took your suggestion to make the main switchboard form modal, and that would
prevent the situation I described earlier in which the database window is
left empty. I know that trying to make the flow flexible requires a lot of
debugging and watching users actually work with the program. This *is* a bit
of a problem for me, since the only users are myself and two library student
helpers; however, I can certainly do my best to try to do things
illogically! <g>

Other questions:

When users open forms over top of the switchboard form, are these forms
maximized? If so, is there a mechanism to see what forms you have open at a
given time? I am finding that my forms are all maximized, and that it is
easy to forget exactly what you have open...

Are there any secondary switchboard forms? Or does each switchboard button
lead to a single form? In my case, my main switchboard has 4 buttons, one
of which opens a secondary switchboard form with 4 buttons. Perhaps this is
a source of some of my problems...

Regarding command bars... I am considering that route as well... if all the
forms can be opened from the menu, then all forms would be available at all
times... but, as you say, perhaps that might cause new issues..

In any case, this is a lot of fun! I suppose taking time to look at examples
of professional Access databases besides Northwind is also a good idea...

Cheers!
Fred

Go Leafs GO!


Jeff Conrad said:
Hi Fred,

As Allen said, there are probably lots of different opinions on this subject.

I'll offer my own thoughts, but please remember my experience level!

I think the "flow through the forms" has a lot to do with the application itself. Some types of data
entry necessitate a logical step from point A to point B. Skipping around could cause all kinds of
problems. Your library application may require a specific series of steps for checking out a book.
Guiding the user through this process seems logical to me. Adding a new book entry into the database
also would require a specific set of steps to follow.

Problems can arise sometimes if the directed "path" is not followed. You may recall a recent post of
my own concerning entering child records on a subform before the main record was completed.
Naturally, Access coughed up a hairball because it needed the main record created and saved first to
enforce RI. So I needed a way to *graciously* inform the user to fill in the main tab first. Users
can't always be expected to follow the set path needed to maintain data integrity! And I can tell
you with a lot of certainty that most of them could care less WHY they need to follow the set path!
I spend an enormous amount of time testing (to the best of my ability) every possible conceivable
thing a user could do while navigating through the forms. It takes a lot of time, but I'D rather be
surprised with an unexpected problem, rather than the user!

Our payroll system has a main menu that somewhat guides you through the payroll cycle. The different
buttons on the main form are grayed out depending upon what step you are in the process. Naturally
you can't transmit the payroll if you have not created it yet, so that option only becomes available
when *appropriate*.

I use a main Switchboard-type form to launch all my procedures. This form is always open and I think
of it as my FILO form (First In, Last Out). This is the first form you see when you come in and it
should be the last one when you leave. It is set as Modal and all other forms just appear right on
top of it. In fact, you can't even see it beneath the other forms so it is not intrusive at all.
From this starting point the user can go anywhere they darn well please since there are lots of
different functions. As they continue to open forms they just sit right on top of each other (there
are a few exceptions to this rule). When they close all the forms they will eventually be back to
square 1: the main form where they can exit when finished. There are many places, however, where I
specifically need to control what the user does and I work with mostly low-tech people.

A good example would be the invoice entry screen. The main tab holds the "One Side" of the
relationship and the second tab holds the invoice details, "Many Side" of the relationship. When the
form opens the second tab is completely blank. Zip, nada, nothing. Problem solved for keeping data
integrity. No error messages for the user just an obvious "can't do
anything here yet feeling." You
first need to select a vendor from the list, on the main form, but what if it's a new vendor? No
problem, trigger the Not In List event OR a button on the tab to create a new vendor. The vendor
entry screen pops up right on top completely covering the screen. They enter the vendor info, close
the form, and poof they are right back on the invoice screen with the new vendor in the list. It
flows very easily for the users. Well, I think it does.

I would suggest watching different people use your library application. Do you see any patterns,
weird stuff they do? You may be surprised at what you find. I had a friend play with a personal
application I'm working on and right from the start they did something completely unexpected to me!
Nothing bad or any error messages, but they EXPECTED something to happen when they clicked in a
certain spot. It's a long story, but it was something very easy for me to add/change. No sense
fighting against their expectations.

Last comment I'll make is the water can get very murky with custom menu bars/toolbars. If you have
custom menu bars/toolbars that allow your users "free reign" to go
anywhere at any time you need to
make sure it is OK for them to do that! What if the user suddenly decides to go elsewhere in the
application without closing and/or finishing an important form! you need to carefully account for
that. You may need to enable/disable certain items on the menu
bars/toolbars at various times.
 
As Allen mentioned, there is a lot of different ideas.

I will give my 2 cents of how I deal with these problems:
Problems crept in because a user could (possibly...) open a number of forms,
minimize the last form opened, close the preceding forms, and then, close
the open form. This would cause the application to break.

Now, I know that I could, if I wished, tightly lock down my forms,
preventing the use of the minimize, maximize, and close buttons, and forcing
users to use my own "custom" close buttons.

ALL you really need to do here is make the forms model, AND remove the
min/max buttons.

If form flow and use can break your application, you have two choices:

1) use model forms (more on this)
2) allow multiple instances of the same from
My latest attempt involved making almost all of the forms modal. This seemed
to work, and I was happy. (Well, reasonably happy, at least..... stress
about the Toronto Maple Leafs in the Stanley Cup playoffs is preventing me
from achieving true happiness at this time, but I digress...<g>) This
morning, I tried to break it, and I did (sigh)...

If I opened a chain of modal forms, and then minimized the active form, I
found that I was trapped...

Bingo!

Lets lay down rule #1

Model forms can not, and SHOULD not be allowed to be minimized!

I suppose ms-access should automatic disable the min/max buttons on the form
when you set it as model. access does NOT do this, so you can hang yourself.
However, as a developer you can fix this. So, in the format tab of the forms
property, set the min/max buttons to "no", and then set the "model" to yes
in the "other" tab. I mean, it makes NO sense to allow a user to minimize
the current form if it is model. You can certainly can, and should leave the
little close "X" in the upper right hand corner. So, as a rule, I usually
place a nice big close button on the lower right of my forms (they are
larger, and easy to hit), and also allow the little X. However, do NOT allow
min/max buttons on a form.

By the way, you might like to know that if you remove the min/max buttons on
a form, then the if the next form loaded is maxed, when you close it, the
previous model form is NOT maximised, and does NOT take on the maxed
setting. This makes sense, since you removed the min/max controls! (a great
little access trick to know. This is especially the case for my forms that
launch reports. The reports are maxed when opened..but when closed..the form
that opened the report is NOT maxed).
So now I'm back
looking at how to disable the minimize button...(which I don't want to
do...)

Like I said, it does NOT make sense to manage the flow of forms, and the
same time allow min buttons. If you need to control the flow of the user,
then you need to use model forms, and as we see, model forms can not, and
should not be minimized (it don't make sense).

If your application can be broken, or confused by the forms being opened, or
not opened, or already opened, then you MUST use model forms.

If you have a case where one form can be opened from more then one spot, you
either must:

Force the user to un-wind back through the current series for forms to close
that form (you likely don't want to do that).

Or:

Allow multiple instances of that same form.

You really only have the above two options.

As mentioned,about 90% or more of my forms are model, and the ALL HAVE the
min buttons turned off..as model, and min do NOT mix in any way shape or
form!

I also as a habit on start-up turn off the "windows in taskbar", as once
again, with model forms, windows in taskbar makes NO sense at all.

And, to be honest, I have a few cases where I have a form get launched that
was already opened. It was a real nasty problem, and also broke my
application in a sense. My solution was to make a copy of the form (yuk!!).
Some other cases where fixed by me simply putting in code to "close" that
already open form via code (both solutions are not clean, nor very good..but
that is what I did!).

Next time, my solution will be to allow multiple instances of the form
opened..and that would have solved the problem complete.

Albert D. Kallal
Edmonton, Alberta Canada
(e-mail address removed)
 
Hi Fred,
Thanks for your comments! I think you are correct that there would be some
processes that require "specific steps", and some that don't. I also think
that a more careful consideration of where to use modal forms might help. I
took your suggestion to make the main switchboard form modal, and that would
prevent the situation I described earlier in which the database window is
left empty.

All of my forms are modal, every last one.
And each border is set to Thin so it can't be resized.
(Again, just a personal preference)
I know that trying to make the flow flexible requires a lot of
debugging and watching users actually work with the program. This *is* a bit
of a problem for me, since the only users are myself and two library student
helpers; however, I can certainly do my best to try to do things illogically! <g>

Do that, seriously.
Just do really odd stuff, click around everywhere, tab in strange places, etc.
Confession - I've even had my kids play with the forms sometimes!
When users open forms over top of the switchboard form, are these forms maximized?

No, they are "semi-maximized" with code here:

http://www.mvps.org/access/api/api0022.htm

Looks much better I think. I would really experiment with this. It's a breeze to set up and you may
really like the results. I needed this also because NO forms have the "X" close button. I know you
don't particularly care for doing that, but I feel as long as I'm consistent throughout the
application then it is 'MY' UI they work with and so far no one has complained at all.
If so, is there a mechanism to see what forms you have open at a
given time? I am finding that my forms are all maximized, and that it is
easy to forget exactly what you have open...

No, I don't have a mechanism set up to do that. I was quite interested in Jan's post about the
"Breadcrumb Trail" of open forms a few months ago. Not sure if she ever got that resolved.

For the most part it really doesn't make a difference how many forms are open on top of each other
in my application. There are, however, a few specific situations where I check to see if certain
forms are open in other form's open event and close them if they are open. A little hard to explain.

If you REALLY want to know which forms are open you could always turn on the Windows In TaskBar
option. But, personally I just don't like that feature at all and turn it off!
Are there any secondary switchboard forms? Or does each switchboard button
lead to a single form? In my case, my main switchboard has 4 buttons, one
of which opens a secondary switchboard form with 4 buttons. Perhaps this is
a source of some of my problems...

The main switchboard leads to sub-menus just like the Switchboard Manager menus. Each sub-menu then
launches the forms. You will always be taken back to the main switchboard when you close the various
forms.
Regarding command bars... I am considering that route as well... if all the
forms can be opened from the menu, then all forms would be available at all
times... but, as you say, perhaps that might cause new issues..

Yep, I'm working through checking for problems right now.
In any case, this is a lot of fun! I suppose taking time to look at examples
of professional Access databases besides Northwind is also a good idea...

Go Leafs GO!

Who do they play next after they win tonight in double overtime?
<vbg>

--
Jeff Conrad
Access Junkie
Bend, Oregon
Install the latest Windows Updates:
http://www.microsoft.com/security/security_bulletins/200404_windows.asp
 
Dear Albert:

There is much here to think about! BTW, I decided to try making my forms
modal because of post of yours on the subject that I found in a Google
Groups search, so I am glad you have responded here! I will be looking
carefully at your suggestions, and trying them as soon as I have time..
Perhaps tomorrow, after the Leafs win... ;)

Many thanks!
Fred
 
Thanks Jeff! No time right now to delve into this, but I will be looking
carefully at it as soon as I can!

Cheers!
Fred
 
Hello Albert...

One question comes to mind: you mention that one of the options of having a
form opened in different places is to "allow multiple instances of a
form"... I am unfamiliar with this; is it difficult to do? That might be a
solution...

Fred
 
Sorry, Albert, I should have looked first. A quick Google Groups search has
turned up a lot of references to multiple instances of forms, including
sample dbs from Sandra Daigle and Allen Browne... I can start researching
from there...

Fred

P.S. Although your opinon of the difficulty of this or any comments are
still welcome! <g>
 
Well, you, Jeff and Albert all use mostly modal forms, so I'm the odd one
out here. Just checked the last 4 applications I wrote, and there *no* bound
modal forms - just a couple of unbound dialogs.

Since you expressed an interest in trying that approach, here are some
ideas. The underlying philosophy is to rely on the events instead of
thinking of sequences of things. In practice, it is incredibly simple. We
have a couple of default forms set up, with the properties set to the
generic functions, so it actually takes us *no* extra time to design it this
way.

1. Subform with no record in main form
============================
Jeff raised this. Dead easy. Set the foreign key's Required property.
Then (so that the user gets a message instantly rather than after filling in
the subform), we set the subform's BeforeInsert property to:
=RequireParent([Form])

Public Function RequireParent(frmMe As Form) As Integer
If frmMe.Parent.NewRecord Then
MsgBox "Enter main form record first", "Insert Canceled..."
DoCmd.CancelEvent
RequireParent = True
End If
End If

Relying on the engine and the event, the problem never arises.


2. Keeping something open
====================
Set the On Close property all forms (except the switchboard) to:
=Keep1Open([Form])
and all reports to:
=Keep1Open([Report])

Public Function Keep1Open(objMe As Object)
Dim sName As String 'Name of the object being closed.
Dim lngObjType As Long 'acForm or acReport
Dim bFound As Boolean 'Flag not to open the switchboard.
Dim frm As Form 'an open form.
Dim rpt As Report 'an open report.

If TypeOf objMe Is Form Then
lngObjType = acForm
ElseIf TypeOf objMe Is Report Then
lngObjType = acReport
End If

'Any other visible forms?
For Each frm In Forms
If frm.Visible Then
If frm.Name <> sName Or lngObjType <> acForm Then
bFound = True
Exit For
End If
End If
Next

'Any other visible reports?
If Not bFound Then
For Each rpt In Reports
If rpt.Visible Then
If rpt.Name <> sName Or lngObjType <> acReport Then
bFound = True
Exit For
End If
End If
Next
End If

'If none found, open the switchboard.
If Not bFound Then
DoCmd.OpenForm "frmSwitchboard"
End If
End Function
(Note: trap error 2046.)


3. Dependencies
============
In the AfterUpdate and AfterDelConfirm events of every bound form, call
another generic procedure which will handle the concurrency issues. For
example, if the user double-clicks a combo and adds an item to the lookup
list, this routine will requery any combos on other forms that have that
table as RowSource.

When you are developing your forms, you probably don't know what other parts
of the interface will rely on them, but that doesn't matter. Just call the
generic routine. Then, when all the forms are in place (including the
interface the reports if that uses combos to offer filtering options), you
can examine the dependencies and write the body of this routine. It ends up
being one large Select Case to handle each form, but we find the maintenance
is easiest if all dependencies are in the one place, and it allows you to
develop forms willy-nilly and sort out the dependencies at the end.

Public Function NotifyCombos(frmSource As Form, Optional vStatus =
acDeleteOK)
Select Case frmSource.Name
Case "frmMyLookup"
If IsLoaded("MyOtherForm") Then
Forms!MyOtherForm!cboMyLookup.Requery
End If
...

4. Concurrency
===========
You do need to be aware of what else might be open at the same time if both
could be dirty, but we do not force a save in a form's Deactivate event
since that would defeat the whole point of going somewhere else to create or
lookup data while entering/editing.

If you plan to execute an Update or Delete query, it's a good idea to
consider the implication for forms that may be open, and close them or at
least ensure they are not Dirty. That's just good practice anyway.

If you issue an OpenForm/OpenReport and the thing is already open, Access
does not give it focus (so it does not appear) and any WhereCondition or
OpenArgs is not applied. We use a little wrapper function that closes it if
it is already open, defaults to preview mode, handles error 2501 if the
report does not open, etc. Since the report is meaningless without a
description of its filter, our wrapper accepts that as well, and it is
handled and cleared by another generic function called in the Format event
of the report's Header.

Public Function OpenTheReport(strDoc As String,
Optional lngMode As Long = acViewPreview,
Optional strWhere As String,
Optional strDescrip As String) As Boolean

If IsLoaded(strDoc, acReport) Then
DoCmd.Close acReport, strDoc
End If
gstrTitleInHeader = strDescrip
DoCmd.OpenReport strDoc, lngMode, , strWhere
OpenTheReport = True
End Function


Hopefully that's enough to let you experiment with the idea of just relying
on the events.
 
Dear Allen:

Thanks for your suggestions! Although I am experimenting with modal forms as
per Jeff and Albert, I really am interested in the "flexible" approach you
are suggesting. Unfortunately, (since this is a "lunch hour and after the
kids are in bed" hobby), I probably won't be able to do anything until the
beginning of next week.. (the kids never seem to go to bed on the weekend
and for some reason my wife wants me to have lunch with *her* not Access...
<g>).

I am certain I will have questions/need further help, so, if you would be so
kind, could you watch this thread over the next while? I would appreciate
it!

Many thanks!

Fred

Allen Browne said:
Well, you, Jeff and Albert all use mostly modal forms, so I'm the odd one
out here. Just checked the last 4 applications I wrote, and there *no* bound
modal forms - just a couple of unbound dialogs.

Since you expressed an interest in trying that approach, here are some
ideas. The underlying philosophy is to rely on the events instead of
thinking of sequences of things. In practice, it is incredibly simple. We
have a couple of default forms set up, with the properties set to the
generic functions, so it actually takes us *no* extra time to design it this
way.

1. Subform with no record in main form
============================
Jeff raised this. Dead easy. Set the foreign key's Required property.
Then (so that the user gets a message instantly rather than after filling in
the subform), we set the subform's BeforeInsert property to:
=RequireParent([Form])

Public Function RequireParent(frmMe As Form) As Integer
If frmMe.Parent.NewRecord Then
MsgBox "Enter main form record first", "Insert Canceled..."
DoCmd.CancelEvent
RequireParent = True
End If
End If

Relying on the engine and the event, the problem never arises.


2. Keeping something open
====================
Set the On Close property all forms (except the switchboard) to:
=Keep1Open([Form])
and all reports to:
=Keep1Open([Report])

Public Function Keep1Open(objMe As Object)
Dim sName As String 'Name of the object being closed.
Dim lngObjType As Long 'acForm or acReport
Dim bFound As Boolean 'Flag not to open the switchboard.
Dim frm As Form 'an open form.
Dim rpt As Report 'an open report.

If TypeOf objMe Is Form Then
lngObjType = acForm
ElseIf TypeOf objMe Is Report Then
lngObjType = acReport
End If

'Any other visible forms?
For Each frm In Forms
If frm.Visible Then
If frm.Name <> sName Or lngObjType <> acForm Then
bFound = True
Exit For
End If
End If
Next

'Any other visible reports?
If Not bFound Then
For Each rpt In Reports
If rpt.Visible Then
If rpt.Name <> sName Or lngObjType <> acReport Then
bFound = True
Exit For
End If
End If
Next
End If

'If none found, open the switchboard.
If Not bFound Then
DoCmd.OpenForm "frmSwitchboard"
End If
End Function
(Note: trap error 2046.)


3. Dependencies
============
In the AfterUpdate and AfterDelConfirm events of every bound form, call
another generic procedure which will handle the concurrency issues. For
example, if the user double-clicks a combo and adds an item to the lookup
list, this routine will requery any combos on other forms that have that
table as RowSource.

When you are developing your forms, you probably don't know what other parts
of the interface will rely on them, but that doesn't matter. Just call the
generic routine. Then, when all the forms are in place (including the
interface the reports if that uses combos to offer filtering options), you
can examine the dependencies and write the body of this routine. It ends up
being one large Select Case to handle each form, but we find the maintenance
is easiest if all dependencies are in the one place, and it allows you to
develop forms willy-nilly and sort out the dependencies at the end.

Public Function NotifyCombos(frmSource As Form, Optional vStatus =
acDeleteOK)
Select Case frmSource.Name
Case "frmMyLookup"
If IsLoaded("MyOtherForm") Then
Forms!MyOtherForm!cboMyLookup.Requery
End If
...

4. Concurrency
===========
You do need to be aware of what else might be open at the same time if both
could be dirty, but we do not force a save in a form's Deactivate event
since that would defeat the whole point of going somewhere else to create or
lookup data while entering/editing.

If you plan to execute an Update or Delete query, it's a good idea to
consider the implication for forms that may be open, and close them or at
least ensure they are not Dirty. That's just good practice anyway.

If you issue an OpenForm/OpenReport and the thing is already open, Access
does not give it focus (so it does not appear) and any WhereCondition or
OpenArgs is not applied. We use a little wrapper function that closes it if
it is already open, defaults to preview mode, handles error 2501 if the
report does not open, etc. Since the report is meaningless without a
description of its filter, our wrapper accepts that as well, and it is
handled and cleared by another generic function called in the Format event
of the report's Header.

Public Function OpenTheReport(strDoc As String,
Optional lngMode As Long = acViewPreview,
Optional strWhere As String,
Optional strDescrip As String) As Boolean

If IsLoaded(strDoc, acReport) Then
DoCmd.Close acReport, strDoc
End If
gstrTitleInHeader = strDescrip
DoCmd.OpenReport strDoc, lngMode, , strWhere
OpenTheReport = True
End Function


Hopefully that's enough to let you experiment with the idea of just relying
on the events.
 
Well, as mentioned, I also got bitten by using model forms.

A had a few cases where the program flow actually go to a form that need to
open a form already in use.

As mentioned, sometimes I simply checked in code if the form was opened..and
closed it.

My 2nd solution as mentioned was I made a copy (but, I sure we both agree
having two forms that do the same thing really is bad. You have to maintain
two copies when you make changes etc.).

As for the multiple forms solution I would actually write, or create a
global sub that opens your forms.

Thus, for general code development, I would continue to use:

docmd.OpenFrom "yourform"

However, for any form that MAY need to be opened more then once, I would
make a special sub like:

Call MultiOpenForm "yourform"

The above routine could also accept the "same" general parameters as the
normal open form..but would always launch a "copy" of the form.

Making a "general" routine to open forms does requite hard coding of the
form name. So, each form that you would allow to be opened multiplies times
would have to be added to the MultiOpenForm sub. Something like:


dim frmO as form

select case strOpenForm

case "frmCustomer"
set frmO = new form_frmCustomer

case "frmLocations"
set frmO = new form_frmLocatons



gblFormsCollection.Add frmO


The above is rough outline. My only point here is to try and make some
"general" routine to launch those multi forms, so each time in code when you
need to launch a form...you don't have to write a whack of code each time.

The other problem of course is that we can't use the "where" clause, and
stuff like OpenArgs. These additional features could be added as a property
of each form that is to allow multi-copies.

I have not yet actually tried the above...but since I was bitten by one case
where I also needed a form (that was already opened). It seems to me, that
the small amount of additional code would out-weight my other solutions.

I can't say the above is a 100% good idea..as I have not yet tired it as a
solution..but I certainly plan to do the above next time I design an
application.

--
Albert D. Kallal (MVP)
Edmonton, Alberta Canada
(e-mail address removed)
http://www.attcanada.net/~kallal.msn
So, you could at aleast have one geneal routine
Unfortantly, when you launch multiplate copies of the same form
 
Hi Fred,

I should have mentioned this before on the link I provided
earlier for the "semi-maximized" forms:

http://www.mvps.org/access/api/api0022.htm

If you wanted to see what this looked like first hand just
look at the Access Games program I sent you. The game grid
form utilizies that code. So the form fills up the entire
space in the Access Window, but easily allows room for
toolbars and menu bars. There is no "dead space" around
the game grid. If I were to open another form on top of
that one with the same code, you would not even know that
one form is on top of the other.
 
Hi Allen,

Thanks for the great code! I'm very interested to see
professional developer's ideas and thoughts as well. I
will definitely play around with the ideas you presented
to expand my knowledge. I shall assimilate at once!

--
Jeff Conrad
Access Junkie
Bend, Oregon
-----Original Message-----
Well, you, Jeff and Albert all use mostly modal forms, so I'm the odd one
out here. Just checked the last 4 applications I wrote, and there *no* bound
modal forms - just a couple of unbound dialogs.

Since you expressed an interest in trying that approach, here are some
ideas. The underlying philosophy is to rely on the events instead of
thinking of sequences of things. In practice, it is incredibly simple. We
have a couple of default forms set up, with the properties set to the
generic functions, so it actually takes us *no* extra time to design it this
way.

1. Subform with no record in main form
============================
Jeff raised this. Dead easy. Set the foreign key's Required property.
Then (so that the user gets a message instantly rather than after filling in
the subform), we set the subform's BeforeInsert property to:
=RequireParent([Form])

Public Function RequireParent(frmMe As Form) As Integer
If frmMe.Parent.NewRecord Then
MsgBox "Enter main form record first", "Insert Canceled..."
DoCmd.CancelEvent
RequireParent = True
End If
End If

Relying on the engine and the event, the problem never arises.


2. Keeping something open
====================
Set the On Close property all forms (except the switchboard) to:
=Keep1Open([Form])
and all reports to:
=Keep1Open([Report])

Public Function Keep1Open(objMe As Object)
Dim sName As String 'Name of the object being closed.
Dim lngObjType As Long 'acForm or acReport
Dim bFound As Boolean 'Flag not to open the switchboard.
Dim frm As Form 'an open form.
Dim rpt As Report 'an open report.

If TypeOf objMe Is Form Then
lngObjType = acForm
ElseIf TypeOf objMe Is Report Then
lngObjType = acReport
End If

'Any other visible forms?
For Each frm In Forms
If frm.Visible Then
If frm.Name <> sName Or lngObjType <> acForm Then
bFound = True
Exit For
End If
End If
Next

'Any other visible reports?
If Not bFound Then
For Each rpt In Reports
If rpt.Visible Then
If rpt.Name <> sName Or lngObjType <> acReport Then
bFound = True
Exit For
End If
End If
Next
End If

'If none found, open the switchboard.
If Not bFound Then
DoCmd.OpenForm "frmSwitchboard"
End If
End Function
(Note: trap error 2046.)


3. Dependencies
============
In the AfterUpdate and AfterDelConfirm events of every bound form, call
another generic procedure which will handle the concurrency issues. For
example, if the user double-clicks a combo and adds an item to the lookup
list, this routine will requery any combos on other forms that have that
table as RowSource.

When you are developing your forms, you probably don't know what other parts
of the interface will rely on them, but that doesn't matter. Just call the
generic routine. Then, when all the forms are in place (including the
interface the reports if that uses combos to offer filtering options), you
can examine the dependencies and write the body of this routine. It ends up
being one large Select Case to handle each form, but we find the maintenance
is easiest if all dependencies are in the one place, and it allows you to
develop forms willy-nilly and sort out the dependencies at the end.

Public Function NotifyCombos(frmSource As Form, Optional vStatus =
acDeleteOK)
Select Case frmSource.Name
Case "frmMyLookup"
If IsLoaded("MyOtherForm") Then
Forms!MyOtherForm!cboMyLookup.Requery
End If
...

4. Concurrency
===========
You do need to be aware of what else might be open at the same time if both
could be dirty, but we do not force a save in a form's Deactivate event
since that would defeat the whole point of going somewhere else to create or
lookup data while entering/editing.

If you plan to execute an Update or Delete query, it's a good idea to
consider the implication for forms that may be open, and close them or at
least ensure they are not Dirty. That's just good practice anyway.

If you issue an OpenForm/OpenReport and the thing is already open, Access
does not give it focus (so it does not appear) and any WhereCondition or
OpenArgs is not applied. We use a little wrapper function that closes it if
it is already open, defaults to preview mode, handles error 2501 if the
report does not open, etc. Since the report is meaningless without a
description of its filter, our wrapper accepts that as well, and it is
handled and cleared by another generic function called in the Format event
of the report's Header.

Public Function OpenTheReport(strDoc As String,
Optional lngMode As Long = acViewPreview,
Optional strWhere As String,
Optional strDescrip As String) As Boolean

If IsLoaded(strDoc, acReport) Then
DoCmd.Close acReport, strDoc
End If
gstrTitleInHeader = strDescrip
DoCmd.OpenReport strDoc, lngMode, , strWhere
OpenTheReport = True
End Function


Hopefully that's enough to let you experiment with the idea of just relying
on the events.

--
Allen Browne - Microsoft MVP. Perth, Western Australia.

Reply to group, rather than allenbrowne at mvps dot org.

Fred Boer said:
Dear Allan:

Thanks for your comments. I would *like* to move to as
flexible an
interface
as possible... I do expect, however, that this will entail a lot more
testing and debugging! :)

Fred


get some good
answers
audience. Someone who
uses
approach of the
interface through an
unvariable day is likely to
find walk through every
step, another sequence first
will
add an item to a
lookup
stays open and
dirty,
front and lets
them
AfterDelConfirm of that
form,
up to date. Nothing
is an immediate
window). software, because there
is
flexibility, and the
end always enjoy seeing
the themselves when you leave
the
the forms in my
little
to be able to call
the
forms, and then,
close
from "Form2" open
"Form3"; in the empty
database application. I even did
it at least.....
stress playoffs is
preventing
minimized the active
form,
I minimized and then
hidden
move or minimize
the
form. So now I'm
back
the flow of forms.
Am
screwed up somehow?
Is manage forms, while
still

.
 
Dear Allen, Jeff, Albert...

Well, first of all, apologies: I asked you to keep an eye on this thread and
then left it rather longer than I expected. I knew it would take me some
time to get to this... but I didn't expect the death of my hard disk to
intervene. (What a pain rebuilding your computer is...even if you have your
data backed up! :( )

Well, I've tried a few things, and what I am going with for now is the
following:

One main modal form, which serves as what the user sees when no forms are
open. Closing this form quits application.
All other forms are opened through a menu bar which is visible at all times.
All forms have standard windows controls.
Any form can be opened at any time.
Some forms are opened from command buttons on "parent" forms. In these cases
the "child" forms are opened with OpenArgs (filtered to one record, and read
only).

I have a really simple application, and this seems to work, although I have
yet to do any serious testing..

I am wondering about whether I actually want the main modal form, since
minimized windows are hidden behind it. These windows can still be re-opened
via the menu, but users might forget they are there, although I don't
*think* that matters...

Thanks!
Fred


Fred Boer said:
Dear Allen:

Thanks for your suggestions! Although I am experimenting with modal forms as
per Jeff and Albert, I really am interested in the "flexible" approach you
are suggesting. Unfortunately, (since this is a "lunch hour and after the
kids are in bed" hobby), I probably won't be able to do anything until the
beginning of next week.. (the kids never seem to go to bed on the weekend
and for some reason my wife wants me to have lunch with *her* not Access...
<g>).

I am certain I will have questions/need further help, so, if you would be so
kind, could you watch this thread over the next while? I would appreciate
it!

Many thanks!

Fred

Allen Browne said:
Well, you, Jeff and Albert all use mostly modal forms, so I'm the odd one
out here. Just checked the last 4 applications I wrote, and there *no* bound
modal forms - just a couple of unbound dialogs.

Since you expressed an interest in trying that approach, here are some
ideas. The underlying philosophy is to rely on the events instead of
thinking of sequences of things. In practice, it is incredibly simple. We
have a couple of default forms set up, with the properties set to the
generic functions, so it actually takes us *no* extra time to design it this
way.

1. Subform with no record in main form
============================
Jeff raised this. Dead easy. Set the foreign key's Required property.
Then (so that the user gets a message instantly rather than after
filling
in
the subform), we set the subform's BeforeInsert property to:
=RequireParent([Form])

Public Function RequireParent(frmMe As Form) As Integer
If frmMe.Parent.NewRecord Then
MsgBox "Enter main form record first", "Insert Canceled..."
DoCmd.CancelEvent
RequireParent = True
End If
End If

Relying on the engine and the event, the problem never arises.


2. Keeping something open
====================
Set the On Close property all forms (except the switchboard) to:
=Keep1Open([Form])
and all reports to:
=Keep1Open([Report])

Public Function Keep1Open(objMe As Object)
Dim sName As String 'Name of the object being closed.
Dim lngObjType As Long 'acForm or acReport
Dim bFound As Boolean 'Flag not to open the switchboard.
Dim frm As Form 'an open form.
Dim rpt As Report 'an open report.

If TypeOf objMe Is Form Then
lngObjType = acForm
ElseIf TypeOf objMe Is Report Then
lngObjType = acReport
End If

'Any other visible forms?
For Each frm In Forms
If frm.Visible Then
If frm.Name <> sName Or lngObjType <> acForm Then
bFound = True
Exit For
End If
End If
Next

'Any other visible reports?
If Not bFound Then
For Each rpt In Reports
If rpt.Visible Then
If rpt.Name <> sName Or lngObjType <> acReport Then
bFound = True
Exit For
End If
End If
Next
End If

'If none found, open the switchboard.
If Not bFound Then
DoCmd.OpenForm "frmSwitchboard"
End If
End Function
(Note: trap error 2046.)


3. Dependencies
============
In the AfterUpdate and AfterDelConfirm events of every bound form, call
another generic procedure which will handle the concurrency issues. For
example, if the user double-clicks a combo and adds an item to the lookup
list, this routine will requery any combos on other forms that have that
table as RowSource.

When you are developing your forms, you probably don't know what other parts
of the interface will rely on them, but that doesn't matter. Just call the
generic routine. Then, when all the forms are in place (including the
interface the reports if that uses combos to offer filtering options), you
can examine the dependencies and write the body of this routine. It ends up
being one large Select Case to handle each form, but we find the maintenance
is easiest if all dependencies are in the one place, and it allows you to
develop forms willy-nilly and sort out the dependencies at the end.

Public Function NotifyCombos(frmSource As Form, Optional vStatus =
acDeleteOK)
Select Case frmSource.Name
Case "frmMyLookup"
If IsLoaded("MyOtherForm") Then
Forms!MyOtherForm!cboMyLookup.Requery
End If
...

4. Concurrency
===========
You do need to be aware of what else might be open at the same time if both
could be dirty, but we do not force a save in a form's Deactivate event
since that would defeat the whole point of going somewhere else to
create
 
Back
Top