# underline one word within a table cell

• ### Question

• hi all

I need to be able to underline a single word within a table cell.  I'm filling the cell with text from a table and the users are putting a '^' on either side of the word that needs underlining.  While finding the caret (^) within the text is no problem, I need to be able to put the first part of the text (up to the caret), then the text between the carets and mark JUST those words between the carets for underline, then the rest of the text.

In other words, I've got:

88. The association that judges the worthiness of entries for the Guinness Book of World Records is awaiting Jim Hager's application.  The Oakland, California, resident hoped to qualify for his feat last weekend:  gobbling 115 M&Ms in three minutes in an event sponsored by a local shop.  But, wait, you say:  ^that^ doesn't sound like very many.  Perhaps not...until you learn that the criteria for this category call for carrying the little candy-coated chocolates to the mouth one at a time--with wooden chops ticks.  Oh, the current record:  112.

The underlined word in the paragraph refers to _____.

And what I need is:

88. The association that judges the worthiness of entries for the Guinness Book of World Records is awaiting Jim Hager's application.  The Oakland, California, resident hoped to qualify for his feat last weekend:  gobbling 115 M&Ms in three minutes in an event sponsored by a local shop.  But, wait, you say:  that doesn't sound like very many.  Perhaps not...until you learn that the criteria for this category call for carrying the little candy-coated chocolates to the mouth one at a time--with wooden chops ticks.  Oh, the current record:  112.

The underlined word in the paragraph refers to _____.

Thanks

Wednesday, December 21, 2011 8:23 PM

• Hi Dorris,

Try:

with oDoc.Content.Find
.MatchWildCards = .t.
.ClearFormatting
.Forward = .t.
.Wrap = 0
.Text = "(^094)(*)(^094)"
.Replacement.ClearFormatting
.Replacement.Text = "\2"
.Replacement.font.Underline = .t.
.Execute , , , , , , , , , , 2
endwith

or:

with oDoc.Content.Find
.MatchWildCards = .t.
.ClearFormatting
.Forward = .t.
.Wrap = 0
.Text = "(^094)(*)(^094)"
.Replacement.ClearFormatting
.Replacement.Text = "\2"
.Replacement.font.Underline = .t.
.Execute Replace:=2
endwith

Cheers
Paul Edstein
[MS MVP - Word]
• Marked as answer by Wednesday, December 28, 2011 7:03 PM
Tuesday, December 27, 2011 11:00 PM

### All replies

• Try this. Instead of ActiveDocument.Content, you can use Selection or a specific range.

```Sub UnderlineBetweenCarets()
With ActiveDocument.Content.Find
.ClearFormatting
.Text = "(^94)(*)(^94)"
.Replacement.ClearFormatting
.Replacement.Text = "\2"
.Replacement.Font.Underline = wdUnderlineWords
.Forward = True
.Wrap = wdFindStop
.Format = True
.MatchCase = False
.MatchWholeWord = False
.MatchAllWordForms = False
.MatchSoundsLike = False
.MatchWildcards = True
.Execute Replace:=wdReplaceAll
End With
End Sub
```

Regards, Hans Vogelaar
Wednesday, December 21, 2011 8:58 PM
• Hi Doris,

If you don't like macros, you can use a wildcard Find/Replace, where:
Find = (^94)(*)(^94)
Replace = \2
and the replacement font format is set to underlined.

In essence, that's what Hans' macro does.

Cheers
Paul Edstein
[MS MVP - Word]
Thursday, December 22, 2011 9:49 AM
• That, of course, is what I did first. But since this is the Developer forum, I assumed that the OP wanted to use code.
Regards, Hans Vogelaar
Thursday, December 22, 2011 10:01 AM
• Hi Hans and Paul

Actually, I'm working from Visual Foxpro - so I'm having to translate VB ->VFP, which isn't difficult - but I have to understand what, exactly, I'm doing - so, a couple of questions.

I'm assuming that the (^94)(*)(^94) translates to "Find anything bracketed by '^'", yes?  But I'm not understanding what the .Replacement.text = '/2' is doing.

Everything else makes sense, except the two statements that are, probably, the most important to understand.

Thanks for the assistance

