Pass information back from called form (8 Viewers)

pdanes

Registered User.
Local time
Today, 09:20
Joined
Apr 12, 2011
Messages
225
I wrote up an article about a systematic way I developed of passing information back from a form called in dialog mode to the form that called it. I tried to publish it here, but this site has a limit for the number of characters allowed in a post, and my article exceeded it, by quite a bit. I published it in a different forum, but this site has been quite useful to me over the years, so I am putting a link here to the article. Hope it will be useful to someone.

Link removed my moderator, until clarification received on whether these type of links are allowed or not!
 
Last edited by a moderator:
You could upload a PDF rather than embedding the article's text.
 
I wrote up an article about a systematic way I developed of passing information back from a form called in dialog mode to the form that called it.

Alternative Method for Passing Data Between Forms in MS Access

Hi pdanes,

I’ve seen your method for passing information between forms in MS Access, and it looks like a neat solution. I’ve worked on a similar system myself with something I call the "Call" "Called" class module. It handles passing data between popup forms and the form that called them, but it uses a different approach, more object-based.

I think our methods are pretty similar in what they achieve. Yours works well with strings, and mine is more focused on keeping everything organized in a class. If you’re interested, I’ve got the code and a few videos explaining how it works on my website:


Would be great to hear your thoughts on it and see how the two ideas compare!

Cheers
Tony
(Uncle Gizmo)
 
Thank you both for posting & especially the explanation of how it works. I lost a lot of time trying to figure out how different ones worked & found tutorials out there to be scarce/ hard to find. Even the basic fundamental of transferring a value from one form to another I found confusing & info lacking. I only managed to get it done thanks to @MajP & some others did I manage to move forward.

Don't hesitate to upload .accdb's of these @pdanes & @Uncle Gizmo, thanks again enjoying reading/ viewing these.
 
Last edited:
In the days of structured programming, we studied the concepts of coupling and cohesion. Coupling described how modules wee connected to each other and cohesion described how code within a module related to other code in the same module. The objective was always to make modules highly cohesive and loosely coupled. A module should perform one and only one task and therefore taking arguments to control its internal logic usually violates the principles of one module, one task. Truly common code can of course be called from multiple other modules. Identifying common code can sometimes be a problem.

What you have is a fairly common requirement to have two forms exchange a couple of pieces of data. That isn't actually common code since it is quite unlikely that all arguments are always the same between any two random forms and calling them arg1, arg2, etc simply obfuscates the variable names for no gain. My approach is to pass the calling form name as an argument using the OpenArgs. That allows the called form to instantiate a form object and using that object, reference actual controls on the calling form to get/put data. OR, if I simply want to display values from the original form for convenience, I hard code the reference in the form's controls.
 
Alternative Method for Passing Data Between Forms in MS Access

Hi pdanes,

I’ve seen your method for passing information between forms in MS Access, and it looks like a neat solution. I’ve worked on a similar system myself with something I call the "Call" "Called" class module. It handles passing data between popup forms and the form that called them, but it uses a different approach, more object-based.

I think our methods are pretty similar in what they achieve. Yours works well with strings, and mine is more focused on keeping everything organized in a class. If you’re interested, I’ve got the code and a few videos explaining how it works on my website:


Would be great to hear your thoughts on it and see how the two ideas compare!

Cheers
Tony
(Uncle Gizmo)
Sorry for lagging - my availability comes and goes.

Yes, I like what you've done there. It allows a popup form to manipulate an element on the calling form without requiring the popup form to know anything about the calling form. This would allow you to use the same popup from multiple places, assuming those places all needed similar functionality from the popup.

I mostly needed to have the calling form DO something, rather than fill in some information, and I rather deliberately limited myself to text strings, copying the .INI file approach. I was trying to tidy up an unholy mess of parameter handling that I had created over the years, and wanted to keep it as simple as I could while I was refining the method and cleaning up random junk I had written when I had not yet built a systematic way to pass parameters. When I got done, I realized that text strings were handling everything I needed in this app, so I left it that way, and started doing it the same way in new development.

