While working on a project recently, I thought about hiding the [X] close button which is at the top of the VBA UserForm. I managed to identify three options for achieving this. If you are thinking about the same thing, then this post will give you the answers you’re looking for.
Download the example file: Join the free Insiders Program and gain access to the example file used for this post.
File name: 0173 Hide or Disable UserFrom Close Button.zip
To hide, or not to hide, that is the question
Before we start looking at the three options, I want to cover the most critical question of all: should we hide a UserForm’s close button?
The close button is a recognized part of the Windows environment. Pretty much every application uses a cross at the top right corner to close a window. Therefore, based on their experience of other applications, all users already know what the cross should do. It doesn’t matter which country the user is in or what language they speak, it is a universally understood icon to close a window. Therefore you should be asking yourself why you want to hide the close button at all. With this in mind, I would say that generally the close button should not be hidden or disabled.
However, there are some circumstances where we may want to break this general rule. One such example is a progress bar. If the user closes a progress bar, they may not know when the macro has finished running, or how long is left. Therefore, it might be useful to hide the close button in this scenario.
By default, the close button will unload a UserForm, but it can be adapted to have a custom close procedure. As we can control this functionality, it may be better to control it rather than hide or disable it.
About Windows API codes
Of the three solutions, two of them rely on Windows API calls. These are where the VBA code makes use of functions which are not part of Excel, but part of the main Windows application. As this is complicated stuff, I will not go through it in detail here. But the key points to note about the Windows API codes are:
- They will not work on a Mac, only on Windows. So if designing for Windows and Mac, you will need to identify which platform is used and adapt the code accordingly.
- The code sections marked “Include this code at the top of the module” must be included before any other Subs or Functions are declared, and must be below the Option Explicit statement (if you have one).
3 Options for disabling/hiding a UserForm close button
If you are still intent on hiding or disabling the close button, then read on to understand the three options.
(1) The unobvious disable with a message box
This first option is the worst of the three. It does not disable or hide the close button, but prevents the user from using it to close the window.
Even though it is the worst option, it is the easiest to use and understand. The following code can be used within the UserForm’s QueryClose event.
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer) If CloseMode = vbFormControlMenu Then Cancel = True MsgBox "Please use the Close button to close the form", vbOKOnly End If End Sub
If the user clicks on the close button, the following message box will appear.
This is effectively disabling the close button, as the UserForm will not close. But from a user’s perspective, this solution is just frustrating. Why would there be a button which doesn’t do anything apart from telling us it doesn’t do anything? This is why it’s not a great option.
(2) Disable the close button (Windows API)
The second option will disable the close button; it will be visible, but will be greyed-out and cannot be clicked.
The first image below shows the close button when it is enabled; the second image shows the close button when it is disabled.
From a user’s perspective, this option is better than the first, but still not great. What is the point of a button which cannot be clicked; it just creates confusion?
Basic code
The code below should be copied into a new standard module and must be included before any other code, but after the Option Explicit statement (if there is one).
'Include this code at the top of the module Private Const GWL_STYLE = -16 Private Const WS_CAPTION = &HC00000 Private Const WS_SYSMENU = &H80000 Private Const SC_CLOSE = &HF060 #If VBA7 Then Private Declare PtrSafe Function FindWindowA _ Lib "user32" (ByVal lpClassName As String, _ ByVal lpWindowName As String) As Long Private Declare PtrSafe Function DeleteMenu _ Lib "user32" (ByVal hMenu As Long, ByVal nPosition As Long, _ ByVal wFlags As Long) As Long Private Declare PtrSafe Function GetSystemMenu _ Lib "user32" (ByVal hWnd As Long, ByVal bRevert As Long) As Long #Else Private Declare Function FindWindowA _ Lib "user32" (ByVal lpClassName As String, _ ByVal lpWindowName As String) As Long Private Declare Function DeleteMenu _ Lib "user32" (ByVal hMenu As Long, _ ByVal nPosition As Long, ByVal wFlags As Long) As Long Public Declare Function GetSystemMenu _ Lib "user32" (ByVal hWnd As Long, ByVal bRevert As Long) As Long #End If
'Include this code in the same module as the API calls above Public Sub CloseButtonSettings(frm As Object, show As Boolean) Dim windowHandle As Long Dim menuHandle As Long windowHandle = FindWindowA(vbNullString, frm.Caption) If show = True Then menuHandle = GetSystemMenu(windowHandle, 1) Else menuHandle = GetSystemMenu(windowHandle, 0) DeleteMenu menuHandle, SC_CLOSE, 0& End If End Sub
The code examples above create reusable functionality to enable or disable the close button. The CloseButtonSettings subprocedure can now be called from any other module. There are two arguments required:
- frm – a reference to the UserForm for which the close button should be enabled/disabled
- show – a True or False value where True = enable the close button and False = disable the close button
Disable the close button on UserForm initialize
If inserted into the UserForm’s initialize event it will disable the button when the form is created.
Private Sub UserForm_Initialize() Call CloseButtonSettings(Me, False) End Sub
Disable or enable the close button at any point
The close button can be enabled or disabled as and when required with the following macros:
Disable the close button
Sub DisableClose() Call CloseButtonSettings(frmMyUserForm, False) End Sub
Enable the close button
Sub EnableClose() Call CloseButtonSettings(frmMyUserForm, True) End Sub
(3) Hide system menu (Windows API)
The third option is to hide the system menu section of the window. The System menu section includes the maximize, minimize and close buttons. The screenshot below shows the close button is not visible.
By hiding the system menu section, we are also removing the ability to see the minimize and maximize buttons. By default a UserForm does not have these buttons visible (separate Windows API calls are required to enable this), therefore this is probably a good option in 99.9% of circumstances.
Basic Code
The code below should be copied into a new standard module and must be included before any other code, but after the Option Explicit statement (if there is one).
'Include this code at the top of the module Private Const GWL_STYLE = -16 Private Const WS_CAPTION = &HC00000 Private Const WS_SYSMENU = &H80000 #If VBA7 Then Private Declare PtrSafe Function GetWindowLong _ Lib "user32" Alias "GetWindowLongA" (ByVal hWnd As Long, _ ByVal nIndex As Long) As Long Private Declare PtrSafe Function SetWindowLong _ Lib "user32" Alias "SetWindowLongA" (ByVal hWnd As Long, _ ByVal nIndex As Long, ByVal dwNewLong As Long) As Long Private Declare PtrSafe Function FindWindowA _ Lib "user32" (ByVal lpClassName As String, _ ByVal lpWindowName As String) As Long Private Declare PtrSafe Function DrawMenuBar _ Lib "user32" (ByVal hWnd As Long) As Long #Else Private Declare Function GetWindowLong _ Lib "user32" Alias "GetWindowLongA" ( _ ByVal hWnd As Long, ByVal nIndex As Long) As Long Private Declare Function SetWindowLong _ Lib "user32" Alias "SetWindowLongA" ( _ ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long Private Declare Function FindWindowA _ Lib "user32" (ByVal lpClassName As String, _ ByVal lpWindowName As String) As Long Private Declare Function DrawMenuBar _ Lib "user32" (ByVal hWnd As Long) As Long #End If
'Include this code in the same module as the API calls above Public Sub SystemButtonSettings(frm As Object, show As Boolean) Dim windowStyle As Long Dim windowHandle As Long windowHandle = FindWindowA(vbNullString, frm.Caption) windowStyle = GetWindowLong(windowHandle, GWL_STYLE) If show = False Then SetWindowLong windowHandle, GWL_STYLE, (windowStyle And Not WS_SYSMENU) Else SetWindowLong windowHandle, GWL_STYLE, (windowStyle + WS_SYSMENU) End If DrawMenuBar(windowHandle) End Sub
These codes create reusable functionality to show or hide the buttons. The SystemButtonSettings subprocedure can be called from any other module. There are two arguments required
- frm – a reference to the UserForm for which the close button should be shown/hidden
- show – a True or False value where True = show the system menu and False = hide the system menu
Hide the system menu buttons on UserForm initialize
If inserted into the UserForm’s initialize event it will disable the button when the form is created.
Private Sub UserForm_Initialize() Call SystemButtonSettings(Me, False) End Sub
Show or hide the close button at any point
The close button can be shown or hidden as and when required using the following macros.
Hide the close button
Sub HideClose() Call SystemButtonSettings(frmMyUserForm, False) End Sub
Show the close button
Sub ShowClose() Call SystemButtonSettings(frmMyUserForm, True) End Sub
Discover how you can automate your work with our Excel courses and tools.
Excel Academy
The complete program for saving time by automating Excel.
Excel Automation Secrets
Discover the 7-step framework for automating Excel.
Office Scripts: Automate Excel Everywhere
Start using Office Scripts and Power Automate to automate Excel in new ways.
One reader asked me about using the API code Access. It appears the Access UserForm has a hWnd property which contains the Window Handle.
So to make it work, change this:
For this:
Great Content.
Thanks A lot , Saved me lot of time !
Thanks Faisal
Option 1
“Why would there be a button which doesn’t do anything apart from telling us it doesn’t do anything?”
You should be asking yourself “Why did I put a msgbox there?”
MsgBox “Please use the Close button to close the form”, vbOKOnly
Remove this unnecessary msgbox in your code and Option 1 will be your new best friend.
UserForm Code:
Private cancelled As Boolean
Private Sub btnCancel_Click()
cancelled = True
Me.Hide
End Sub
Public Property Get Cancelling() As Boolean
Cancelling = cancelled
End Property
Private Sub UserForm_QueryClose(cancel As Integer, CloseMode As Integer)
If CloseMode 1 Then
cancel = True
cancelled = True
Me.Hide
End If
End Sub
Module Code:
With userForm1
.Show
If .Cancelling Then
Exit Sub or Exit Function or just be glad you didn’t have to click an extra msgbox button
End If
End With
The users won’t even know the difference between clicking the ‘X’ button and the ‘Close’ button, or in this code’s case the ‘Cancel’ button
Hi John
In that specific scenario, where there is a close button on the UserForm I agree that is a suitable solution. In fact it’s often the best solution as we get to control the Hide or Unload procedure – https://exceloffthegrid.com/closing-vba-userforms-hide-vs-unload/
But what if we don’t want the user to Close the UserForm at all. Maybe it should close automatically after a period of time, or if it is displaying the progress of another process. In these scenarios, there is no purpose to the [x] button and no suitable alternative to point the close procedure to.
Thanks it Helped, for excel vba. Calling API, was new thing. But served the purpose.. Thanks once again
Thanks Sriram 🙂
Just discovered that your form must have a caption otherwise the cross will still appear!
Thanks Mark – that’s a useful piece of insight. I appreciate you sharing.
I keep getting the following error:
Compile Error:
invalid outside procedure
OK Help
It highlights VBA7 in the If statement
What do I need to do?
It creates hidden compile error in macro when run in excel 2007.
Yes you must have a caption however you can use blank spaces to work around this. Thank you for the code.
I can’t seem to run this code, and I hope someone can assist.
I am getting this message when using VBA editor. “The code in this project must be updated for use on 64-bit systems. Please review and update declare statements and then mark them with the PtrSafe attribute”
According to Microsoft, this only shows as an issue in VBA editor, and won’t error when the macro is run.
But when I run it, I get an error in the Public Sub CloseButtonSetting procedure with reference to “GetSystemMenu”
In VBA editor, everything after Else is red, and the #Endif is excluded from the General Declarations section of the module, and resides above the “Public Sub CloseButtonSetting …” line.
Any suggestions?
I tried using the code to hide the X button, but VBE rejected the second half of the first code as it wasn’t compactable with 64 bits, so I had to take the other half out. I called the Hideclose() function under the initialize event of the userform but it didn’t work when I ran the code. Is there something I could do to fix it?
This is fantastic, does anyone know how to combine this with WS_ThickFrame which allows resizing? i have managed to combine the two to give me a window with no close button and resizing possible however it gives me an unintended horizontal scroll bar at the bottom weirdly