[AccessD] Refreshing open forms when something changes

jwcolby jwcolby at colbyconsulting.com
Mon Jun 20 13:55:51 CDT 2011


 > For a separate form, first determine if the form is open:

Yes, but that is so... Access 2 ish.  ;)

Raising and sinking events solves this problem nicely.  And I don't have to do all the "is this 
open, and if so then do this exact thing..." stuff.

To see this all work just cut and paste the following into a pair of classes and a module and tun 
the test function.

Create a class called dclsMsg and put the following into it.

Option Compare Database
Option Explicit
Public Event Message(varFrom As Variant, varTo As Variant, _
                         varSubj As Variant, varMsg As Variant)
Public Event MessageSimple(varMsg As Variant)

Function Send(Optional varFrom As Variant, Optional varTo As Variant, _
                 Optional varSubj As Variant, Optional varMsg As Variant)
     RaiseEvent Message(varFrom, varTo, varSubj, varMsg)
End Function
Function SendSimple(varMsg As Variant)
     RaiseEvent MessageSimple(varMsg)
End Function

Create a class called clsMsgReceiver and place the following into it:

Option Compare Database
Option Explicit

Dim WithEvents mclsMsg As dclsMsgClean

Private Sub Class_Initialize()
     Set mclsMsg = cMsg()        'Get a pointer to the public message class
End Sub

Private Sub mclsMsg_Message(varFrom As Variant, varTo As Variant, varSubj As Variant, varMsg As Variant)
Dim strMsgStuff As String

     On Error Resume Next
     strMsgStuff = "Hi, this is MsgRcvr.  My message is " & vbCrLf
     strMsgStuff = strMsgStuff & "From: " & varFrom & vbCrLf
     strMsgStuff = strMsgStuff & "To: " & varTo & vbCrLf
     strMsgStuff = strMsgStuff & "Subj: " & varSubj & vbCrLf
     strMsgStuff = strMsgStuff & "Msg: " & varMsg
     MsgBox strMsgStuff
End Sub

Private Sub mclsMsg_MessageSimple(varMsg As Variant)
     MsgBox "Hi, this is MsgRcvr.  My received message is: " & varMsg
End Sub

Create a module called basMsg and put the following into it.

Option Compare Database
Option Explicit

Private mclsMsg As dclsMsgClean
Private mclsMsgRcvr As clsMsgReceiver

Function cMsg() As dclsMsgClean
     If mclsMsg Is Nothing Then
         Set mclsMsg = New dclsMsgClean
         Set mclsMsgRcvr = New clsMsgReceiver
     End If
     Set cMsg = mclsMsg
End Function

Function mMsgTerm()
     Set mclsMsg = Nothing
End Function

Function mTest()
     cMsg.SendSimple "VolID=1;"
     cMsg.Send "mTest", , "VolID=1;"
     cMsg.Send "mTest", "frmMsgTest", "VolID=1;"
     cMsg.Send "mTest", "frmMsgTest", "VolID", 1

End Function

Now run mTest and watch what happens.

Now step through mtest and watch what happens.

Now you can dimension the messageclass in a form (see the header of clsMsgReceiver), get the pointer 
to the global message class (see the Class_Initialize) and sink messages in any form (see 
mclsMsg_Messge()).

Why do you do this?

Because the message sender (mTest in this case) can send messages and not care who is actually 
receiving them.  All it needs to know is that it needs to send a message.  No "is form xyz open" 
kind of stuff.

FrmXYZ can now sink events (messages in this case) and check whether it wants to actually process 
the message.  It can look for messages with its own name in the "To" field, or it can check the 
subject to see if it is a subject that it wants to process etc.

FrmABC can also sink events (messages in this case).  It can do the same thing, only handle messages 
sent "To" that form, or process all messages "from" a specific "sender" etc.

Just remember that the sender doesn't care whether the recipient is listening.

Now, let's take my actual case:

I have a volunteer form with a subform where the user adds and deletes cities that that volunteer 
wants to see locations and meetings in.

In the Cities subform I (literally) do the following:

Private Sub Form_AfterDelConfirm(Status As Integer)
     cMsg.send "sfrmVolunteerCities", "frmVolunteers", "RefreshLists"
End Sub

Private Sub Form_AfterUpdate()
     cMsg.send "sfrmVolunteerCities", "frmVolunteers", "RefreshLists"
End Sub

So if I update the form I send a message saying "refreshLists", and if I delete a record I send the 
same message.

As it happens frmVolunteers is the parent of sfrmVolunteerCities, but that really doesn't matter.

In frmVolunteers I dimension the message class withevents and then set it to the global message 
class pointer:

Private WithEvents mcMsg As dclsMsg

Private Sub Form_Open(Cancel As Integer)
     Set mcMsg = cMsg()
End Sub

I then sink the event:

Private Sub mcMsg_Message(varFrom As Variant, varTo As Variant, varSubj As Variant, varMsg As Variant)
     If varFrom = "sfrmVolunteerCities" Then
         If varTo = "frmVolunteers" Then
             If varSubj = "RefreshLists" Then
                 lstCities.Requery
                 lstMeetings.Requery
             End If
         End If
     End If
End Sub

In this case I am getting *very* specific but in the end all I am really doing is checking that the 
message is for me and then refreshing two lists.  These lists display all the locations and meetings 
in the cities I am adding in the subform.

Now... I *could* already have another form open which has some combos on it that also need to be 
refreshed...

in those two subforms I do the same thing:

Private WithEvents mcMsg As dclsMsg

Private Sub Form_Open(Cancel As Integer)
     Set mcMsg = cMsg()
End Sub

and then sink the event:

Private Sub mcMsg_Message(varFrom As Variant, varTo As Variant, varSubj As Variant, varMsg As Variant)
     If varFrom = "sfrmVolunteerCities" Then
         If varSubj = "RefreshLists" Then
             cboMeeting.Requery
         End If
     End If
End Sub

Notice that in this case I ignore the To: and only filter on who the message is from and the 
subject.  If it is from the right place and is the right subject then I requery the combo.

The key information here is that:

1) Events are raised by the message class and pass parameters to the message sink
2) If there is no recipient, no harm no foul.
3) If there is a recipient, then the recipient decides what to do.
4) The sender does not have to check whether the recipient is open, or write code in the sender 
specific to what the recipient is supposed to be doing.
5) I can have no code actually open and sinking the message or I can have 20 places sinking the 
messages.  In any case, the *recipient* decides what to do with the message, *not* the sender.

In my case, each and every recipient needs to do something different but similar.  Requery 
something.  A pair of lists in one form, a different combo in each of two other forms.  The sender 
just says "I modified the list of cities".  The recipients says "OK, I need to do this thing when 
the list of cities changes" and then does that thing.



John W. Colby
www.ColbyConsulting.com

On 6/20/2011 11:06 AM, Dan Waters wrote:
> Hi John,
 > For a separate form, first determine if the form is open:



More information about the AccessD mailing list