WizMsg(): A Simple Way to Create a MsgBox with Bold Text
Bold text in a Microsoft Access message box? It's true. And it requires no API calls, no third-party libraries, and no arcane string syntax. Check out WizMsgBox!
It's not possible to add formatting to a standard message box in VBA.
However, using the hidden WizHook object in Access, we gain the ability to easily create message boxes with a leading line of bold text. We can do this without relying on API calls or other advanced trickery.
The message box is very similar to the built-in version, with a few key differences:
- WizMsgBox treats the
@
symbol as a paragraph separator - First paragraph of the message box appears in bold
- Up to three distinct paragraphs may be included
As this is an undocumented internal function that was never meant to see the light of day, it takes an...umm...eccentric approach to parsing the message text. The at sign (@
) is used as a paragraph separator for...reasons. ¯\_ (ツ)_/¯
The Starting Point
I used the following code to test exactly how the @ symbols get treated by WizMsgBox:
Public Sub TestWizMsgBox()
With WizHook
.Key = 51488399
Dim s As String
s = "BoldLine1 @ @": .WizMsgBox s, "_" & s & "_", 0, 0, ""
s = "BoldLine1 @ @ ": .WizMsgBox s, "_" & s & "_", 0, 0, ""
s = "BoldLine1 @@": .WizMsgBox s, "_" & s & "_", 0, 0, ""
s = "BoldLine1@@": .WizMsgBox s, "_" & s & "_", 0, 0, ""
End With
End Sub
If you run this code, you'll see it produces a surprising variety of results.
The Approach
Trying to understand and remember the arbitrary rules for the special @ symbol handling seemed destined to cause frustration and lead to easily-missed bugs.
So, instead of calling WizMsgBox directly in my code, I created a wrapper function that encapsulates the @-handling complexity.
A Note About Parameter Order
One thing you may notice is that–unlike the standard MsgBox–the dialog's Title (A) is the first parameter and (B) is required.
I agonized over this difference, but ultimately decided it made sense since I have separate optional arguments for lines 2 and 3. It didn't feel right to put the title after both of the optional lines, especially since I don't anticipate Line3 will be called very often. And putting the Title between lines 2 and 3 seemed like the worst sort of compromise.
In the end, I'm happy with how it turned out. The arguments appear on the dialog box from top to bottom:
- Title
- Line 1
- Line 2
- Line 3
- Button Styles
Obviously, you can change the order of the arguments as you see fit.
The Optional Values of Line2 and Line3
As the comments explain, these values are not typos.
- Line2's default value is a single space
- Line3's default value is a zero-length string
I arrived at these values through trial and error. See the bottom of the article for a screenshot of how various message boxes appear based on which lines are provided and which are not.
The WizMsg
Function
As with many WizHook methods, we first need to unlock the WizHook object by setting its key to the magic value of 51488399
. Rather than do this directly, I call my Wiz() convenience function, which handles applying the magic value when needed.
' ----------------------------------------------------------------
' Procedure : WizMsg
' Date : 10/28/2022
' Author : Mike Wolfe
' Source : https://nolongerset.com/wizmsg/
' Purpose : Wrapper around the WizHook.WizMsgBox function
' for improved code readability and reliability.
' Notes - The optional values of Line2 and Line3 are not typos:
' - Line2 is a single space
' - Line3 is a zero-length string
' - This provides us with the most intuitive results when
' the calling code chooses not to provide either or both.
' ----------------------------------------------------------------
Public Function WizMsg(Title As String, _
BoldLine1 As String, _
Optional Line2 As String = " ", _
Optional Line3 As String = "", _
Optional MsgBoxStyle As VbMsgBoxStyle = vbOKOnly) As VbMsgBoxResult
With Wiz
WizMsg = .WizMsgBox(BoldLine1 & "@" & _
Line2 & "@" & _
Line3, Title, MsgBoxStyle, 0, "")
End With
End Function
' Wiz() function included to allow easy copy-pasting of code sample
' Source : https://nolongerset.com/wiz-wizhook-helper/
Private Function Wiz() As Access.WizHook
Static WizHookInitialized As Boolean
If Not WizHookInitialized Then
'Most WizHook methods only work after this key has been applied:
Const MagicWizHookKey As Long = 51488399
WizHook.Key = MagicWizHookKey
'The key only needs to be applied once per session
WizHookInitialized = True
End If
Set Wiz = WizHook
End Function
Sample Usage
I used the following test routine to see the impact of setting or not setting the three line arguments: BoldLine1
, Line2
, and Line3
.
Public Sub TestWiz()
Dim x As String, y As String, z As String
x = "Line1": y = " ": z = "": WizMsg "x" & x & "x y" & y & "y z" & z & "z", x, y, z
x = "Line1": y = "Line2": z = "": WizMsg "x" & x & "x y" & y & "y z" & z & "z", x, y, z
x = "Line1": y = " ": z = "Line3": WizMsg "x" & x & "x y" & y & "y z" & z & "z", x, y, z
x = "Line1": y = "Line2": z = "Line3": WizMsg "x" & x & "x y" & y & "y z" & z & "z", x, y, z
x = "": y = " ": z = "": WizMsg "x" & x & "x y" & y & "y z" & z & "z", x, y, z
x = "": y = "Line2": z = "": WizMsg "x" & x & "x y" & y & "y z" & z & "z", x, y, z
x = "": y = " ": z = "Line3": WizMsg "x" & x & "x y" & y & "y z" & z & "z", x, y, z
x = "": y = "Line2": z = "Line3": WizMsg "x" & x & "x y" & y & "y z" & z & "z", x, y, z
End Sub
There are eight samples that represent all possible combinations of these three strings being either populated or not (2^3 = 8). I've included the sample output below as a handy reference.