Thursday, December 22, 2011 2:28 PM
• We're doing a wildcard search here.

^94 is the code for ASCII character #94, which is the caret character ^. We can't just specify ^ since that is a special character in the Find what and Replace with boxes.

* is the wildcard character for 'any number of characters'.

The three pairs of parentheses () divide the Find what text into three parts that can be referred to as \1, \2 and \3.

So \1 is the first caret ^, \2 is the *, i.e. the characters in between, and \3 is the second caret ^.

In the Replace with box, we specify \2, i.e. the middle part: all characters between the carets.

Regards, Hans Vogelaar
Thursday, December 22, 2011 2:40 PM
• Hans

Ok, the light begins to dawn.  So, if I wanted to then replace the carets with nothing (""), I would want to issue 2 Replacement code sets?

First to underline the word(s) between the carets

Execute that Find/Replace

Then a second to replace the carets with ""

Thanks

Thursday, December 22, 2011 3:02 PM
• Hi Dorris

No, it requires only one pass.

When you do a wildcard search, the parentheses around search terms divide the entirety into "expressions". This lets you deal with each expression in the Replace part.

so \2 is telling Replace to take what's found in the second expression and use it as the Replacement.Text.

So Find is saying, search for caret-text-caret as expressions 1, 2 and 3.

Replacement is saying, we only care about expression two (the text). Retain expression 2 and underline it.

Cindy Meister, VSTO/Word MVP
Thursday, December 22, 2011 3:38 PM
• Cindy

So you're saying that I can essentially say

with  ...Find

.Replacement.Text = "\2"
.Replacement.font.Underline = .t.
.Replacement.Text = "\1"
.Replacement.Text = ""
.Execute(2)

endwith

?

Thursday, December 22, 2011 3:56 PM
• Hi Dorris

almost. But need only the first two "Replacement" lines, not the second two.

You're automatically discarding the first and third expressions.

If you want to feel better about it, try it out in the Word UI using the Replace dialog box :-)

Cindy Meister, VSTO/Word MVP
Thursday, December 22, 2011 4:07 PM
• D'OH!

Thank you for putting it in incredibly plain English which, apparently, I need today.  Went back and reread your post before this and actually saw the word 'retain'.  Can't really tell you what I saw when I read it the first time, but obviously it wasn't 'retain'

Unfortunately, it doesn't seem to be working.  Currently, the document is divided into 3 sections.

Section 1

Directions for first section of test

Table 1

Directions for 2nd section of test

Section 2

Table 2

Directions for 3rd section of test

Section 3

Table 3

End of Test verbiage

As this point, I'm running the following code:

```with oDoc.Content.Find
.MatchWildCards = .t.
.ClearFormatting
.Forward = .t.
.Wrap = 0
.Text = "(^094)(*)(^094)"
.Replacement.ClearFormatting
.Replacement.Text = "\2"
.Replacement.font.Underline = .t.
.Execute(2)
endwith```

The code runs without errors, but nothing seems to be happening and I can't seem to find if Execute returns anything beyond True or False.  What am I missing?

Thursday, December 22, 2011 4:26 PM
• Hi Dorris

Mmm. What is .Execute(2) supposed to be? Find.Execute has a LOT of parameters; the first is used to set the Text, and you appear to be overwriting the wildcard string with the numerical value 2?

.Execute returns a boolean value, .t. if something is found, otherwise .f.

And I also think that you're not actually telling Find.Execute to do a replacement. You need to take a really close look at all the parameters for the method. You don't need all of them, but in order to do a replacement you certainly need to specfy the Replace parameter. I imagine that's what you want to use the 2 for, but Replace isn't the first parameter, not by a long shot.

Cindy Meister, VSTO/Word MVP
Thursday, December 22, 2011 5:44 PM
• hmmm...color me confused again.

What I see in all examples has the .Execute within the With..EndWith block as

.Execute Replace = wdReplaceAll

wdReplaceAll 'enumerates' out to 2, and that statement doesn't translate directly to Visual Foxpro, hence the .Execute(2), since all the other parameters are set above the execute statement.

So, it should be .Execute(,,,,,,,,,2) since all before Replace are optional, or

