Is there any way to update a ListBox on a UserForm outside of the Userform_Initialize sub?
Why?
I am building a blackjack game and using listboxes to tell the user what cards they have/ the dealer has. I was hoping to use a simple sub (ShowCards) to add items to the listboxes but I've run into problems:
The Play button calls the PlayBlackjack sub which sits in a normal module
Option Explicit
Dim cards As New Collection
Sub ShowGame()
UFDisplay.Show
End Sub
Sub PlayBlackjack()
'fill the cards collection with 5 shuffled decks
PrepareCards
Dim i As Integer
Dim userHand As New Collection
Dim dealerHand As New Collection
'deal cards (removing the dealt cards from the cards collection)
For i = 1 To 2
DealCard cards, userHand
DealCard cards, dealerHand
Next i
ShowCards userHand, UFDisplay.UserHandList <-- ERROR HERE (Type mismatch)
'more code to follow
End Sub
Private Sub ShowCards(hand As Collection, list As ListBox)
Dim i As Integer
For i = 1 To hand.Count
list.AddItem hand(i).CardName
Next i
End Sub
Let me know if you think you need any more of the code. hand is a collection of card classes where .CardName returns something like 3 of Hearts
Everything I read seems to tell me that the userform is static after being initialized so I would need to refresh it in some way after adding the new items. I tried Userform.Repaint with no luck.
So if there really is no other way, should I declare userHand and dealerHand as global variables, update them and call Useform_Initialize to take the updated values and show them to the user? Given the nature of the game being that multiple more cards could be dealt for both players it doesn't seem sensible to be re-initializing the userform multiple times each game.
All suggestions welcome. If you think I should have done it completely differently I'd still be keen to hear (but not so interested in worksheet solutions)
Update #1
For clarity, ShowGame is called by a button on the workshet, then PlayBlackjack is called from the Play button on the Userform (nothing else in the userform code)
Ah, I see. You cannot declare the listBox parameter as ListBox. The latter is reserved for Activex controls, not VBA controls. Change the signature of your ShowCardssub into this:
Private Sub ShowCards(hand As Collection, list As Control) '<~~ or MSForms.ListBox, or simply as Object...
You could use a class for the hand also, and set the class listbox to be the form listbox, for example, the class clsHand
Public colHand As collection
Public lstToUpdate As MSForms.ListBox
Private Sub Class_Initialize()
Set colHand = New collection
End Sub
Friend Function AddCard(card As clsCard)
colHand.Add card, CStr(colHand.Count)
If Not lstToUpdate Is Nothing Then
lstToUpdate.AddItem card.strCardName
End If
End Function
It's use in a form
Private clsPlayerHand As clsHand
Private Sub UserForm_Initialize()
Set clsPlayerHand = New clsHand
Set clsPlayerHand.lstToUpdate = Me.ListBox1
End Sub
Private Sub CommandButton1_Click()
Dim clsC As New clsCard
clsC.strCardName = "one"
clsPlayerHand.AddCard clsC
End Sub
EDIT: Recommendation,
Use the numeric and the suit name for your cards, then you can do the following, say enabling the split button, incidentally, you'd use an array of hands then arrHands(x) ...
Public colHand As collection
Public lstToUpdate As MSForms.ListBox
Public cmdSplitButton As MSForms.CommandButton
Private Sub Class_Initialize()
Set colHand = New collection
End Sub
Friend Function AddCard(card As clsCard)
colHand.Add card, CStr(colHand.Count)
If Not lstToUpdate Is Nothing Then
lstToUpdate.AddItem card.CardName
End If
If Not cmdSplitButton Is Nothing Then
If colHand.Count = 2 Then _
cmdSplitButton.Enabled = colHand(1).NumericPart = colHand(2).NumericPart
End If
End Function
Look at using classes to their full potential and look at events also, to react to certain things.
Related
tl;dr Is there a way to enable events for built-in objects without coupling the event to the original object's parent, assuming the event interacts with the parent?
Disclaimer 1: I don't have access to MS Office on my home machine and therefore type all code from memory. I'm sorry if something's incorrect.
Disclaimer 2: This post is incredibly lengthy because I've been trying to figure out how to do this process for several years but never quite hit the correct Google terms to figure it out. I do a lot of explaining in the hopes that it might help someone with the same issues.
The Original Problem
I've had this longstanding issue of having Userforms with near-identical event handling but no way to compact the code into a generic solution. For example, let's say I have a Userform with a bunch of Command Buttons that all do the same thing when clicked. Traditionally, you would have to include something like the following in Userform1
Private Sub CommandButton1_Click()
Me.DoSomething CommandButton1.Name
End Sub
Private Sub CommandButton2_Click()
Me.DoSomething CommandButton2.Name
End Sub
'...a bunch more of these...'
Private Sub CommandButtonN_Click()
Me.DoSomething CommandButtonN.Name
End Sub
This is annoying to setup and hurts readability for a large number of buttons.
The Naive Solution
I recently discovered that wrapper classes can be utilized to make a generic WithEvents handler for built-in objects. Applying this to our previous example, we create an EventCommandButton.cls Class with the following code
Private WithEvents mCommandButton as MSForms.CommandButton
Private Sub mCommandButton_Click()
mCommandButton.Parent.DoSomething(mCommandButton.Name)
End Sub
Property Get CommandButton() as MSForms.CommandButton
Set CommandButton = mCommandButton
End Property
Property Set CommandButton(cmdBtn as MSForms.CommandButton)
Set mCommandButton = cmdBtn
End Property
And Userform1 turns into
Private EventCommandButtons() as New EventCommandButton
Private Sub Userform1_Initialize()
For Each ctl in Me.Controls
If TypeName(ctl) = "CommandButton" Then
i = i + 1
ReDim Preserve EventCommandButtons(1 to i)
Set EventCommandButtons(i).CommandButton = ctl
End If
Next
End Sub
This approach saves space and looks comparatively nice, but it presents (at least) 2 major issues:
All of Userform1's control events are no longer housed in its own code
Our EventCommandButton requires a specific procedure (DoSomething(str)) to exist in its parent or else we'll get an error.
A Slight Refinement
The solution I'm currently implementing is to take a more intuitive approach that returns control of the event handling back to where you'd expect it to be. In EventCommandButton.cls we add a new property to specify where we expect to find the return code:
Private mCommandButton as MSForms.CommandButton
Private mCallback as Object
Private Sub mCommandButton_Click()
'Some error handling should be here to check that mCallback is set
mCallback.EventCommandButton_Click(mCommandButton)
End Sub
Property Get Callback() as Object
Set Callback = mCallback
End Property
Property Set Callback(ParentObject as Object)
'Let's not assume it's always the .Parent
Set mCallback = ParentObject
End Property
Property Get CommandButton() as MSForms.CommandButton
Set CommandButton = mCommandButton
End Property
Property Set CommandButton(cmdBtn as MSForms.CommandButton)
Set mCommandButton = cmdBtn
End Property
And in Userform1
Private EventCommandButtons() as New EventCommandButton
Public Sub EventCommandButton_Click(cmdBtn as MSForms.CommandButton)
Me.DoSomething cmdBtn.name
End Sub
Private Sub Userform1_Initialize()
For Each ctl in Me.Controls
If TypeName(ctl) = "CommandButton" Then
i = i + 1
ReDim Preserve EventCommandButtons(1 to i)
Set EventCommandButtons(i).CommandButton = ctl
Set EventCommandButtons(i).Callback = Me 'Set new property
End If
Next
End Sub
This approach feels close to the intuitive solution of the original problem (with some extra steps involved) and resolves issue #1 from the previous, but we still have issues:
There's still coupling between the Class and Userform, now requiring that each parent object must have corresponding pseudo-event procedures of the form Public Sub [ClassName]_[EventName]([OriginalObject], Optional [EventParams]), which isn't intuitive and looks weird amongst the sea of Private Event Subs.
The coupling now depends on the class name, which may not always be ideal. Renaming the class will require editing the events to reflect that.
For the wrapper to be "complete", it must include all events and error handling to ignore the ones that aren't setup on the Parent side. At some point I'd think having all these On Error GoTo EoF statements in each class instance will have a performance impact.
The Question
Is there a way that this process can be further improved to reduce the coupling between (in this case) the Class and Form code? With VBIDE we could detect the classname and generate the pseudo-events, but without VBIDE access it seems like it requires some upkeeping and instruction to properly utilize the class.
In Python (and I'm sure other languages), you could just pass a reference to a function to direct the event returns; however, VBA doesn't seem to support this.
If you can pass the method name from the parent as a string you could use something like CallByName mCallback, vbMethod, mProcName, mCommandButton from within the class instance, to call the method mProcName on the parent, passing the clicked-on button.
For example:
Event class (properties changed to public fields for brevity)
Option Explicit
Public WithEvents mCommandButton As MSForms.CommandButton
Public mCallback As Object '<< object on which the callback method is to be called
Public mProcName As String '<< name of the callback method
Private Sub mCommandButton_Click()
CallByName mCallback, mProcName, VbMethod, mCommandButton
End Sub
Form code:
Private EventCommandButtons As Collection
Public Sub ButtonClick(cmdBtn As MSForms.CommandButton)
MsgBox "clicked on button " & cmdBtn.Caption
End Sub
Private Sub Userform_Initialize()
Dim ctl As Object
Set EventCommandButtons = New Collection
For Each ctl In Me.Controls
If TypeName(ctl) = "CommandButton" Then
EventCommandButtons.Add NewClickHandler(ctl)
End If
Next
End Sub
Function NewClickHandler(btn As Object) As EventCommandButton
Set NewClickHandler = New EventCommandButton
Set NewClickHandler.mCommandButton = btn
Set NewClickHandler.mCallback = Me
NewClickHandler.mProcName = "ButtonClick"
End Function
I have created a VBA UserForm which is essentially a glorified input box
Just like an input box can be employed like this
Dim returnVal As String
returnVal = InputBox("Write some string")
I would like my userform to run like this
Dim returnVal As customClass
Set returnVal = MyUserForm([some arguments])
ie. the MyUserForm() code passes some arguments to the userform, and when the userform is closed, it gets some arguments back (in the form of a custom class rather than a plain string)
What's the best way of structuring my userform to allow this functionality?
Currently, I'm just declaring some variables and the custom class publicly. I'm catching command button clicks and Query_close() events to hide the form, then I read the outputVal and close the form completely. I don't like this because I'd like my form to be totally self contained, and I think the capturing of events is messy.
In simplified code (read/return a string):
Function myUf(inVal As String) As String
Dim frm As New frmTest
frm.inputval = inVal
frm.Init 'sets caption. We cannot rely on userform initialize as this runs before inputval is set
'We could pass a variable here to save writing to the public variable
frm.Show
myUf = frm.outputVal
Set frm = Nothing
End Function
And in my Userform called frmTest with a textbox called tb1
Public inputval As String
Public outputVal As String
Public Sub Init()
Me.Caption = inputval 'setting caption, but could pass this anywhere
End Sub
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
If CloseMode <> 1 Then Cancel = 1
outputVal = tb1 'reading value from textbox, but could return anything here
Me.Hide
End Sub
You need to find a way to initiate the UserForm from a ClassObject. Then, you can use a simple factory pattern to create the UserForm exactly the way you want.
In general, I have copied a bit of the code of Mat's Mug somewhere in StackOverflow and I wrote an article about the User Forms. If you take a look here (http://www.vitoshacademy.com/vba-the-perfect-userform-in-vba/) you will find a way to initialize the form with Public Sub ShowMainForm() It's possible to add a parameter to the ShowMainForm, then pass it to the initializer of the class.
In general, take the code from the article, make sure it works, and change the ShowMainForm initializer to the following:
Public Sub ShowMainForm(strText As String, strText2 As String)
If (objPresenter Is Nothing) Then
Set objPresenter = New clsSummaryPresenter
End If
objPresenter.Show
Call objPresenter.ChangeLabelAndCaption(strText, strText2)
End Sub
Then, if you call like this in the immediate window:
call ShowMainForm("Just","testing")
You will get this:
Which is quite what you need. :)
The basic idea is:
Create a Function in witch you combine your arguments to a string like:
strOpenArg = "param1:=value1;param2:=value2;"
than open the form with the OpenArgs
DoCmd.OpenForm "UserForm", acNormal, , , , acDialog, strOpenArgs
get your value and close the Form
Value= Form_UserForm.Value
DoCmd.Close acForm, "UserForm", acSaveNo
in the UserForm set Form_open. Here you can get your parameters.you can devide this by string splitting.
Set also an OK Button, where you make the form just invisible and set the return value
Private Sub Form_Open(Cancel As Integer)
Dim strParameter as String
strParameter = Me.OpenArgs 'Here are your parmeters
End Sub
Private Sub ok_Click()
m_Value = "Your ReturnValue"
Me.Visible = False
End Sub
Private m_Value As String
Public Property Get Value() As String
Value = m_msgBoxResult
End Property
There's no way to one-liner the code like you want to, unfortunately. If all your userform code is self-contained then the only way for it to pass values out is to change the values of public variables. Mat's Mug's answer here is the layout I usually use when trying to simulate functions like 'InputBox' but you still can't get it in one line without writing a separate function. Using userform properties allows you to contain more of your code within the form itself.
I have a userform which assembles itself at runtime, by looking in a folder and extracting all the pictures from it into image-controls on my form. What makes the process a little more complex is that I'm also using the image-controls' events to run some code.
As a simplified example - I have a form which creates a picture at runtime, the picture has an on-click event to clear its contents. To do this I have a custom class to represent the image object
In a blank userform called "imgForm"
Dim oneImg As New clsImg 'our custom class
Private Sub UserForm_Initialize()
Set oneImg.myPic = Me.Controls.Add("Forms.Image.1") 'set some property of the class
oneImg.Init 'run some setup macro of the class
End Sub
In a class module called "clsImg"
Public WithEvents myPic As MSForms.Image
Public Sub Init() 'can't put in Class_Initialise as it is called before the set statement - so myPic is still empty at that point
myPic.Picture = LoadPicture(path/image)
End Sub
Public Sub myPic_MouseDown(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
onePic.Picture = Nothing
End Sub
The problem is, this doesn't display the changes, and I realised I needed a imgForm.Repaint in there somewhere - the question is, where?
Attempts
First option is to put it in the Init() sub of clsImg. (ie. have a line imgForm.Repaint at the end of the click event) That works, but not ideal as the class can then only be used with the userform of the correct name.
A 2nd idea was to pass the userform as an argument to Init()
Public Sub Init(uf As UserForm) 'can't put in Class_Initialise as it is called before the set statement - so myPic is still empty at that point
myPic.Picture = LoadPicture(path/image)
uf.Repaint
End Sub
And called with
oneImg.Init Me
That works too, but would mean that wherever I require a repaint, I would have to pass the parameter which is also not ideal - the code is in reality a lot more complex than is shown here, so I don't want to have to add in this extra parameter unless necessary
The third option which I'm currently using is to pass the userform object to the class and save it there.
So with a Public myForm As UserForm at the top of my class module, I can pass the userform with the Init(uf As UserForm) and have a
Set myForm = uf 'Works with a private "myForm"/ class Property
Or I can set it directly from the userform code with a
Set clsImg.myForm = Me 'only if "myForm" is Public
But what does this do for memory - does saving the userform as a variable in my class take up a lot of memory? Bear in mind that in my real code I declare an array of clsImgs that can be of the order of >100 instances so I don't really want to be making copies of the UF in each class if that's what this method does. Also, it's ugly
What I really want...
... is a way of telling the userform that it needs to repaint, rather than directly repainting from within the class. To me this says I need an event to occur in my class, which the userform hears with some custom event handler. Exactly how Worksheet_Change works, the sheet object raises a change event, the sheet class code handles it.
Is such a thing possible (I suppose I would have to declare clsImg WithEvents - can you do that for an array?), or is there a better alternative. I'm looking for a method which does not impede performance with a large number of classes declared, as well as one which is portable and easily readable. This is my first use of Classes so I may be missing something really obvious!
Since good practice is that classes are self-contained (as you obviously know) the clsImg should indeed not have to be aware of the UserForm and thus shouldn't tell the UserForm to repaint.
What this calls for, is indeed that the clsImg raises an event that the UserForm hooks into, so it repaints based on that event, or, in your own words: "a way of telling the userform that it needs to repaint."
I replicated your Custom Class (clsImg) as follows (wanted to use a proper Setter / Getter, functionality doesn't really change)
clsImg Code:
Private WithEvents myPic As MSForms.Image 'Because we need the click event.
Public Event NeedToRepaint() 'Because we need to raise an event that the UserForm can hook into.
Public Property Let picture(value As MSForms.Image)
Set myPic = value
End Property
Public Property Get picture() As MSForms.Image
Set picture = myPic
End Property
Public Sub myPic_MouseDown(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
myPic.picture = Nothing
RaiseEvent NeedToRepaint
End Sub
Next, in the UserForm we hook into this NeedToRepaint Event that's raised during the Event Handler of the MouseDown of the picture.
UserForm1 Code:
Private WithEvents oneImg As clsImg 'Our custom class
Private Sub oneImg_NeedToRepaint() 'Handling the event of our custom class
Me.Repaint
End Sub
Private Sub UserForm_Initialize()
Dim tmpCtrl As MSForms.Image
Set oneImg = New clsImg
Set tmpCtrl = Me.Controls.Add("Forms.Image.1")
tmpCtrl.picture = LoadPicture("C:\Path\image.jpg")
oneImg.picture = tmpCtrl
End Sub
The second part of your question is whether you can use this in an array.
The short answer is "no" - Each object would have to have it's own Event Handler. However, there are ways to work around this limitation by using a Collection or some similar approach. Still, this wrapper will have to be "UserForm aware" since that's where you'll be repainting. The approach would be something like in this article
EDIT: A solution / workaround for not being able to use an Array:
Since I really liked this question - Here's another approach.
We can apply somewhat of a PubSub pattern as follows:
I did a quick build for CommandButtons, but no reason that it can not be made for other classes of course.
Publisher class:
Public Event ButtonClicked(value As cButton)
Public Sub RegisterButtonClickEvent(value As cButton)
RaiseEvent ButtonClicked(value)
End Sub
'Add any other events + RegisterSubs.
In a regular class, I setup a factory routine to keep this specific Publisher a singleton (as in: It will always be the very same in memory object that you're pointing at):
Private pub As Publisher
Public Function GetPublisher() As Publisher
If pub Is Nothing Then
Set pub = New Publisher
End If
Set GetPublisher = pub
End Function
Next, we have the UserForm (I just made one with 4 buttons) and the button class to utilize this Publisher. The Userform will just subscribe to the event it raises:
Userform code:
Private WithEvents pPub As Publisher 'Use the Publishers events.
Private button() As cButton 'Custom button array
Private Sub pPub_ButtonClicked(value As cButton) 'Hook into Published event.
MsgBox value.button.Caption
End Sub
Private Sub UserForm_Initialize()
Set pPub = GetPublisher 'Private publisher for getting it's event. Will be always the same object as long as you use "GetPublisher"
Dim i As Integer
Dim btn As MSForms.CommandButton
'Create an array of the buttons:
i = -1
For Each btn In Me.Controls
i = i + 1
ReDim Preserve button(0 To i)
Set button(i) = New cButton
button(i).button = btn
Next btn
End Sub
Last we have the cButton class, that centralizes the button events (through the array). Instead of handling each event individually, we just tell the publisher that an Event has been raised.:
Private WithEvents btn As MSForms.CommandButton
Private pPub As Publisher
Public Event btnClicked()
Private Sub btn_Click()
pPub.RegisterButtonClickEvent Me 'Pass the events to the publisher.
End Sub
Public Property Let button(value As MSForms.CommandButton)
Set btn = value
End Property
Public Property Get button() As MSForms.CommandButton
Set button = btn
End Property
Private Sub Class_Initialize()
Set pPub = GetPublisher
End Sub
With this approach we have one "Publisher" that can handle any event from specific classes that register the right event with it. You could also add image events, workbook events, etc.
The publisher itself raises the events we need based on what gets passed to it.
This way the UserForm can be agnostic of the button class and vice versa.
Based on what is supported in VBA, I'm quite confident this is the cleanest approach for your scenario. If anyone has a better idea, I'd love to see another answer.
I did the following, If you pass the control as a control, you can use the parent.
In my form
Public c As Collection
Private Sub UserForm_Initialize()
Dim ctl As Control
Dim cls As clsCustomImage
Set c = New Collection
For Each ctl In Me.Controls
If TypeName(ctl) = "Image" Then
Set cls = New clsCustomImage
cls.init ctl
c.Add cls, CStr(c.Count)
End If
Next ctl
End Sub
and in my class, clsCustomImage
Private WithEvents i As MSForms.Image
Private frm As UserForm
public event evtRepaint
Public Sub init(c As control)
Set frm = c.parent
Set i = c
End Sub
Private Sub Class_Initialize()
End Sub
Private Sub Class_Terminate()
Set frm = Nothing
Set i = Nothing
End Sub
'
Private Sub i_MouseDown(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
i.Picture = Nothing
frm.Repaint
raiseevent evtRepaint
End Sub
EDIT
To have a single handler, you'd need to look at something along these lines, in a class called say clsHoldAndHandle
Private c As Collection
Private f As UserForm
Private WithEvents cls As clsCustomImage
Public Sub AddControl(ctl As Control)
Set cls = new clsCustomImage
If f Is Nothing Then Set f = ctl.Parent
cls.init ctl
c.Add cls, CStr(c.Count)
End Sub
Private Sub Class_Initialize()
Set c = New Collection
End Sub
Private Sub cls_evtRepaint()
f.Repaint
End Sub
I'm trying to get a value from this userform to determine the next step to selecting a material.
Private Sub UserForm_Initialize()
Dim Material(1, 0)
Dim MaterialC As Variant
Material(0, 0) = "Carbon Steel"
Material(1, 0) = "Stainless Steel"
ComboBox1.List = Material
ComboBox1.Value = Material
Worksheets("Sheet2").Range("A1").Value = MaterialC
End Sub
Also would really appreciate it if someone could briefly clarify the difference in a sub and a private sub?
I want to use the choice of the 2 materials to present the user with a set of sizes unique to either choice, so I need to know what they chose.
Also, is there a more efficient way of pasting code than adding 4 spaces before each line of code?
The difference is the Private keyword. Private is defining the scope of your subroutine, which is that you can only use it inside your Module. Public means that you can call it from outside the Module as well. Since Public is the default scope modifier, the difference between a Sub and a Private Sub is that the Sub will be Publicly usable, while a Private Sub will be kept Private.
For more information read this.
In a VBA project of mine I am/will be using a series of reasonably complex userforms, many of which are visually identical but have different subroutines attached to the buttons. As a result I'm not overly keen on the idea of duplicating them multiple times in order to get different functionality out of the same layout. Is it possible to have a userform detect which subroutine called it and use this in flow control? I would like to be able to do something like this:
Private Sub UserForm_Initialize()
If [the sub that called the userform is called "foo"] then
Call fooSub
else
Call barSub
End If
End Sub
My backup plan is to have the calling subroutine set a global variable flag and have the userform check that, but that seems like a rather crude and clumsy solution.
Thanks everyone,
Louis
You can use the tag property of the form. Load the form, set the property, then show the form:
Sub PassCallerToForm()
Load UserForm1
UserForm1.Tag = "foo"
UserForm1.Show
End Sub
Now that the property is set, you can determine what to do in the form:
Private Sub UserForm_Activate()
If Me.Tag = "foo" Then
Call fooSub
Else
Call barSub
End If
End Sub
You can also use public variables:
' in userform
Public Caller As String
Private Sub UserForm_Click()
MsgBox Caller
Caller = Now()
Me.Hide
End Sub
' in caller
Sub callUF()
Dim frm As New UserForm1
frm.Caller = "Test Caller"
frm.Show
MsgBox frm.Caller ' valid after Me.Hide
Set frm = Nothing
End Sub
Personally, I would not have one userform doing two disparate activities. The code would get hard to read pretty quickly, I think. Copying the layout of a userform is pretty trivial.
To copy a userform: Open a blank workbook. In the Project Explorer, drag the userform to the new workbook. Rename the userform in the new workbook. Now drag it back to the original workbook. Change the code in the userform copy.
If you absolutely don't want separate userforms, I recommend setting up a property of the userform. Userforms are just classes except they have a user interface component. In the userform module
Private mbIsFoo As Boolean
Public Property Let IsFoo(ByVal bIsFoo As Boolean): mbIsFoo = bIsFoo: End Property
Public Property Get IsFoo() As Boolean: IsFoo = mbIsFoo: End Property
Public Sub Initialize()
If Me.IsFoo Then
FooSub
Else
BarSub
End If
End Sub
I always write my own Initialize procedure. In a standard module:
Sub OpenForm()
Dim ufFooBar As UFooBar
Set ufFooBar = New UFooBar
ufFooBar.IsFoo = True
ufFooBar.Initialize
ufFooBar.Show
End Sub