Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations TouchToneTommy on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Opening form by function call - PROBLEMS! 7

Status
Not open for further replies.

Moss100

Technical User
Aug 10, 2004
584
GB

I have several forms which I need to pass criteria to when opening.


Rather than using the docmd.... I have a function in the form which I call and this results in the form opening.

Doing this lets me pass multiple criteria to the form when it opens.

The code in the form is like...

Form_frm_Email.fnc_SetUpForm SetForm_Title:="Email To Landord: " & Me.Lan_1_Name_Long, _
SetAddress_To:=Me.Lan_1_Email, _ etc...

To Open the form from another using a button I use:

Public Sub fnc_SetUpForm(ByVal SetAddress_To As Variant, _
Optional ByVal SetAddress_CC As Variant = Null etc....

This all works fine, BUT I have the following problem...

When opening the form (although it is set as Modal and Popup), the code from the open button continues to execute, rather than halting when the form opens.

I also note that when the form is opened by using the function, there is no design view avaiable (although if i just open the form norally design view is avalable).

I would very much appreciate thoughts about what is different when the form is opened through a function and also if there is away I can get the code to stop running on opening, but still retain the method of opening the form by calling the funstcion.

Many thanks Mark
 
[lol] - very enjoyable read this one!

So am I right in thinking that if you call a function against a form that isn't open, it opens the form?

Is that like when in development and the minute you alter any class form code, the form is auto opened in edit mode?

-> OK, I just tested it and it does... interesting.

Code:
Private Sub Form_Open(Cancel As Integer)
 MsgBox "I opened"
End Sub
Public Function test() As Boolean
   
End Function

Then in the immediate window...

Code:
Form_Test_Form.test

The response was "I opened"

Is this considered bad practice to instantiate the form class by calling a public method in its protocol?

Can you set OpenArgs any other way before hand, or would you simply make the instantiating method called take all the arguments you require?

If using this method is OK, (Though should a form have public methods like this?), are there any caveats to be considered?



"In complete darkness we are all the same, it is only our knowledge and wisdom that separates us, don't let your eyes deceive you."

"If a shortcut was meant to be easy, it wouldn't be a shortcut, it would be the way!"
Free Electronic Dance Music
 
Since you call [tt]Form_frm_Email_Flexible.fnc_SetUpForm[/tt] I assume when you use "Me" in this Sub, you refer to [tt]Form_frm_Email_Flexible[/tt], right?

And the very first line:
[tt]Me.txt_Email_To = SetAddress_To[/tt]
populates the text box named [tt]txt_Email_To[/tt] with the passed value of (Variant)[tt] SetAddress_To[/tt]

In order to assign the value to this control, it needs to be there, which means the Form (this control is on) needs to load.

I would guess if you put a brake on Form_Load (in Form_frm_Email_Flexible) this event fires before your first line of code is executed.

Just my $0.02...

Have fun.

---- Andy

A bus station is where a bus stops. A train station is where a train stops. On my desk, I have a work station.
 
> if you call a function against a form that isn't open, it opens the form?

Completely instantiates it.

>Is this considered bad practice to instantiate the form class by calling a public method in its protocol?

Well, efficitively:

Form_Test_Form.test

is just a shorthand for:

Dim myForm as New Test_Form
myForm.test

So not necessarily bad prqactice. Just perhaps unexpected to some.

The issue being encountered is that COM classes do not support parameters with their contructors. So Access wraps the constructor in a bunch of support code when it uses DoCmd.OpenForm. That support code then provides additional control of the behaviour of the form - and is why we get different behaviour if we instantiate the form directly from VBA rather than indirectly through DoCmd.


 
Thank you all for your input.

I have used this method for a good deal of time without any problems, other than the calling code does not stop running on form open (often this is not required anyhow).

A workaround to this is to put a continual loop on the open form which runs until the form is closed.

Out of interest to others, in most situations when you just want to open a form, the method works fine.

I do not use it for all forms, but for certain forms where I am required to pass a fair bit of criteria it is very useful.

For example I use this method for opening my email form, which is used in many different areas of my program. The advantage here is that when programming the opening form call, the function displays all the criteria options, which speeds up development.

Thanks as always for the input.

Regards and happy new year. Mark.
 
Hey Mark,

There is a 'sleep' but it's not very good practice to try to pause code.

I can see doing what you do could be useful to pass a form arguments of specific data types, as OpenArgs is limited.

Though I tend to use MVC type logic and decouple my code from the form and outsource it to classes.

Again the lack of a constructor with signatures is a pain, so I use the same logic as you and have a special 'SetObject' type method for passing in all the required data to my model class.

This way my buttons simply open forms (sometimes with the odd pipe delimited string OpenArgs), that has a private form scoped class object (Model / Controller) that is required to do the actual work.

I use the MVC paradigm 'UpdateView' form method for keeping the GUI and the model object in sync with each other. It's been a challenge trying to apply a true OOP MVC paradigm to VBA, but I think I have managed to do a fairly decent job.

It's not like I don't have the odd conditional loop waiting for something specific to happen or finish...
Code:
' Check outbox and see if email is still sending
Do While CheckOutbox() And iTimeOutCnt <= iTimeOut
    Call AddProgress("Still sending email, please wait.")
    iTimeOutCnt = iTimeOutCnt + 3
    Sleep 3000
Loop
But I do try to keep it to an absolute minimum.

Happy new year, Craig.



"In complete darkness we are all the same, it is only our knowledge and wisdom that separates us, don't let your eyes deceive you."

"If a shortcut was meant to be easy, it wouldn't be a shortcut, it would be the way!"
Free Electronic Dance Music
 
As you have found out, there is no way to pass an access form complex variables and instantiate the form in dialog (stop the code) mode. There are several workarounds, none are real clean.

I do not use it for all forms, but for certain forms where I am required to pass a fair bit of criteria it is very useful.
Although this works you have not actually gained anything by opening the form in this way. Since you cannot open a form in dialog mode by instantiating it in this manner you have the same capabilities and limitation you would have if you simply used the docmd.open form method (without using acdialog). In other words just open the form regularly and then call your function. (Note: understanding what you are doing is important if planning to work with multiple instances of the same form).
As stated the form or report module is simply a special Class module that includes a gui component. You can do everything with it that you can do with a custom class to include adding setters and getters and instantiating multiple objects at once. The only thing you cannot do is raise custom events and the form is not added to the forms collection. You can trap events from other objects.
But as was pointed out when a form opens through direct/indirect instantiation without the docmd method it cannot be opened dialog (cannot stop code execution in the calling code). No idea why that is. So that means you could have opened the form using the Docmd.openform method and then called your function. This is cleaner IMO, and from the confusion it appears others also think so.
I would call it "bad practice", because your approach it is not really clear and direct. When things happen as a side-effect of code or event execution, code can get confusing to follow and debug. You are calling a function in a class module that has the side effect of opening the form of that class. If you do not want to use the docmd method then do it more directly like Strongman shows. That way it is clear what is happening

Code:
   Private myForm as Form_frmOne

   Private cmdButton_Click()
     set myForm = new Form_frmOne
     myForm.SetUpForm SetAddress_To:=Me.Lan_1_Email, _
         SetAddress_CC:=IIf(Me.chk_Lan_1_Email_CCLan2, Me.Lan_2_Email, Null), _
         SetAddress_BCC:=Null, _
         SetSubject:=Null, _
         SetBody:=Null 
     myForm.visible = true
   end sub

This works the same as your code, but it is clear where and how the class gets instantiated versus indirectly by calling a function in the class.
Note the docmd.open form has a benefit that it adds a pointer to the forms collection. This is kind of a big deal, because as you see I have to declare the myform variable at the module level. If I would declare it at the procedure level then as soon as the procedure ends the variable would go out of scope and the form would no longer show.
An extreme example of how this is not clear would be if you had code in the on open event of the form being called. Lets say you had a message box with "Hello World" in the on open event of your form. Calling your function would instantiate the object and the form opens invisibly and the message hello world would appear.

Bottom line you are not gaining anything opening the form this way, since there is no way to stop code execution and vba does not let you create a parameterized constructor. You would get the exact same results and benefits of the function by doing it a little more traditionally/direct using the docmd method.

Code:
dim frmEmail as access.form
Docmd.openform "frm_email_Flexible"
set frmEmail = forms("frm_email_Flexible")
frmEmail.fnc_SetUpForm SetAddress_To:=Me.Lan_1_Email, _
         SetAddress_CC:=IIf(Me.chk_Lan_1_Email_CCLan2, Me.Lan_2_Email, Null), _
         SetAddress_BCC:=Null, _
         SetSubject:=Null, _
         SetBody:=Null

But your real problem is there is no real clean way to open a form in dialog mode and pass it complex objects. One way to do this in access is to set a public variable and then read the variable when the form opens.
You could do something like this in a standard module

Code:
Public myEmailInfo as EmailInfo
Public Type EmailInfo
    To as variant
    CC as variant
    BCC as variant
    Subject as variant
    Body as variant
end Type
Then in your calling code

Code:
myEmailInfo.To = Me.Lan_1_Email
MyEmailInfo.CC = IIf(Me.chk_Lan_1_Email_CCLan2, Me.Lan_2_Email, Null)
MyEmailInfo.BCC = Null
MyEmailInfo.subject =Null
MyEmailInfo.Body = Null
Docmd.OpenForm "YourFormName",,,,,acdialog

Then in your forms open event

Code:
  Me.txt_Email_To = myEmailInfo.To
  Me.txt_Email_CC = myEmailInfo.CC
  Me.txt_Email_BCC = myEmailInfo.BCC
  Me.txt_Email_Subject = myEmailInfo.Subject
  Me.txt_Email_Body = myEmailInfo.Body

But as you can see not having a parameterized constructor in vba or an "openargs" that accepts variables is a large limitation.
If I was weighing all the pros and cons I would use some helper functions that Duane shows to make it easy to pass and read a lot of openargs information. That allows me to decouple my design as best as Access provides. If I have to pass objects then I am setting public variables and having the form read the public variable.
 
Thank you all for the time taken to discuss this matter.

The various approaches are interesting. If I understand correctly it would seem that using the docmd and then calling a function would be a more straight forward way of doing what the code is already doing at present and in instances where I need the calling code to stop executing using the opensargs like Duane suggests is again a good method.

Many thanks to all. Mark.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top