.Execute("(^094)(*)(^094)",F,F,T,F,F,T,0,,,2)?

Thursday, December 22, 2011 7:03 PM
• Try

```        .Execute( , , , , , , , , , , 2)
```

All the other parameters have already been set.

Regards, Hans Vogelaar
Thursday, December 22, 2011 8:55 PM
• Hi Dorris

What Hans said :-)

<<What I see in all examples has the .Execute within the With..EndWith block as
.Execute Replace = wdReplaceAll>>

the VB languages support the combination of optional and named parameters. When you can use named parameters, you can supply the parameter name and assign a value to it. In this case, the parameters can be listed in any order you like. Otherwise, not.

BTW, and I know I'm being nit-picky, but you never know who may read this and try to use the information in it... The correct syntax you've seen else where would be:
.Execute Replace:=wdReplaceAll

Cindy Meister, VSTO/Word MVP
• Marked as answer by Friday, December 23, 2011 2:28 PM
• Unmarked as answer by Friday, December 23, 2011 2:28 PM
Friday, December 23, 2011 11:28 AM
• Cindy and Hans,

Ok, I understand what should be happening now, but it's not and the .Execute reports back .f., and yes - there are actually things to find and replace.  Is there anything else that could tell me what's going on?

And Cindy, you're being no more nit-picky than the language would be - and I'm famous for forgetting the ':' unless I start saying to myself "IS equal" while I hit the ':='.

Friday, December 23, 2011 2:32 PM
• Hi Dorris

<<Ok, I understand what should be happening now, but it's not >>

And have you tried this in the UI, as I suggested in an earlier reply? That's usually the best way to track down whether it's a problem in the arguments you're passing, or in your code. I always start from the Find/Replace dialog box in the UI to make sure I've got the right search strings. Especially when using wild cards.

One possibility would be that the caret symbol you're writing into the Word document is not ANSI 94?

Another possibility would be that you have the wrong number of commas in the Execute method for optional parameters.

It looks like there might be a space between the sets of parentheses in the wildcard expression - that would be wrong, if there are...

Cindy Meister, VSTO/Word MVP
Friday, December 23, 2011 4:34 PM
• Yes, I have tried it in the UI and it finds something for both '^' and '^94', but not anything for "(^94)(*)(^94)" or ^94*^94, which I really wouldn't expect it to, but don't know enough about Word's Find property to say definitively, and given how many things in Word work counter-intuitively, I'm prolly wrong there.

The 'Replace' is the 11th parameter, and I've counted it twice and the '2' is in the 11th place.

Nope, no space in the wildcard expression.

Friday, December 23, 2011 4:53 PM
• Hi Doris

This search string works for me in the UI on the text you posted here: (^94)(*)(^94)

You'll notice there are no quotes in there...

Here's how the dialog box looks

Cindy Meister, VSTO/Word MVP
Friday, December 23, 2011 5:30 PM
• Ok, set up like that, it finds the first instance, but nothing after that.  I checked and, yes, all '^' are '^' but mayhaps they're getting translated differently?  Is there a chart anywhere that would tell me what Word translates these characters as?
Friday, December 23, 2011 7:14 PM
• Hi Dorris,

If you type 'wildcard' into Word's help system, you'll get plenty of useful info. FWIW, in a wildcard Find, the ^ symbol tells Word that what follows in the ASCII code for a character. This is typically used where the character is one that is itself not a 'Find' character in a wildcard expression (^ is such a character).

As for finding only the first instance, that's all that 'Replace' or 'Find Next' on its own will do - 'Replace All' will do as its name suggests. If only one instance is found, that suggests there was only one instance to find.

Do note that the Find expression you're working with can produce some unexpected results. If there are ^ characters anywhere in your document other than around the words you want to underline, there's a risk that the underlining will be applied to the wrong ranges. This could happen if someone's used a mathematical expression with the ^ operator, for example. When working through Word's GUI, the Find starts at the current insertion point. If that's in the middle of one of the strings you're interested in, you could end up with large blocks of everything except your string being underlined!

To reduce the risk of unwanted matches, you could code the Find/Replace expression as:

Find = ( )^94(*)^94( )
Replace = \1\2\3
to ensure the matched string has at least one space each side; or

