|
|
|
 |
Making a UserForm show above a newly created
Word 2000+ document
|
Article contributed by Jonathan West
One of the nuisances of the SDI feature of Word 2000 is that it makes VBA
rather complex when you are dealing both with UserForms and multiple documents
at the same time. If you have a UserForm showing, and then you open or create
a new document, it appears on top of the UserForm, often covering it
completely. This is not usually what you want!
Word 2002 allows you to toggle SDI off and on, so the
following is only necessary in Word 2000.
Unfortunately, the fix to this involves some Windows API programming.
Experimenting with the Windows API isn't for the faint-hearted, but if you
just adapt the code in this article instead of experimenting with your own,
you should be fairly safe. The trick to this is that in Windows, every window
has a handle, an index number that uniquely identifies that window.
Also, every window has a parent, which is the handle of the window it
is supposed to be associated with. Every window has a parent, which is either
another window or the desktop.
Unlike people, windows can choose their parents, and this is the trick that
is used here. If we can set the UserForm so that its parent window is the new
document, then the UserForm pops up above it.
To see how this works, do the following
1. |
Create a UserForm (for the purposes of the example, call it
UserForm1.
Place a CommandButton on it (called CommandButton1) |
2. |
Place the following code into the UserForm Option
Explicit
Private Declare Function GetActiveWindow Lib
"user32" () As Long
Private Declare Function SetParent Lib
"user32" _
(ByVal hWndChild As
Long, ByVal hWndNewParent As
Long) As Long
Private Sub CommandButton1_Click()
Dim iHandleForm As
Long
Dim iHandleDoc As
Long
Dim iNull As
Long
Dim oDoc As
Document
iHandleForm = GetActiveWindow()
Set oDoc = Documents.Add
iHandleDoc = GetActiveWindow()
iNull = SetParent(iHandleForm, iHandleDoc)
oDoc.Activate
End Sub |
3. |
Call the UserForm by means of the following routine, placed
in a module Sub TestFormFocus()
UserForm1.Show vbModeless
End Sub |
With your first document open, if you run the macro, what should happen is
the following:
1. |
The UserForm will appear. Click on the CommandButton. |
2. |
A new document will be created, and the form will appear
above the new document. You will be able to type into the new document with
the form visible. |
3. |
You can Alt-Tab back to the first document, and the form
will be hidden under it. |
4. |
Alt-tab back to the second document, and click the
CommandButton again. A further document will get created. |
The key to this are the Windows API calls GetActiveWindow and SetParent. It
is absolutely vital that these calls are made when the application is
in the correct state. The GetActiveWindow call gets the handle of the window
that currently has the focus. Call it at the wrong moment, and you pick the
wrong window!
The GetActiveWindow call that places a value into iHandleForm must be
called when the UserForm is the active window. For code used in a larger
application, I would recommend that you make iHandleWindow a module-level
variable, and do the GetActiveWindow call as the first thing in the Activate
event of the form. It only needs to be called once, the value of the handle
will not change until the form is unloaded.
The second GetActiveWindow call must be made when the new document is
active. Again, once you have made the call you can store the value of the
handle, it will not change while that window is open.
The exact timing of the SetParent call is less vital, provided you are
quite certain you have valid values for iHandleForm and iHandleDoc.
Things would be a lot easier if the hWnd property of Word's various windows
were made available to VBA, the way they are in VB6. <sigh>
Don't allow this code to run under Word 97, it will freeze the machine.
|