I considered making some sort of class for passing parameters, but finally rejected it, since having that squirreled away in a custom class would somewhat hide the information I was passing, and the temptation to make the class do some clever things that I would subsequently forget about would be great. This way, all is clear - the parameter string is assembled right before the call to the popup, and returned parameters are dealt with right after, so I don't have to go poke around in a separate class module to ascertain what information is being passed around, and possibly modified, or something 'invisible' done with it.

I have also modified much of my code to utilize custom events. I recently did a great deal of reading and experimentation in this. I had been constantly reading about custom events, and could not get anything to work reliably, so I finally set myself a goal. After a massive amount of hair-pulling, I finally got a grip on the issue - there is a serious lack of documentation on this topic, at least for VBA. Maybe for someone who grew up with the modern OO languages, such things are obvious, but it was not for me. I now have any action that modifies data in a table call a custom event in a special class module written just for that, which broadcasts the information about the change to anyone who may be listening. This completely unlinks the code doing the change from code that must react to any changes. The code doing the change simply broadcasts information about what it did, neither knowing nor caring if anyone is listening. The code that must respond to a change does what is appropriate when an event informs it that its data has changed, neither knowing nor caring who did it (usually things like repopulating a combobox, or refreshing a datasheet). Wish I had taken the time to learn all that earlier - when I think of all the time and effort I wasted doing such updates without custom events...
 
Last edited:
My approach is to pass the calling form name as an argument using the OpenArgs. That allows the called form to instantiate a form object and using that object, reference actual controls on the calling form to get/put data. OR, if I simply want to display values from the original form for convenience, I hard code the reference in the form's controls.
That rather violates the principle of loose coupling. Having knowledge of the calling form hard-coded into the called form seems like exactly the sort of thing you would want to avoid. What if you want to call that form from different places? I need to do exactly that quite often, and that was one of the hassles that finally prompted me to write the code that was the basis for my original post - a method of cleanly passing an arbitrary number of parameters in both directions.
 
Here's a simple way you can communicate between objects in an application without them knowing anything about each other. This example passes a command button and a string to a form opened with the acDialog switch, after it opens, from the form that opened it.

To run the sample, open the fTest form, and click the button.
 

Attachments

Here's a simple way you can communicate between objects in an application without them knowing anything about each other. This example passes a command button and a string to a form opened with the acDialog switch, after it opens, from the form that opened it.

To run the sample, open the fTest form, and click the button.
Nice - lots of good functionality there in very few lines of code. I considered trying to make something like that, but decided that I didn't really know enough to be sure I could do it well, and since I was trying to make my existing constructs simpler and clearer, sticking to strings was a better choice than launching headlong down yet another path I didn't properly understand.

But this is elegant - definitely going into the queue for further study, then into the toolbox. Many thanks.
 
@MarkK demos a good solution when the called form is ACDIALOG, which makes the problem harder.
However I think people over think this in many cases where you only need to open the form Dialog / Modal but not stop code execution in the calling form. The only real reason you need ACDIALOG is to stop code execution (or in cases where sometimes you want to open it dialog/modal and other times you do not. Which for me is not a thing).

If you want to pass data to the popup form (not opened with ACDIALOG argument). You can pass in and pull out whatever you want and control the popup form. The Pop up form knows nothing about who called it. In fact in the demo here is my code (or lack there of)

Code:
Private Sub Form_Close()
 'does need a module so you can trap the event
End Sub

Now simply open the pop up, Pass any values, run events in the popup, change properties. Then simply capture when the popup closes and "pull" what you want.
In this Example I change the popup color and pass two values to the popup and return 2 values from the popup.
Code:
Option Compare Database
Option Explicit
Private WithEvents FP As Access.Form