Find = ^94([A-Za-z]{1,})^94
Replace = \1
to ensure the matched string contains only letters (no numbers or spaces); or

Find = ^94([0-9A-Za-z]{1,})^94
Replace = \1
to ensure the matched string contains only letters and/or numbers (no spaces); or

Find = ( )^94([0-9A-Za-z]{1,})^94( )
Replace = \1\2\3
to combine the above.

Cheers
Paul Edstein
[MS MVP - Word]
Friday, December 23, 2011 10:53 PM
• Hi Dorris

OK, and what were you missing in the setup of the dialog box?

And you're saying that clicking FindNext again had no effect? Because it works fine here.

You can discover what ANSI number a character has:

1. Select the character
2. Alt+F11 to open the VBA editor
3. Cttl+G to open and put the focus in the Immediate Window
4. Type, then press Enter: Asc(Selection.Range)

The number will appear in the next line.

Cindy Meister, VSTO/Word MVP
• Proposed as answer by Monday, December 26, 2011 9:09 AM
Saturday, December 24, 2011 7:01 AM
• What I am saying is that Find/Next dialog box works wonderfully from within Word.

What I am also saying is that is doesn't work for squat programmatically, which is where I need it to bleedin' work.

```with oDoc.Content.Find
.MatchWildCards = .t.
.ClearFormatting
.Forward = .t.
.Wrap = 0
.Text = "(^094)(*)(^094)"
.Replacement.ClearFormatting
.Replacement.Text = "\2"
.Replacement.font.Underline = .t.
endwith

mRpl = oDoc.Content.Find.Execute( , , , , , , , , , , 2)
```

That is my code.  Yes, I am sure that the "Replace" parameter is the 11th spot, where it is supposed to be, according to the Word Help file.

I can also promise you that my variable "mRpl" comes back as False.  Every spankin' time I try it.  I don't want to run it from the dialog box, because that's of no help to me and tells me (as far as I can tell) absofreakinlutely nothing that is getting me any closer to this thing working.  It should be so hard, should it?  One would think it shouldn't.

And yes, I'm incredibly frustrated with this - as I'm sure y'all are seeing questions about the same blessed thing.

Tuesday, December 27, 2011 9:25 PM
• Hi Dorris,

Try:

with oDoc.Content.Find
.MatchWildCards = .t.
.ClearFormatting
.Forward = .t.
.Wrap = 0
.Text = "(^094)(*)(^094)"
.Replacement.ClearFormatting
.Replacement.Text = "\2"
.Replacement.font.Underline = .t.
.Execute , , , , , , , , , , 2
endwith

or:

with oDoc.Content.Find
.MatchWildCards = .t.
.ClearFormatting
.Forward = .t.
.Wrap = 0
.Text = "(^094)(*)(^094)"
.Replacement.ClearFormatting
.Replacement.Text = "\2"
.Replacement.font.Underline = .t.
.Execute Replace:=2
endwith

Cheers
Paul Edstein
[MS MVP - Word]
• Marked as answer by Wednesday, December 28, 2011 7:03 PM
Tuesday, December 27, 2011 11:00 PM
• Hi Dorris

The reason I asked you to try with the dialog box is because this is the way to find out if the problem is with the search criteria or what it is you're trying to search.

If the dialog box doesn't work, we know there's a problem with the search criteria. Because if you can't get the dialog box to work, your code isn't going to. Then we concentrate on the criteria until we find the right combination.

If the dialog box works, then we can record a macro and you can compare how Word wants you to use the search criteria (the correct syntax).

That's much more efficient than trying to "tweak" your code, running it, and saying "it doesn't work, but we don't know why".

Since no one here uses VFP we can't test it for you, we can only make suggestions based on our knowledge of Word's object model. If it seems this is a problem with how VFP intergrates with Word, then I recommend you go to a VFP forum and ask there. Someone like Tamar Granor, with a long history of automating Office using FoxPro, might know a trick you need.

Cindy Meister, VSTO/Word MVP
Wednesday, December 28, 2011 10:57 AM
• Cindy

