How can I change a Modal Userform from Modal to Modeless at run time ? !

Jaafar Tribak

Well-known Member
Joined
Dec 5, 2002
Messages
9,807
Office Version
  1. 2016
Platform
  1. Windows
Hi all,

I am wondering if one can force a Modal UserForm to become Modeless after loading the UserForm.

Load the Userform as Modal => Click on a CommandButton on the Userform => The Userform now becomes Modeless.

Any ideas ?

Regards.
 
It is true, but you can initially load userform as modeless but in activation event code set it to the modal. In this case you can use ShowWindow API to switch modal / modeless without keyboard blocking and other limitations.

I now understand . The trick lies in setting the form to modal in its activate event.

Thank you vladimir for solving the problem and for teaching me something new.

I don't know if it's really relevant to this but there is no hWnd property of the application in earlier versions of Excel.

Norie,
I believe the hwnd property wasn't introduced till XL2000 but what could easily use the FindWindow API to retrieve the excel app hwnd.

Regards.
 
Upvote 0

Excel Facts

What does custom number format of ;;; mean?
Three semi-colons will hide the value in the cell. Although most people use white font instead.
Along the process, I found something that can be useful for excel 97 users where Modeless userforms were not introduced yet.

here is a simple custom Method .ShowModeless that loads a userform Modeless without relying on the vbModeLess argument hence the possibility to have a modeless userform in excel 97.


Code in the UserForm module :

Code:
Option Explicit
 
Private Declare Function ShowWindow Lib "user32.dll" ( _
     ByVal hwnd As Long, _
     ByVal nCmdShow As Long) As Long
 
Private Declare Function FindWindow Lib "user32.dll" _
    Alias "FindWindowA" ( _
    ByVal lpClassName As String, _
    ByVal lpWindowName As String) As Long
 
Public Sub ShowModeless()
 
    With Me
        .Left = (Application.Width - .Width) / 2
        .Top = (Application.Height - .Height) / 2
    End With
    ShowWindow FindWindow(vbNullString, Me.Caption), 1
 
End Sub

You simply call the UserForm like so in a standard module:

Code:
Sub CallUserForm()
 
    [COLOR=seagreen]'displays a modeless userform.
    'useful for XL97.
[/COLOR]    
    UserForm1.ShowModeless

End Sub

Notice no need here to use the EnableWindow API.

Regards.
 
Upvote 0
Jaafar,

It was new for me too :)
And it was rather improvisation with known APIs to unknown decision than ready to use experience. Therefore, thanks for initiation and direction of this theme.

Regards,
Vladimir
 
Upvote 0
For safe exiting of user form this additional code is required in UserForm1:
Rich (BB code):

Private Sub UserForm_Terminate()
  ' Safe termination as Modeless
  EnableWindow Application.hWnd, Modeless
  With Me
    .Hide
    .Show 0
  End With
End Sub

The downloadable example is updated now: ZVI_Modal_Modeless_01.xls

Application.Hwnd is available at least in Excel 2002, 2003, 2007.
It can be replaced by GetParent (FindWindow(vbNullString, Me.Caption))
with API functions:
Rich (BB code):
Private Declare Function FindWindow Lib "user32.dll" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Private Declare Function GetParent Lib "user32.dll" (ByVal hWnd As Long) As Long


ZVI - The Userform_Terminate code you provided for a safe exit seems to work properly when there is only 1 Excel Workbook open. However, when there are two or more, the additional workbooks have a frozen (unclickable) screen. The only way I can make the other workbooks clickable is by going in the Immediate Window and executing "unload Userform1".

