How to Keep a Modeless UserForm Always on Top in Excel VBA?
When working with Excel VBA UserForms, you may want a modeless UserForm to stay visible while the user interacts with Excel or other applications. By default, a modeless form can slip behind other windows, making it harder to access.
In this guide, we will explain how to keep your UserForm always on top using the Windows API.
Understanding Modal vs Modeless UserForms
Before applying the solution, itβs important to understand how modal and modeless UserForms behave inside Excel VBA.
Modal UserForm
- Locks the application until closed.
- Prevents the user from working with Excel until the form is dismissed.
- Code execution pauses until the UserForm is closed.
Modeless UserForm
- Allows multitasking: Excel and other applications remain accessible.
- Code execution continues after the form is displayed.
- Useful for tool palettes, dashboards, and quick-access utilities.
Hereβs a simple comparison:
UserForm Type | User Interaction | Code Execution |
---|---|---|
Modal | Excel is locked until the form closes | Pauses at the .Show line |
Modeless | User can interact with Excel & other apps | Continues immediately after .Show |
To show a form modeless, you use:
UserForm1.Show vbModeless
The Problem with Modeless UserForms
While modeless UserForms provide flexibility, they suffer from one drawback: if the user clicks on another window, the form may get hidden behind Excel or other programs. This reduces usability, especially when the form is meant to act as a floating toolbar or persistent panel.
The Solution: Keep UserForm Always on Top
We can solve this issue by using the Windows API. Specifically, we will use:
- SetWindowPos β To control the formβs z-order (stacking order).
- FindWindow β To get the handle (hwnd) of the UserForm, since Excel UserForms do not expose
.hwnd
directly.
Step 1: Add API Declarations
Insert the following into a standard VBA module:
Public Const HWND_TOPMOST = -1
Public Const SWP_NOSIZE = &H1
Public Const SWP_NOMOVE = &H2
Public Const SWP_NOACTIVATE = &H10
Public Declare PtrSafe Function SetWindowPos Lib "user32" _
(ByVal hwnd As LongPtr, ByVal hWndInsertAfter As LongPtr, _
ByVal X As Long, ByVal Y As Long, ByVal cx As Long, _
ByVal cy As Long, ByVal uFlags As Long) As Long
Public Declare PtrSafe Function FindWindow Lib "user32" Alias "FindWindowA" ( _
ByVal lpClassName As String, _
ByVal lpWindowName As String) As LongPtr
Step 2: Create Helper Functions
Add a function to get the window handle of a UserForm using its caption, and a subroutine to make it topmost:
Public Function GetHwndFromForm(frm As Object) As LongPtr
GetHwndFromForm = FindWindow(vbNullString, frm.Caption)
End Function
Public Sub SetTopMost(hwnd As LongPtr)
SetWindowPos hwnd, HWND_TOPMOST, 0, 0, 0, 0, _
SWP_NOMOVE Or SWP_NOSIZE Or SWP_NOACTIVATE
End Sub
Step 3: Show the UserForm and Apply Always-On-Top
Now, when you show your modeless UserForm, call the SetTopMost
sub immediately:
Private Sub txtView_FPAnnualAgenda_DblClick(ByVal Cancel As MSForms.ReturnBoolean)
With ufViewFPAnnualAgenda
.Show vbModeless
SetTopMost GetHwndFromForm(ufViewFPAnnualAgenda)
.txtText.Value = txtView_FPAnnualAgenda.Value
.txtText.Tag = txtView_FPAnnualAgenda.Value
End With
End Sub
Bonus: Using a Timer to Refresh Always-On-Top
Sometimes, other applications may override z-order. To handle this, you can use a timer inside your UserForm to refresh the always-on-top state.
Private Sub UserForm_Activate()
Application.OnTime Now + TimeValue("00:00:02"), "KeepOnTop"
End Sub
Public Sub KeepOnTop()
SetTopMost GetHwndFromForm(ufViewFPAnnualAgenda)
Application.OnTime Now + TimeValue("00:00:02"), "KeepOnTop"
End Sub
This ensures the UserForm is repositioned on top every 2 seconds.
Considerations Before Using Always-On-Top
- User experience: Always-on-top forms can be useful for tool panels, but overusing them may annoy users.
- Performance: Calling API functions repeatedly has minimal impact but should be used wisely.
- Compatibility: Some full-screen programs (e.g., games) may override your z-order settings.
- Mac limitation: This method works only on Windows because it depends on the Windows API.
Final Thoughts
By combining the SetWindowPos and FindWindow APIs, you can keep a modeless UserForm always visible in Excel VBA. This technique is particularly useful for tool palettes, dashboards, or real-time data monitors where the UserForm must remain in view while the user continues working across Excel and other applications.
Frequently Asked Questions
Can I use this always-on-top UserForm technique with multiple forms?
Yes. You can apply the SetTopMost
procedure to multiple UserForms. Just call it with each formβs handle obtained through GetHwndFromForm
. Each UserForm can be kept on top individually.
Why do I get a compile error when using .hwnd
on my UserForm?
Excel VBA UserForms do not expose the .hwnd
property directly. Thatβs why you see a compile error. To fix this, you must use the Windows API function FindWindow
with the UserFormβs caption to retrieve its handle.
Will this solution work in Excel for Mac?
No. The method depends on the Windows API, which is available only on Windows. Excel for Mac does not support these API calls. Mac users would need an alternative approach using macOS system APIs.
Can a modeless UserForm stay on top of all applications, not just Excel?
Yes. By using the HWND_TOPMOST
flag in the SetWindowPos
API, the UserForm can remain on top of all windows, not just Excel. However, some programs such as full-screen apps or games may override this behavior.
Is there a way to toggle the always-on-top behavior?
Yes. You can create a Boolean flag (e.g., AlwaysOnTop
) and check its value before calling SetTopMost
. This allows you to turn the always-on-top behavior on or off as needed from your VBA code.
Will repeatedly setting a UserForm on top slow down Excel?
The performance impact is usually minimal. Even if you use a timer to refresh the topmost state every few seconds, the API call is lightweight. Still, you should use it only when necessary to avoid unnecessary system calls.

