Search code examples
vbams-worduserform

Checkbox in UserForm on startup not working


I have a userform with 2 dropdowns which I normally not need so I build a checkbox and hide the dropdown menu and the text label. The checkbox is checked by default. But the dropdown and label isn't hidden when I start the UserForm. When I manually uncheck and check the checkbox after UserForm is started it's working. So I dont know why the checkbox seems to work but I need to uncheck/check it manually after every start of the UserForm.

I think I have to do something at the initial start of the userform?

Private Sub SortCheckBox(blnChecked As Boolean)
Private Sub CheckBox1_Click()
ActiveDocument.Bookmarks("KurbeitragKinder").Range.Font.Hidden = 
CheckBox1.Value
If CheckBox1.Value = True Then
Label8.Enabled = False
Label8.Visible = False
Label9.Enabled = False
Label9.Visible = False
ComboBox6.Enabled = False
ComboBox6.Visible = False
ComboBox7.Enabled = False
ComboBox7.Visible = False
Else
Label8.Enabled = True
Label8.Visible = True
Label9.Enabled = True
Label9.Visible = True
ComboBox6.Enabled = True
ComboBox6.Visible = True
ComboBox7.Enabled = True
ComboBox7.Visible = True
End If
End Sub

Solution

  • The state of the controls on a UserForm when it is first displayed will be, by default, whatever the design-time state is; let's call this the "default state".

    You can define what that default state is by configuring each control's individual properties, using the designer's properties toolwindow (F4).

    If the form's default instance is shown, then its state will be preserved between calls:

    UserForm1.Show
    

    ...unless the instance gets reset - which can easily happen if you Unload that form, or if the user clicks the red "X" button to close it: the instance is destroyed, and since forms have a predeclared ID (aka default instance), the object is automatically re-created the next time it's referenced - with whatever the default (design-time) state is. If you handle the QueryClose event and programmatically Hide the form when CloseMode is VbQueryClose.vbFormControlMenu (and set the Cancel parameter to True, to prevent destroying the form instance and its state), then the state will be preserved ...and this can lead to unexpected or inconsistent behavior.

    The solution is to make sure you always display a fresh new instance of the form, instead of the default one:

    With New UserForm1
        .Show
    End With
    

    That way the form's state is guaranteed to always be the default/intended design-time state every time it's displayed, and you can access the form's state between .Show and End With. All you need to do is to handle QueryClose and cancel the form's destruction when the user clicks the "form control menu" aka "the X button".

    Initializing a form will raise the Initialize event; if you're using the form's default instance (i.e. UserForm1.Show), then you can't really control exactly when this happens, but if you show a fresh new instance every time (i.e. With New UserForm1), then you are certain that this event will be raised exactly once, every time you need to show the form.

    The Initialize event is raised as soon as the object is created, and that happens before the first member call is made against it (i.e. when the New UserForm1 returns, the event has already executed). If you need to check a box and then initialize the form accordingly, then you might want to handle the Activate event instead, which will be raised when the form is actually displayed (i.e. when the .Show method is called):

    With New UserForm1 'UserForm_Initialize runs
        .CheckBox1.Value = foo 'form state is accessible here
        .Show 'UserForm_Activate runs
        'UserForm_QueryClose runs
        foo = .CheckBox1.Value 'form state is accessible here
    End With 'UserForm_Terminate runs
    

    Looks like you want to run that CheckBox1_Click handler before the form is shown - problem is, event handlers aren't Public, and you don't want them to be.

    The solution is to pull the logic into a Public Sub, invoke that procedure from the client code, and invoke it from the checkbox':

    Public Sub InitializeFormState()
        Dim isChecked As Boolean
        isChecked = CheckBox1.Value
        ActiveDocument.Bookmarks("KurbeitragKinder") _
                      .Range.Font.Hidden = isChecked
        Label8.Enabled = isChecked
        Label8.Visible = isChecked
        Label9.Enabled = isChecked
        Label9.Visible = isChecked
        ComboBox6.Enabled = isChecked
        ComboBox6.Visible = isChecked
        ComboBox7.Enabled = isChecked
        ComboBox7.Visible = isChecked
    End Sub
    
    Private Sub CheckBox1_Click()
        InitializeFormState
    End Sub
    

    And now your client code can look like this:

    With New UserForm1
        .InitializeFormState
        .Show
        'consume form state here
    End With
    

    Or, you can invoke InitializeFormState from the form's Initialize or Activate handler, as needed:

    Private Sub UserForm_Initialize()
        InitializeFormState
    End Sub
    

    Or

    Private Sub UserForm_Activate()
        InitializeFormState
    End Sub
    

    In which case the procedure should probably be made Private, and there's no need to invoke it from the client code before the .Show method.