Private Sub cmdPopup_Click()
  DoCmd.OpenForm ("frmPopUp")
  Set FP = Forms("frmPopUp")
  'demo set a property
  If Not IsNull(Me.cmboBack) Then FP.Detail.BackColor = Me.cmboBack
  'demo set some control properties
  FP.txtExchange1 = Me.txtExchange1
  FP.txtExchange2 = Me.txtExchange2
End Sub

Private Sub FP_Close()
  'return values from pop up by Trapping when pop up closes.
  Me.txtExchange1 = FP.txtExchange1
  Me.txtExchange2 = FP.txtExchange2
End Sub

1. No additional classes needed
2. No clumsy open args and code in the called form to handle open args
3. Completely decoupled. Pop up can be called by any form and calling forms can do different things with the pop up.
4. Allows you to push and pull unlimited values / properties
Again this solution only works when you do not require to stop code execution in the popup. My opinion people overuse acdialog because most of the time the pop up is (should be) modal / dialog.

popup.PNG

Maybe I am missing something, but the proposed solution in the PDF seems like a overly complicated solution to what can be done with little or no code as demoed here. The only limitation here is if you want to open the form ACDIALOG since your code stops and you cannot modify the Popup such as sending values to it. You still could pull values.
 

Attachments

Last edited:
The Pop up form knows nothing about who called it.
True, but the calling form knows about the popup, and explicitly references its named members, so this is still a tightly coupled approach. To improve this, the popup might implement an interface, and then the calling form can reference the interface. Then they would be more truly agnostic about each other. And they could exchange data via another abstraction like IEventArgs, maybe IPopDataExchangeArgs.

But the upshot to me is that using the acDialog switch on a form to implement in-process data collection adds complexity without advantage. If you have global event notification, though this enables you to pass data to an acDialog form, it also makes doing so unnecessary. The would-be acDialog form can instead raise a global event that it's data collection is complete, and broadcast the result. Also, @MajP 's approach is simpler and effectively eliminates the need to open an acDialog form for data collection.
 
That rather violates the principle of loose coupling. Having knowledge of the calling form hard-coded into the called form seems like exactly the sort of thing you would want to avoid.
The common code does something with form controls. ALWAYS the same thing. ALWAYS the same specificlly named controls or in other cases all controls of a particular type. Passing the form name means that if other forms need the same functionality, the called procedure will work for all forms requiring a particular functionality.

Doesn't violate any hard coupling rule that I can see. Passing a form name means the called code works for any form that needs that specific functionality.
What if you want to call that form from different places?
Executing code in a form externally isn't what we are talking about. We are talking about multiple forms calling 1 procedure and that single procedure is going to do the same thing to all the relevant controls on the calling form.

One case were I use this concept is when I need to lock/unlock particular controls on a form. The controls on any form that uses this common procedure use the Tag property to define controls that need special treatment. So, the tag either says Lock or Unlock or is empty. The procedure takes two arguments. The form object and a Boolean where true = lock and false = unlock. So, if no controls have a value in their Tag property, the code simply loops through all textboxes, combos, listboxes, checkboxes and sets them to locked or unlocked depending on the Boolean. The tag property is used to identify exceptions. For example, PK's are always locked and so you wouldn't want the common code to unlock them. And unbound search fields are always unlocked so you wouldn't want the common code to ever lock them. I use the tag property to identify EXCEPTIONS since it is far less work when setting up a new form since usually you have only a couple of controls that are always locked and/or always unlocked. Every other control simply obeys the direction of the Boolean.

Using the AllowEdits to lock/unlock the whole form doesn't give you the flexibility of leaving search fields unlocked.

I have similar procedures that do certain types of validation.
 
Last edited:
@MarkK

1. No additional classes needed
Passing string parameters also does not require any classes.

2. No clumsy open args and code in the called form to handle open args
Only clumsy if you make it so. Parsing strings is a trivial exercise in coding, and setting it up as a Select Case inside a loop makes it childishly simple to read, understand and add to if needed.