Vaishvi Desai is the founder of Excelsamurai and a passionate Excel enthusiast with years of experience in data analysis and spreadsheet management. With a mission to help others harness the power of Excel, Vaishvi shares her expertise through concise, easy-to-follow tutorials on shortcuts, formulas, Pivot Tables, and VBA.
Very well written post – however I’m having problems getting it to work.
I have the API declaration at the top of a VBA module.
Public Const HWND_TOPMOST = -1
Public Const SWP_NOSIZE = &H1
Public Const SWP_NOMOVE = &H2
Public Const SWP_NOACTIVATE = &H10
Public Declare PtrSafe Function SetWindowPos Lib “user32” _
(ByVal hwnd As LongPtr, ByVal hWndInsertAfter As LongPtr, _
ByVal X As Long, ByVal Y As Long, ByVal cx As Long, _
ByVal cy As Long, ByVal uFlags As Long) As Long
I call this “SetTopMost” sub immediately after showing the userform:
Public Sub SetTopMost(hwnd As LongPtr)
SetWindowPos hwnd, HWND_TOPMOST, 0, 0, 0, 0, _
SWP_NOMOVE Or SWP_NOSIZE Or SWP_NOACTIVATE
End Sub
like this:
Private Sub txtView_FPAnnualAgenda_DblClick(ByVal Cancel As MSForms.ReturnBoolean)
With ufViewFPAnnualAgenda
.Show 0
SetTopMost ufViewFPAnnualAgenda.hwnd
.txtText.Value = txtView_FPAnnualAgenda.Value
.txtText.Tag = txtView_FPAnnualAgenda.Value
End With
End Sub
But I’m getting a compile error: “Method or data member not found” with the .hwnd property highlighted in my function call above.
Hello Del,
The issue happens because Excel UserForms donβt actually expose the .hwnd property, which is why youβre seeing the compile error.
Instead, youβll need to use the Windows API function FindWindow to get the handle of the UserForm based on its caption, and then pass that handle into the SetTopMost routine.
Iβve updated the article to include this fix with full working code so you can copy it directly. That should solve the problem and keep your modeless UserForm always on top.
If you still face any problems, let me know.