Userforms And The SetParent Function
This page describes how to use the SetParent API function to
control how UserForms are displayed.
Normally, a modeless userform floats above, and
independently of,
the Excel application window and the worksheet windows. This means that when
you move the application window or a sheet window, the form does not move
with the window -- it stays in its original location on the screen,
disconnected from the Excel windows. Broadly
speaking, this is not desirable. It is my experience that the users expect
the userform to move along with the application window or the sheet window.
DEFINITION:
A modeless form, or a form displayed
modelessly, is where the form remains
visible but does not have focus,
and you can edit the worksheet or work with menus and command bars
while the form is visible. The "Find" dialog is an example of a modeless
form. You can modify cell values while the form is visible on the screen.
A modal
form, or a form shown modally, is when the
form has focus and you
can access only items on the form while it is visible. You cannot access
anything that is
not on the form until the form is hidden or closed. The "Options" dialog is
an example of
a modal form. You must close this dialog before doing anything else.
There is no "move" event of any Excel object, so you
cannot detect when the user moves a window. To overcome this situation, you
can
use a series of Windows API system calls to set the form as a "child" window
to
the worksheet window. When you do this, the form will move along with the
application window and the sheet window.
Note:
This code applies only to Excel 2000 (VBA6) and later, and only for forms
that are
shown modelessly. It will not work in Excel97 or earlier, and will not work
with modal forms.
You show a form modelessly with code like the following:
UserForm1.Show vbModeless
You can also show a form modelessly by setting the
userform's ShowModal property to
False.
Download an example workbook illustrating the code
here.
All of the following code should be placed in the userform's code
module.
At the top of the module, outside of and before any procedure, paste the code shown
below.
Private Declare Function
SetParent Lib "user32" ( _
ByVal hWndChild As Long, _
ByVal hWndNewParent As Long) As Long
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" _
(ByVal lpClassName As String, _
ByVal lpWindowName As String) As Long
Private Declare Function FindWindowEx Lib "user32" Alias "FindWindowExA" _
(ByVal hWnd1 As Long, _
ByVal hWnd2 As Long, _
ByVal lpsz1 As String, _
ByVal lpsz2 As String) As Long
Private Declare Function GetAncestor Lib
"user32.dll" ( _
ByVal hwnd As Long, _
ByVal gaFlags As Long) As Long
Note that the function declarations above must be
Private
not Public .
You can't have Public
declarations in an object module, such as a Sheet, Form or Class module.
Then, use the userform's Initialize
event to set the
form as a child to the worksheet window:
Private Sub UserForm_Initialize()
Const C_VBA6_USERFORM_CLASSNAME = "ThunderDFrame"
Dim AppHWnd As Long
Dim DeskHWnd As Long
Dim WindowHWnd As Long
Dim MeHWnd As Long
Dim Res As Long
''
AppHWnd = FindWindow("XLMAIN", Application.Caption)
If AppHWnd > 0 Then
DeskHWnd = FindWindowEx(AppHWnd, 0&, "XLDESK", vbNullString)
If DeskHWnd > 0 Then
WindowHWnd = FindWindowEx(DeskHWnd,
0&, "EXCEL7", ActiveWindow.Caption)
If WindowHWnd > 0 Then
Else
MsgBox
"Unable to get the window handle of the ActiveWindow."
End If
Else
MsgBox "Unable to get the window
handle of the Excel Desktop."
End If
Else
MsgBox "Unable to get the window handle of the Excel
Application."
End If
MeHWnd = FindWindow(C_VBA6_USERFORM_CLASSNAME, Me.Caption)
If (MeHWnd > 0) And (WindowHWnd > 0) Then
Res = SetParent (MeHWnd, WindowHWnd)
If Res = 0 Then
MsgBox "The call to SetParent
failed."
End If
End If
End Sub
The code above makes the form a child of the active
worksheet window. That means that if you switch to another window the form
will disappear and will reappear when you activate the original window. If
you want the form to remain visible in all windows, but still move with the
application window, use the following simplified
UserForm_Initialize procedure to make the form a
child window of the main application window.
Private Sub UserForm_Initialize()
Const C_VBA6_USERFORM_CLASSNAME = "ThunderDFrame"
Const GA_ROOTOWNER As Long = 3&
Dim AppHWnd As Long
Dim UserFormHWnd As Long
Dim Res As Long
UserFormHWnd = FindWindow(C_VBA6_USERFORM_CLASSNAME,
Me.Caption)
If UserFormHWnd > 0 Then
AppHWnd = GetAncestor(UserFormHWnd,
GA_ROOTOWNER)
If AppHWnd > 0 Then
Res =
SetParent(UserFormHWnd, AppHWnd)
If Res = 0
Then
MsgBox "The call to SetParent failed."
End If
End If
End If
End Sub
Note: This code applies only to Excel 2000 and above,
and applies only to forms that are displayed modelessly:
UserForm1.Show vbModeless
Download an example
here.
This page last modified: 27-June-2007.