Question

I am using a Word template with a user form. The user opens the template to start a new document, and enters data in the form. The data is then inserted at bookmarks in the document. I'm using the following sub to insert the data at bookmarks. The variables passed to the sub are the name of the bookmark to insert text at, and the text to insert.

Public Sub UpdateBookmark(BookmarkToUpdate As String, ByVal TextToUse As String)
    Dim BMRange As Range
    Set BMRange = ActiveDocument.Bookmarks(BookmarkToUpdate).Range
    BMRange.Text = TextToUse
    ActiveDocument.Bookmarks.Add BookmarkToUpdate, BMRange
End Sub

This works fine except that in some cases users are tabbing to other word documents before completing the form. When they do this, the focus is left on the other document they used and so my reference to ActiveDocument means the script looks for the bookmarks in the other document instead of the new template document.

Is there an alternative to using ActiveDocument to look for the bookmarks in the new document which was created from the template? Or alternatively is there a way to tell my script to change focus (activate) the new document from which it is being executed?

Many thanks for any suggestions - I've spent a long time googling and experimenting but can't quite find a solution that works for this specific problem (or maybe I just don't understand enough about the Word object model to figure it out on my own).

Was it helpful?

Solution

There are a few ways you could handle it, the simplest of which would be to scope a global/public variable and assign to it early in the process (i.e., immediately before the UserForm is opened, or during the _Initialize or _Activate event procedures).

Set myDocument = ActiveDocument
                 'Or alternatively, 
                 ' = Documents.Add(...) etc.

NOTE that a public variable Public myDocument as Document must be in a normal code module and not a class or userform module.

Then in your form procedures, simly replace ActiveDocument with the variable:

Public Sub UpdateBookmark(BookmarkToUpdate As String, ByVal TextToUse As String)
    Dim BMRange As Range
    Set BMRange = myDocument.Bookmarks(BookmarkToUpdate).Range
    BMRange.Text = TextToUse
    myDocument.Bookmarks.Add BookmarkToUpdate, BMRange
End Sub

Alternatively you can scope the variably more narrowly but then you would need to pass it to the procedures where it is needed, like:

Call UpdateBookmark(myDocument, BookmarkToUpdate, TextToUse)

And modify the procedure:

Public Sub UpdateBookmark(myDocument as Document, BookmarkToUpdate As String, ByVal TextToUse As String)
    Dim BMRange As Range
    Set BMRange = myDocument.Bookmarks(BookmarkToUpdate).Range
    BMRange.Text = TextToUse
    myDocument.Bookmarks.Add BookmarkToUpdate, BMRange
End Sub

Further reading:

Although this is using examples from Excel, the same general principles apply to avoid reliance on Selecting and Activating objects, when you can more efficiently refer to them directly.

How to avoid using Select in Excel VBA macros

(Word is not my area of expertise and from my limited experience, it is more difficult to avoid using the Selection object in Word)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top