3. Completely decoupled. Pop up can be called by any form and calling forms can do different things with the pop up.
Not true - exactly the opposite. Your called form has hard-coded references to the guts of the calling form. That is precisely the opposite of decoupled.

4. Allows you to push and pull unlimited values / properties
That's somewhat true, but passing strings allows a huge amount of information as well - over 2 billion characters. And if you need to pass more than a few short parameters to a routine or form, you're probably doing something wrong anyway. Shoveling around huge amounts of information as parameters is almost always a mistake.

Maybe I am missing something, but the proposed solution in the PDF seems like a overly complicated solution to what can be done with little or no code as demoed here. The only limitation here is if you want to open the form ACDIALOG since your code stops and you cannot modify the Popup such as sending values to it. You still could pull values.
I see nothing complicated about a few parameters in a string, with a uniform method for delimiting. And my method IS decoupled, except for the one variable pointing back to the calling form, and even that is dynamically assigned at runtime. Source code in the called form and calling form do not need to know ANYTHING about each other. THAT is decoupled.
 
Doesn't violate any hard coupling rule that I can see. Passing a form name means the called code works for any form that needs that specific functionality.

"using that object, reference actual controls on the calling form to get/put data"

THAT is what violates the anti-coupling rule. Unless you do everything with loops and such, referring to an actual control requires knowledge of that control in source code. Such knowledge violates decoupling.
 
This idea of that is "tightly coupled" and this is "loosely coupled" is silly. It is a design consideration not a set of rules. Or at least I have never seen the "decoupling rules." It is a scale. You can compare two things as more or less dependent but there is no set of rules that determines the cut off between loosely and tightly coupled. It is kind of like porn according to Justice Stewart. I cannot define it "But I know it when I see it."
There are principles which you can google. It is like saying that is a "quality" car and that is not a quality car and the cut off is with Car model X. It is also contextual. If I am driving in Indy 500 my level of quality may need to be higher than for my beater for daily commute to the store. If I am building my own code or or a small team it may require a lesser level then a big project and large team. And the likelihood of things changing is a factor.

We seem to be suggesting if we instantiate an object and use it public properties and procedures exposed by the object then we are somehow tightly coupled. Even when the instantiated object has no knowledge of how it was insantiated. The only way not to be tightly coupled is to have a centralized event listener and all data exchange requires the use of interfaces. The use of interfaces is a tool that can improve decoupling and would move you farther up the decoupling scale, but I do not think that is the magical cutoff. And event driven architecture also can reduce coupling since you are reacting to events instead of directly calling objects. However, this then could be a race to ever increasing levels of abstraction with no real gain at some point.

To me this statement is a bit much.
using that object, reference actual controls on the calling form to get/put data"
THAT is what violates the anti-coupling rule. Unless you do everything with loops and such, referring to an actual control requires knowledge of that control in source code. Such knowledge violates decoupling.
Again that is saying if you use the public "interfaces" of an encapsulated class you are violating the mythical rules of anti-coupling. This puts most oop design into question. The magical bar is you cannot directly reference members of a class? Or just not form controls?

So lets walk the dog until we think it is "loosely coupled", based on some unwritten bar that must be met.

1. I am going to build my form and X is building the pop up. We work in different offices. I tell X that I am passing him in a value and need him to do something and pass back. I tell him my form is form1 and he says his form is formPopUp. I tell him to return the value txtBxF1 and he says to pass it in to txtbxP1. So in the close event of formPopUp, X writes

Code:
forms!form1.txtBxF1 = me.txtbxP1
2. Now I tell X that is too tightly coupled because I want to call formPopUp from form2. I think we probably can all agree this is too tightly coupled because formPopUp only works with form1. It is dependant. So I tell x I will pass in the calling form name and then X does this in the close event.

Code:
forms(me.openargs).txtbxF1 = me.txtbxP1
So now it is loosely coupled because I can use any calling form that has a control called txtBxF1? But wait. On form2 the control is called txtBxF2.