Do you have any idea of how to resolve this issue (I've been pulling my hair out for the past hour trying to figure it out!)
 
Upvote 0
... when there are two or more, the additional workbooks have a frozen (unclickable) screen. The only way I can make the other workbooks clickable is by going in the Immediate Window and executing "unload Userform1".
Hi Chris,

Here is updated code, now it's compatible with 64 bit versions of Excel as well.
Code of UserForm_Terminate() is a bit simplified.
Rich (BB code):
' ZVI:2009-08-09 http://www.mrexcel.com/forum/excel-questions/408356-how-can-i-change-modal-userform-modal-modeless-run-time.html
' 2018-09-13 64 bit compatible
' Switching form to Modal / Modeless at runtime
' Code in userform module
#If  VBA7 Then
  Private Declare PtrSafe Function EnableWindow Lib "user32" (ByVal hWnd As LongPtr, ByVal fEnable As Long) As Long
#Else 
  Private Declare Function EnableWindow Lib "user32.dll" (ByVal hWnd As Long, ByVal fEnable As Long) As Long
#End If
 
Const Modal = 0, Modeless = 1
 
Private Sub CommandButton1_Click()
  ' Modeless
  EnableWindow Application.hWnd, Modeless
End Sub
 
Private Sub CommandButton2_Click()
  ' Modal
  EnableWindow Application.hWnd, Modal
End Sub
 
Private Sub CommandButton3_Click()
  Unload Me
End Sub
 
Private Sub UserForm_Activate()
  ' Activate as Modal
  EnableWindow Application.hWnd, Modal
End Sub
 
Private Sub UserForm_Terminate()
  ' Safe termination as Modeless
  EnableWindow Application.hWnd, Modeless
  Me.Show 0
End Sub

Code in Module1:
Rich (BB code):
' Code of Module1 - Load UserForm1
Sub Start()
  UserForm1.Show 0  'vbModeless = 0
End Sub

Can't reproduse that problem in Excel 2010 32bit and Excel 2016 64bit on Windows10 64bit.
May be your code turns Application.ScreenUpdating = False.
If so then add this line to the UserForm_Terminate() subroutine: Application.ScreenUpdating = True

Put all the above code to the new workbook without any other code, and then apply testing.
If problem will persist then let me know version of OS and version of Excel including its bitness.
 
Last edited:
Upvote 0
Thank you for your quick reply ZVI!

I was able to confirm that the code you posted is not causing the issue directly. I have some code in place to optimize speed that seems to be causing the issue in combination with EnableWindow. Below is an example macro that if you run while the userform is open (I just call it prior to your Unload Me line in CommandButton3), you will get the result of the first workbook being clickable and after navigating to the second workbook being unclickable. Do you have any idea why Calculation and DisplayStatusBar would be causing this? In my source code I also play around with EnableEvents and DisplayPageBreaks but those two didn't seem to cause any issues.

Code:
Public PageBreakState As Boolean
Public CalcState As Long

Sub TestMacro()

'Optimize Code and Store current settings
  CalcState = Application.Calculation
  Application.Calculation = xlCalculationManual
  
  StatusBarState = Application.DisplayStatusBar
  Application.DisplayStatusBar = False

'Do something
  x = 1 + 1
  
'Return settings to original state
  Application.DisplayStatusBar = StatusBarState
  Application.Calculation = CalcState
  
End Sub
 
Upvote 0
Here is revised & stable version of the code.
Rich (BB code):
' ZVI:2009-08-09 http://www.mrexcel.com/forum/excel-questions/408356-how-can-i-change-modal-userform-modal-modeless-run-time.html
' ZVI:2018-09-14 32/64 bit compatible, stable.
' Switching form to Modal / Modeless at runtime
' Code of userform module
#If VBA7 Then
  Private Declare PtrSafe Function EnableWindow Lib "user32" (ByVal hWnd As LongPtr, ByVal fEnable As Long) As Long
#Else
  Private Declare Function EnableWindow Lib "user32.dll" (ByVal hWnd As Long, ByVal fEnable As Long) As Long
#End If
 
Const Modal = 0, Modeless = 1
Dim hWnd As Variant
 
Private Sub CommandButton1_Click()
  ' Modeless
  EnableWindow hWnd, Modeless
End Sub
 
Private Sub CommandButton2_Click()
  ' Modal
  EnableWindow hWnd, Modal
End Sub
 
Private Sub CommandButton3_Click()
  Unload Me
End Sub
 
Private Sub UserForm_Activate()
  ' Activate as Modal
#If VBA7 Then
  hWnd = Application.HinstancePtr
#Else
  hWnd = Application.Hinstance
#End If
  EnableWindow hWnd, Modal
End Sub
Code of Sub UserForm_Terminate is not required.
Code of Module1:
Rich (BB code):
' Code of Module1 - Load UserForm1
Sub Start()
  UserForm1.Show 0  'vbModeless = 0
End Sub
Tested in Excel 32 bit versions 2003, 2007, 2010 and in Excel 2016 64bit.
 
Last edited:
Upvote 0
You're amazing! That fixed the issue!! Just curious, from a high level what was the issue?

Also, another curiosity...it seems the userform will only stay modeless (float on the screen) in a single excel file. If I toggle to another Excel file, the userform stays within the file it was originally launched in. Is there a way simple way to have it stay floating above the whole Excel application (appearing no matter what file you are currently in) or would that require a significant amount of coding?
 
Upvote 0
To understand difference between MDI and SDI (Excel 2013+) read this page – Programming for the Single Document Interface in Excel
Jan Karel Pieterse has developed a great code for switching SDI form between workbooks - Keeping Userforms On Top Of SDI Windows In Excel 2013 And Up

The below is more correct code tested for both modal and modeless modes in Excel 2003/2007/2010 32 bit, Excel 2016 64bit.
It provides features of MDI form navigations to the SDI one, but only in modeless mode.
Navigation of form between workbooks in modal mode is possible by choosing workbook's name in ComboBox1.
Rich (BB code):
' Switching form to Modal / Modeless at runtime
' ZVI:2009-08-09 http://www.mrexcel.com/forum/excel-questions/408356-how-can-i-change-modal-userform-modal-modeless-run-time.html
' ZVI:2018-09-15 added 64 bit compatibility, fixed code.
' A bit modified code of Jan Karel Pieterse is used for switching form between workbooks in Excel 2013+
'   see more details in his web page https://www.jkp-ads.com/Articles/keepuserformontop02.asp
' Code of UserForm with controls:
'   ComboBox1 - to show modal form in another workbook
'   CommandButton1 - set form to modal mode
'   CommandButton2 - set form to modeless mode
'   CommandButton3 - close form
 
Option Explicit
 
#If VBA7 Then
  Private Declare PtrSafe Function EnableWindow Lib "user32" (ByVal hWnd As LongPtr, ByVal fEnable As Long) As Long
  Private Declare PtrSafe Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As LongPtr
  Private Declare PtrSafe Function SetForegroundWindow Lib "user32" (ByVal hWnd As LongPtr) As Long
  Private Declare PtrSafe Function SetWindowLong Lib "user32" Alias "SetWindowLongPtrA" (ByVal hWnd As LongPtr, ByVal nIndex As Long, ByVal dwNewLong As LongPtr) As LongPtr
#Else
  Private Declare Function EnableWindow Lib "user32.dll" (ByVal hWnd As Long, ByVal fEnable As Long) As Long
  Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
#End If
 
Dim WithEvents App As Application
Dim FrmHwnd As Variant
 
Private Sub App_WindowActivate(ByVal Wb As Workbook, ByVal Wn As Window)
  ' Repopulate ComboBox1
  ComboBox1_DropButtonClick
  ' Attach form to Wn
  AttachFormToWindow Wn
End Sub
 
Private Sub App_WorkbookBeforeClose(ByVal Wb As Workbook, Cancel As Boolean)
#If VBA7 Then
  ' Prevent form closing at closing of active workbook
  SetWindowLong FrmHwnd, -8&, 0&
#End If
End Sub
 
Private Sub UserForm_Initialize()
  ' For catching a window activating
  Set App = Application
  ' Save wibdow's handle of the UserForm
  FrmHwnd = FindWindow("ThunderDFrame", Me.Caption)
  ' Populate ComboBox1 by the names of visible workbooks for navigation in modal mode
  ComboBox1_DropButtonClick
  'Caption
  Me.Caption = Me.Caption & " [Modeless]"
  ' Uncomment the below line to initialize modal mode
  'SetModal True
End Sub
 
Private Sub ComboBox1_Change()
  ' Activate the choosen workbook (required for modal mode)
  On Error Resume Next
  Workbooks(Me.ComboBox1.Value).Activate
End Sub
 
Private Sub ComboBox1_DropButtonClick()
  ' Populate ComboBox1 by names of visible workbooks
  Dim a(), i As Long, j As Long, Wb As Workbook
  i = Workbooks.Count
  ReDim a(1 To i)
  For Each Wb In Application.Workbooks
    If Wb.Windows(1).Visible Then
      j = j + 1
      a(j) = Wb.Name
    End If
  Next
  If j = 0 Then
    Workbooks.Add
    j = 1
    a(j) = ActiveWorkbook.Name
  End If
  If j < i Then ReDim Preserve a(1 To j)
  Me.ComboBox1.List = a()
  Me.ComboBox1.Value = ActiveWorkbook.Name
End Sub
 
Private Sub CommandButton1_Click()
  SetModal False
End Sub
 
Private Sub CommandButton2_Click()
  SetModal True
End Sub
 
Private Sub CommandButton3_Click()
  Unload Me
End Sub
 
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
  ' Safe closing
  SetModal False
End Sub
 
Private Sub AttachFormToWindow(Wn As Window)
#If VBA7 Then
  If Val(Application.Version) > 14 Then
    SetWindowLong FrmHwnd, -8&, Wn.hWnd
    SetForegroundWindow FrmHwnd
  End If
#End If
End Sub
 
Private Sub SetModal(Optional Flag As Boolean)
  Dim AppHwnd As Variant, i As Long, Mode As Long, Wn As Window
  If Flag Then Mode = 0 Else Mode = 1
  ' Change mode
  If Val(Application.Version) < 15 Then
    ' MDI - set current window which is global
    EnableWindow Application.hWnd, Mode
  Else
    ' SDI - set each window because of its unique Hwnd
    For Each Wn In Application.Windows
      EnableWindow Wn.hWnd, Mode
    Next
  End If
  ' Show mode in UserForm's caption
  With Me
    i = InStr(.Caption, " [Mod")
    If i = 0 Then i = Len(Caption) + 1
    .Caption = Left(.Caption, i - 1) & " [" & IIf(Flag, "Modal]", "Modeless]")
  End With
End Sub
 
Last edited:
Upvote 0
Code of Module1:
Rich (BB code):
Option Explicit
' Code of Module1 - Load UserForm1
Sub Start()
  UserForm1.Show vbModeless
End Sub
 
Upvote 0

Forum statistics

Threads
1,224,827
Messages
6,181,197
Members
453,021
Latest member
pingpong7117

We've detected that you are using an adblocker.

We have a great community of people providing Excel help here, but the hosting costs are enormous. You can help keep this site running by allowing ads on MrExcel.com.
Allow Ads at MrExcel

Which adblocker are you using?

Disable AdBlock

Follow these easy steps to disable AdBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the icon in the browser’s toolbar.
2)Click on the "Pause on this site" option.
Go back

Disable AdBlock Plus

Follow these easy steps to disable AdBlock Plus

1)Click on the icon in the browser’s toolbar.
2)Click on the toggle to disable it for "mrexcel.com".
Go back

Disable uBlock Origin

Follow these easy steps to disable uBlock Origin

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back

Disable uBlock

Follow these easy steps to disable uBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back
Back
Top