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.