3. So now I call X and say give my a public variable that I can set, of type access control and I will pass in a reference direct to the control

So X does

Code:
Public callingCtl as access.control
I open the pop up and set the value of callingCtl

set forms("formPopUp").callingCtl = me.txtbxF1 (or me.txtbxF2 if called from form2)

and then X in the close event does
Code:
callingCtl = me.txtbxP1
So now it is loosely coupled, right? FormPopUp can work with any form with controls of any name with no knowledge of that form that called.

3. Wait a minute. Now I want form3 to call the pop up but do not want anything returned. I call X to tell them if callingCtl is nothing then do not do anything. But I cannot reach X to update the form to not break with form3. Also I do not want them to push the value because I first need to do some checks. What if I originally told them that I would simply "pull/push" the value as I wish.

Code:
Private WithEvents FP As Access.Form

Private Sub cmdPopup_Click()
  Set FP = Forms("formPopUp")
  FP.txtbxP1 = Me.txtBxF1
End Sub

Private Sub FP_Close()
  'return values from pop up by Trapping when pop up closes.
   dim rtn as variant
   rtn = FP.txtBxP1
   'do something with P1
End Sub

So now it is loosely coupled because I can control what I pull and push using public properties of the pop up being less dependent on the pop up to return something?

4. Wait a minute. X was told to by the boss to use a new control naming scheme and it broke and now the updated names breaks the code. So I ask X to build assessors for the value. Now it is decoupled.
So X does this

Code:
Public Property Get Val1() As Variant
  Val1 = Me.TextBoxP1
End Property

Public Property Let Val1(ByVal NewValue As Variant)
  Me.TextBoxP1 = NewValue
End Property

Great now it is decoupled. X can rename as much as he needs as long as he provides constant accessors. We have created clear defined interfaces to the properties. Is this enough to eliminate the concern:
sing that object, reference actual controls on the calling form to get/put data"
THAT is what violates the anti-coupling rule

5. Better yet X says. I will raise a custom event and report the value only if changed. Then you simply listen for the change.
So X does something like

Code:
Public mCancel As Boolean
Public Event PassData(Val1 As Variant)

Private Sub cmdCancel_Click()
  mCancel = True
  DoCmd.Close acForm, Me.Name
End Sub
Private Sub Form_Close()
 'does need a module so you can trap the event
  If Not mCancel Then
    RaiseEvent PassData(Me.txtBxP1.Value)
  End If
End Sub

I simply capture that event
Code:
Private Sub fp_PassData(Val1 As Variant)
  'listen for event and choose to do something.
end sub
So now it is loosely coupled?

6. Wait form2 wants to exchange 2 pieces of data not one. So lets build an interface where the exchanged arguments can be of any type ...
So now it is loosely coupled?

7...
8...

So where is the cutoff?
 
THAT is what violates the anti-coupling rule. Unless you do everything with loops and such, referring to an actual control requires knowledge of that control in source code. Such knowledge violates decoupling.
But THAT is what the function does! Functions are not all things to all people. You can't use functionA to perform actionB. functionA does something unique. They perform specific actions and if those actions reference specific form fields, THAT IS WHAT THE CODE DOES. The lock/unlock procedure references only specific control types but does not address them by name because it locks/unlocks all controls of specific types. On rare occasions, I create multiple forms (usually only two variations) to edit a record. Always a single record form that allows edits to all data fields but in some cases, a list version that only allows edits to a couple of fields to give the user a quick way to change certain fields in a slightly different context without having to go another level and open the full edit form. Rather than duplicate the validation logic, I break the common fields out to a function into which I pass the form object. The other fields are validated within the code module of the edit form. So each field of a record ends up being edited in one and only one place even if edits are allowed to originate from more than one form. Think of this like passing arguments by reference rather than by value.
 

Users who are viewing this thread

Back
Top Bottom