It was working in the dialog box, and it's working in the code now - apparently the problem was actually caused by moving the .Execute outside of the With..EndWith box.  It seems to REALLY want to be inside the With..EndWith box.

Thanks all

Wednesday, December 28, 2011 7:05 PM
• - apparently the problem was actually caused by moving the .Execute outside of the With..EndWith box.  It seems to REALLY want to be inside the With..EndWith box.

Hi Dorris,

The problem was probably caused by:
mRpl = oDoc.Content.Find.Execute( , , , , , , , , , , 2)
which looks like it should have been simply:
oDoc.Content.Find.Execute( , , , , , , , , , , 2)

Cheers
Paul Edstein
[MS MVP - Word]
Thursday, December 29, 2011 3:34 AM
• It was working in the dialog box, and it's working in the code now - apparently the problem was actually caused by moving the .Execute outside of the With..EndWith box.  It seems to REALLY want to be inside the With..EndWith box.

That could make sense if VFP interprets With...End With the way C# works with using. The End With in that case would "throw away" all the settings you made on Content.Find and you'd revert back to the defaults outside the With...End With.
Cindy Meister, VSTO/Word MVP
Thursday, December 29, 2011 9:56 AM
• I was wanting to see what the .execute returned, which is why I pulled it out of the With..Endwith.

Thursday, December 29, 2011 2:27 PM
• That's not how VFP works with "native" VFP code.

For example, I can set several form variables with

With Thisform

.AutoCenter = .t.

.Fontname = 'Arial'

.Fontsize = 10

endwith

and those will hold through out the life of the form.

It could be something in the 'translation' from VFP object handling through to Word object handling.

Anyway, it's working now - and now I just need to figure out why my 3rd table is going to another page leaving most of the previous blank.

Thursday, December 29, 2011 2:31 PM
• Hi Dorris

<<now I just need to figure out why my 3rd table is going to another page leaving most of the previous blank. >>

Would actually be best in a new question, but...

