Handling dialog closure in a VBA user form
I like how your form is no longer concerned with anything other than being a form.

One More Abstraction Level

I'd go one step further, and encapsulate the controls, so that the presenter class doesn't need to know about the implementation details of the form. For example the ChangeLabelAndCaption method knows that there's a Label control on the form, named lblInfo.

The form could expose a property for it instead:

Public Property Get InformationText() As String
    InformationText = lblInfo.Text
End Property

Public Property Let InformationText(ByVal value As String)
    lblInfo.Text = value
End Property


This adds a little layer of abstraction, so that the presenter no longer needs to care what actual controls are on the form, so if you decide to change the Label control for some Frame instead, nothing breaks - your code is now independent of the form's implementation details:

objSummaryForm.InformationText = strLabelInfo




For Closure's Sake


Private Sub UserForm_QueryClose(CloseMode As Integer, Cancel As Integer)

    End

End Sub



The End keyword is essentially analoguous to this:



Sure, that will get your form closed, and everything the VBA runtime has been doing will get released. But it's a tiny little wee bit excessive: the End keyword literally ends execution of all VBA code, right there and then.

The QueryClose event is fired just before the form closes, and its parameters give you a handle on the process.

It's just unfortunate that their types are Integer.

The CloseMode parameter tells you how the form is closing; its value is one of the VbQueryClose enum values:


vbAppTaskManager
vbAppWindows
vbFormCode
vbFormControlMenu
vbFormMDIForm


The interesting value - the one that means "user clicked the red X button", is vbFormControlMenu. The integer value for it is 0. So you can do this:

If CloseMode = 0 Then


Or better, this:

If CloseMode = vbFormControlMenu Then


And then handle form close yourself.

When the "red X button" is clicked, the form instance is normally destroyed. If the form contains state that the calling code wants to use, if you don't handle QueryClose and the calling code looks like this:

myForm = New MainForm
myForm.Show vbModal
Debug.Print myForm.SomeProperty


Then the last statement will be a runtime error, because myForm is Nothing at that point.



The Golden Rule


  He who creates an object, shall be responsible for its destruction.


If the form can destroy itself, the calling code is left needing awkward If myForm Is Nothing post-conditions to handle the case where it created an object that decided to hara-kiri.

A cleaner approach is to prevent the form from being able to destroy itself.

Private Sub UserForm_QueryClose(CloseMode As Integer, Cancel As Integer)
    If CloseMode = vbFormControlMenu Then
        Cancel = True
        Hide
    End If
End Sub


We set the Cancel parameter to True, thus preventing the destruction of the object, but only when the CloseMode flag indicates that the form was closed from the control menu. If we didn't call the Hide method, the form would simply refuse to close with the "red X button". This is useful for the rare circumstances where you don't want a form to be closable by the user.



The QueryClose handler signature would arguably be much clearer like this:

Private Sub UserForm_QueryClose(ByRef CloseMode As VbQueryClose, ByRef Cancel As Boolean)


Unfortunately that signature mismatches the interface declaration, and wouldn't compile. But handling the QueryClose event works exactly as if that's what the signature was.



Hungarian Notation

Your code suffers from the "bad" form of Hungarian Notation. While Systems Hungarian is traditionally acceptable for form controls (e.g. lblInformation, txtUserInput, btnOk, etc.; the prefix tells us what type of control we're looking at), using it in actual code for everyday variables, makes things messy:


Dim lngLong         As Long
Private WithEvents objSummaryForm As frmMain
Public Sub ChangeLabelAndCaption(strLabelInfo As String, strCaption As String)



Hungarian Notation was never intended to turn out this way (&lt;~ must read!).

The guy that invented Hungarian Notation meant prefixes to describe the kind of variable, not their type. Apps Hungarian actually does make the code easier to read and understand, and makes wrong code, look wrong:

't: measured in twips
'p: measured in pixels

tArea = tHeight * tWidth 'obviously correct
tArea = pHeight * tWidth 'obviously wrong


Assuming the same context (twips vs pixels), compare to:

'int: integer
'lng: long

lngArea = intHeight * intWidth 'correct?
lngArea = lngHeight * intWidth 'correct?


When you prefix an object variable with obj, or a string with str, an integer with int, lng, or any other type-prefix, you add zero useful information to its identifier, and make the names read awkwardly.

Dim oRange As Range


Stop doing that.