Using Word’s Find Object in Field Result Ranges

Filed under Office, VB Feng Shui

image If you’ve ever had to use the Word Object Model’s Find object, you probably know it can be a bit… well… finicky. It has guids that conflict with Excel 95 typelibs which causes problems, it uses a veritable forest of optional parameters, most of which are declared as OBJECT so intellisense doesn’t work well with it, it’s a real pain to use with C# because of C#’s lack of optional parameter handling, etc, etc.

And I have a new one to add to the list.

I was recently working on a kind of super advanced mail merge facility for Word documents. Word’s MailMerge capabilities are certainly good, but they couldn’t cut the mustard for this particular batch of requirements.

However, since the MAILMERGE field (and all the various other fields in Word) have a lot of capability, I wanted to leverage that as much as possible.

Essentially, the templates to be used would embed normal mail merge fields, but Word’s Merge ability would never get used. Instead, I’d have a VB.NET assembly that would navigate through each field in the document, analyze its code, and replace it with the appropriate result. The code looks something like this:

Dim Code = Field.Code.Text
'....parse the code for the field here
'     determine the final field contents, put it in Content

Field.Result.Text = Content
Field.Unlink

Pretty simply stuff, really.

But, the content of the field might have embedded style information. Sort of like a stripped down HTML code system. For instance, <b> might be “Turn bold on”, and </b> would turn bold off.

First thought

Initially, I thought I could just detect the existence of the opening tag, remove all the tags, and just format the entire field consistently. It’s simple and fast. But unfortunately, some field values had tags embedded within the field, meaning that only a portion of the field’s data should be formatted.

Strike 1.

RTF??? In 2010?

Then, I figured I could just swap out the formatting code with the equivalent RTF codes, and use PasteSpecial to paste the RTF formatted data directly in the field.

While this did work, it presented a bad side effect. Since the RTF text coming off the clipboard represented specific and complete formatting information, it overrode all default formatting already applied to the field. So, say the entire document was formatted in Calibri Font. When I pasted the RTF text “{\b My Text Here}”, the end result was a field formatted in bold Times New Roman, not Calibri. So even though I hadn’t specifically set the font for the text, the paste special was assuming a default of Times.

Strike 2

That ol’ Word FIND Object

I finally realized that I was going to HAVE to use the FIND object in Word to locate those formatting marks, and then apply the applicable styles to the found text.

Performing the Find is pretty straightforward, although there’s a few things to watch for.

The first is the aforementioned Excel 95 Guid clash. What it means is that you need to access the FIND object via late binding. So obtain a FIND object from the (Field.Result range) and store it in an OBJECT variable. That will force VB.net to make latebound calls to it.

The next problem is how to actually find the marks. You’ll need to use the Find object’s wildcard support for that. The code looks something like this:

Dim Find as Object = Field.Result.Find
With Find
   .ClearFormatting
   If .Execute(FindText:="\<b\>*\</b\>", MatchCase:= false, MatchWildcards:= True, Forward:= True) then
      'the text was found
   Else
      'The text was not found
   End If
End With

Again, pretty simple stuff.

But there was two problems.

The first was, the above would find the text alright, but I still needed to remove those embedded formatting marks. After a false starts, I realized that the REPLACE functionality was could do this very easily. Just mark the * (i.e. the found text that you want to keep) by enclosing it in “()”, then Replace with “\1” (the first marked substring).

Dim Find as Object = Field.Result.Find
With Find
   .ClearFormatting
   If .Execute(FindText:="\<b\>(*)\</b\>", MatchCase:= false, MatchWildcards:= True, Forward:= True, Replace:=wdReplace.wdReplaceAll, ReplaceWith:="\1") then
      'the text was found
   Else
      'The text was not found
   End If
End With

The second problem was much more insidious.

It worked perfectly on fields embedded in the body of the document, but completely failed to find anything in field embedded in cells in a table. Needless to say, I was scratching my head over that one for a while.

It turns out that Find just doesn’t work right if you attempt to use it in the Result range of a Field  object when that object has been embedded within a table.

The trick, then is to Unlink the field object before you attempt a find inside of it.

Dim Rng = Field.Result
Field.Unlink
Dim Find = Rng.Find
With Find
   '.... continue as before

With the range no longer within the field, the Find works just like it should, regardless of whether the field is in the body of the document or embedded within a table.

So there’s my terrifically arcane Office object model trivia for the day!

Post a Comment

Your email is never published nor shared. Required fields are marked *

*
*