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.