I'd check the paragraph formatting of the text in the table. If it's all set to "Keep with next" and "Keep lines together" that would force Word to put the entire table (and possibly any text following it" on a single page, rather than letting it break across the page. (Look in the Paragraphs dialog box, Page and Line breaks tab.)

the other thing that could cause this, although I wouldn't expect it with a "standard" table generated using code, is that the table is "floating" (text can flow around it, as if it were a picture).

Cindy Meister, VSTO/Word MVP
Thursday, December 29, 2011 5:06 PM
• I've basically set up the document as

Instructions

Table #1

Instructions

Table #2

Instructions

Table #3

It's this 3rd table that's sliding to the new page, and it's doing it from the first row on.

At the end of Tables 1 and 2 I'm doing

oTab.Range.Collapse(0)
oDoc.Range.Collapse(0)
(Print directions for next section - then setting up next table as follows)

oDoc.Range.Collapse(0)
oDoc.PageSetup.SectionStart = 0 && Continuous
sRange = oDoc.Sections[2].Range
Rele oTab
oTab.Rows.allowbreakacrosspages = .F.
oTab.AllowAutoFit = .F.
oTab.Rows.SetHeight(oApp.InchesToPoints(1.2),1)

Like I said, the first and 2nd tables are working fine, any the only difference between the first two and the third is that the third is a one column table so that the command is actually

oTab = oDoc.Tables.Add(sRange, 1, 1)  and the range name is different, of course.

Thursday, December 29, 2011 5:14 PM
• Hi Dorris

But none of that tells me what the paragraph formatting settings are which I mentioned. The things I mentioned are what usually cause text to "break" to another page, even if there would be room on it...

(I don't know how to interpret: Rele oTab. Is this like setting to Nothing in VBA (or to null in C#)? "Release", perhaps?)

Cindy Meister, VSTO/Word MVP
Thursday, December 29, 2011 5:27 PM
• The only paragraph settings I have are:

DocRange = oDoc.Range()
DocRange.Font.Name = 'Courier New'
DocRange.Font.Size = 10.5
DocRange.ParagraphFormat.LineSpacingRule = 0  && wdLineSpaceSingle
DocRange.ParagraphFormat.KeepTogether = .T.
DocRange.ParagraphFormat.KeepWithNext = .T.

and yes, "Rele oTab" is equivalent to "Release oTab"

Thursday, December 29, 2011 5:35 PM
• Hi Dorris

<<DocRange.ParagraphFormat.KeepTogether = .T.
DocRange.ParagraphFormat.KeepWithNext = .T.>>

Yes, these are exactly the settings I mentioned.

Select an entire row somewhere in the top third/middle of the table, go to the Paragraphs dialog box, Line and Page breaks tab and deactivate these two settings. Does the top part of the table jump back to the mostly empty page?

If yes, you can see the effect of these settings and why you're seeing the behavior. You have to think about how/where/under what circumstances the table will be allowed to split across the page if you want it to fill the empty space...

Cindy Meister, VSTO/Word MVP
Thursday, December 29, 2011 5:50 PM
• It appears to be happening when the table has enough rows to go to the next page.  For example, rows 1-6 are on the same page (say Page 7)  as the instructions.  Once row 7 has been added (oTab.Rows.Add()), rows 1-6 slide to the next page (page 8) and the next row starts page 9.

I did as you suggested and changing the Paragraph settings made no difference.  When I set formatting on, I see the paragraph symbol at the end of the instructions, then the Continuous Section Break, and at the bottom of the page I see a 'soft' page break.  If I physically remove the Section Break, the table comes back up to where it should be, but if I run without a section break, I don't know how to define the range for the table so that it doesn't start all over on page 1.

Again, I'm adding a new section before the 2nd table and that works just fine - so I'm a little confused as to why it's not working for the 3rd table

Thursday, December 29, 2011 7:05 PM
• Hi Dorris,

<<now I just need to figure out why my 3rd table is going to another page leaving most of the previous blank. >>

The reason the 3rd table is going to the next page is because of the combination of:
and:
oTab.Rows.allowbreakacrosspages = .F.
That tells me you're creating a single-celled table that is not allowed to split across a page break. This condition is being reinforced by:
DocRange.ParagraphFormat.KeepTogether = .T.
and:
DocRange.ParagraphFormat.KeepWithNext = .T.

Evidently, there's insufficient space for the 3rd table to fit on the current page, so it shifts to a new page. Changing:
DocRange.ParagraphFormat.KeepTogether = .T.
and/or:
DocRange.ParagraphFormat.KeepWithNext = .T.
won't be sufficient to allow the 3rd table to split across the page break (if that's what you want it to do) while you have 'oTab.Rows.allowbreakacrosspages = .F.' In any event, even with the 'DocRange.ParagraphFormat.KeepWithNext = .T.' parameter applied to all paragraphs (which includes the table because it falls within DocRange), there comes a point when Word has to split the page. You've apparently passed that point.

Finally, if there's no content after your 3rd table, you'll find that merely getting it onto the previous page will leave a blank page at the end of the document. In that case, you might be able to delete that page by formatting the document's last paragraph with a 1pt font and no before/after spacing.

Cheers
Paul Edstein
[MS MVP - Word]
Thursday, December 29, 2011 8:47 PM
• If I physically remove the Section Break, the table comes back up to where it should be, but if I run without a section break, I don't know how to define the range for the table so that it doesn't start all over on page 1.

Hi Dorris,

You manage that via 'oTab = oDoc.Tables.Add(sRange, 1, 1)'. If you want the table to be inserted at the end of the document, for example, simply change 'sRange = oDoc.Sections[2].Range', to 'sRange = oDoc.Paragraphs.Last.Range'. Your code will then insert the table before the final paragraph break (you can't insert one after it).

If that's not where you want the table to go, tell us where and we'll tell you how to get it there without the need for a Section break.

Cheers
Paul Edstein
[MS MVP - Word]
Thursday, December 29, 2011 9:04 PM
• I'll give that a try.  I guess my biggest problem is not understanding why, if I'm setting the 3rd table up the same way as the first two (with the exception of the number of columns) and I'm treating it the same way as far as adding rows and such, is it acting differently?  I really am trying to understand the hows and whys of what is going on with this code
Friday, December 30, 2011 8:06 PM
• Sure enough, that took care of the issue, Thanks Paul

Friday, December 30, 2011 8:10 PM