PDA

View Full Version : Insert Field In Bookmark Range



gmaxey
12-07-2010, 01:47 PM
Maybe I should know better but I am just stumped. I want to programtically insert a field and the wrap it in a bookmark range. The best I can cobble together is:

Sub ScratchMacro()
'A quick macro scratch pad created by Greg Maxey
Dim oRng As Word.Range
Dim oFld As Word.Field
Set oRng = ActiveDocument.Range
Set oFld = oRng.Fields.Add(oRng, wdFieldPage)
oFld.Select
ActiveDocument.Bookmarks.Add "Test", Selection.Range
End Sub


Is there a more elegant way? Do I have to use "Select" and Selection.Range?

Thanks.

macropod
12-07-2010, 02:21 PM
Hi Greg,

Try:
Sub ScratchMacro()
Dim oRng As Word.Range
Dim oFld As Word.Field
Set oRng = ActiveDocument.Range(0, 0)
Set oFld = oRng.Fields.Add(oRng, wdFieldPage)
oRng.MoveEnd Unit:=wdWord, Count:=1
oRng.Bookmarks.Add "Test", oRng
End Sub

gmaxey
12-07-2010, 03:02 PM
Paul,

Thanks. I had already tried: oRng.MoveEnd wdField, 1 but that didn't work ;-)

It seems that there shoud be a clean way to:

1. Add a field to an existing bookmark range.
or
2. Reference the range of a field.

macropod
12-07-2010, 03:30 PM
Hi Greg,

The code I posted works just fine for me in both Word 2000 and Word 2007!

I note that you tried 'oRng.MoveEnd wdField, 1', but 'wdField' isn't a valid parameter. Form Word's Help file on MoveEnd:


Unit Optional Variant. The unit by which to move the ending character position. Can be one of the following WdUnits constants: wdCharacter, wdWord, wdSentence, wdParagraph, wdSection, wdStory, wdCell, wdColumn, wdRow, or wdTable. If expression returns a Selection object, wdLine can also be used. The default value is wdCharacter.You can even use 'oRng.MoveEnd Unit:=wdCharacter, Count:=1', since extending a range into a field necessarily incorporates the whole field.

gmaxey
12-07-2010, 06:37 PM
Paul,

I'm sorry. Yes your method works just I fine and I forgot to mention that.

It just seems that there should be away to reference a field's range or even better add a field to an existing bookmark range without having to either select the field or manipulate the range.

macropod
12-07-2010, 08:23 PM
Hi Greg,

Yes, that would be nice, but at least there is a more elegant way than using "Select" and Selection.Range.

fumei
12-08-2010, 09:59 AM
A variation using the standard put-in-bookmark procedure.

Sub FillBM(strBM As String, FieldType As Long)
Dim r As Range
Set r = ActiveDocument.Bookmarks(strBM).Range
r.Fields.Add r, FieldType
r.MoveEnd Unit:=wdCharacter, Count:=1
ActiveDocument.Bookmarks.Add Name:=strBM, Range:=r
End Sub

Sub Yadda()
Call FillBM("There", 29)
End Sub
Puts wdFieldFileName into the bookmark "There".

You could also do as a string.
Sub FillBM(strBM As String, FieldType As String)
Dim r As Range
Set r = ActiveDocument.Bookmarks(strBM).Range
r.Fields.Add r, FieldType
r.MoveEnd Unit:=wdCharacter, Count:=1
ActiveDocument.Bookmarks.Add Name:=strBM, Range:=r
End Sub

Sub Yadda()
Call FillBM("There", wdFieldFileName)
End Sub
It essentially does the same thing as macropod does, moving the End of the range object.

1. Add a field to an existing bookmark range.
or
2. Reference the range of a field.
#1 can be done, as per above.

#2 is a problem because the range of a field changes depending on whether field codes are showing, or not.

gmaxey
12-08-2010, 12:18 PM
Gerry,

Thanks. What I meat by #1 was something along the lines of:
Dim oRng as Range
Set oRng = ActiveDocument.Bookmarks("Test").Range
Set oFld = oRng.Fields.Add(oRng, wdFieldPage)

It doesn't seem to be possible.

fumei
12-08-2010, 01:37 PM
Actually it IS possible, that is, the code will execute, but the field will not be INSIDE the bookmark. It acts like all additional content to a bookmark - it goes outside the bookmark.

The only way I know of to get ANY content actually inside the bookmark boundaries is that re-creation technique.

I assume if you put other content into bookmarks (actually inside and want to retain the bookmark) you use something similar to FillBM. You could easily alter it to be able it to put a field in the bookmark.
Sub FillBM(strBM As String, _
strText As String, _
Optional bolField As Boolean)
Dim r As Range
Set r = ActiveDocument.Bookmarks(strBM).Range
If bolField Then
r.Fields.Add r, strText
r.MoveEnd Unit:=wdCharacter, Count:=1
Else
r.Text = strText
End If
ActiveDocument.Bookmarks.Add Name:=strBM, Range:=r
End Sub
That way, if you do not add the optional parameter, it acts like a "normal" bookmark insertion procedure, but if you add the boolean in as a parameter it will function as a field insertion procedure.

As an aside, I always like to bookmark tables so I can make a reference to them by name (i.e. the bookmark). It makes it a lot easier dealing with tables, when you can point to them by name. Here is the same add-inside-bookmark procedure for inserting a table.
Sub FillBMWithTable(strBM As String, _
TableRowCount As Long, _
TableColCount As Long)
Dim yadda As Table
Set yadda = ActiveDocument.Tables.Add(ActiveDocument.Bookmarks(strBM).Range, _
TableRowCount, TableColCount)
With ActiveDocument
.Bookmarks(strBM).Delete
.Bookmarks.Add strBM, yadda.Range
End With
End Sub

gmaxey
12-08-2010, 02:19 PM
Gerry, Yes I knew that you can write code to add a field to a bookmark range, but as you describe it isn't "in" the range. :-(

I'll keep your code snipets as they will come in handy. Thanks.

fumei
12-08-2010, 02:41 PM
You know, I have always found that property of bookmark ranges to be a kind of bug. I have never really accepted that:

bookmark.range.text = "whatever"

will put the string AT the bookmark location, but delete the bookmark itself.

It seems wrong to me. InsertBefore does not work. InsertAfter does not work. InsertAt (so to speak) via .Text does not work. Seems sloppy.

I mean, after all, the .Start and .End of the bookmark range are very flexible using the GUI. Say you have a table bookmarked (as I usually do). You can put 100 new rows into the table. No problem. It remains bookmarked. The .End is gracefully changed. You can grab the table and move it 400 pages downstream - the main reason I like to bookmark tables!. No problem. It remains bookmarked. The .Start and the .End are gracefully changed.

So what is the big deal about inserting text (or any other content) into it?

But, sigh, there ya go.

gmaxey
12-08-2010, 07:40 PM
Well said. I am on your side ;-)

macropod
12-09-2010, 11:29 PM
Count me in too!

Tinbendr
12-10-2010, 06:33 AM
bookmark.range.text = "whatever"

will put the string AT the bookmark location, but delete the bookmark itself.
Makes me wonder what their original intent was.

It really would be nice to have a BookMark(Index).Permanent = True property.

fumei
12-10-2010, 09:16 AM
"It really would be nice to have a BookMark(Index).Permanent = True property."

Except...that would mess up most of the good stuff bookmarks do - like the moving around within the document I cited as an example.

Say the table I have bookmarked is named "Client", and its Index is 4 (fourth in the document by range).

I move it downstream a bunch. Its [b]name" is still "Client", but now it is 15th in the document.

Are you saying it would be good to retain its Index as 4?

Hmmmmm. I am inclined to disagree.

fumei
12-10-2010, 10:38 AM
The issue is that bookmark.Range.Text deletes the bookmark range.

Original bookmarked text = "Yadda"

Instruction is: BM.Range.Text = "The new text here"


Taken as a straightforward replacement of content, then IMO, the internal actions should be:


1. BM.Range.Text = "" (removing the original text)
2. BM.Range.Start = BM.Range.Start (keeping original starting location)
3. BM.Range.Collapse = 1 (collapse range to a point)
4. BM.Range.InsertAfter Text (the string value of BM.Range.Text)
5. BM.Range.End = BM.Range.Start + Len(.Text)

IMO the above is implied by making the instruction for the .Text to be "x".

I want the Range.Text to be the text of Range.Text

Instead, what Word does is:

1. Insert .Text at Range.Start
2. delete the bookmark

How bloody sensible is that???

You can use Selection (i.e. put the cursor inside the bookmark and type whatever you want. The bookmark is gracefully adjusted.

AND, you can do this (changing text content INSIDE a bookmark) via VBA, without the re-creation technique...BUT you have to use Selection (the GUI).

Sub FillBM_UsingSelection(strBM As String, strText As String)
ActiveDocument.Bookmarks(strBM).Select
With Selection
.MoveStart 1, 1
.MoveEnd 1, -1
.Delete
.TypeBackspace
.TypeText strText
.Delete Unit:=wdCharacter, Count:=1
End With
End Sub

Sub TryIt()
Call FillBM_UsingSelection("test", "yadda ta-de-dum")
End Sub


The text content inside the bookmark is changed, and the bookmark is still there (not re-created).

But it uses Selection and that sucks.

gmaxey
12-10-2010, 11:24 AM
Gerry,

You realize this selection process won't work if there are not 3 or more characters in the range?

fumei
12-10-2010, 11:41 AM
Yes, I was just posting a very simplified version with no testing for minimal characters, or if the bookmark is "empty". I never use it this way because it uses Selection.

Tinbendr
12-10-2010, 02:22 PM
Are you saying it would be good to retain its Index as 4?Yes, I guess I am.

Just like a formfield, but without the OLE.

aDoc.Formfield(1).Result = ""

Doesn't delete the formfield, just makes it's contents (range) empty.

(Or it could be a conspiracy to create work for VBA programmers! :motz2:

fumei
12-13-2010, 09:53 AM
Whoa. Hold on there Tinbendr. If I can repeat...

"It really would be nice to have a BookMark(Index).Permanent = True property."

While, true, FormField.Result = "blah", does not affect the index, moving the formfield certainly does.

.Result has no effect on the index. However, a formfield is a different beastie from a bookmark. Never mind I often see people refer to "bookmark", when they are actually dealing with a formfield.

A formfield is an object. A bookmark is not. EXCEPT for being, or more accurately, using a range. A bookmark is simply - essentially - two pointers: a Range.Start and a Range.End.

Taking some numbers.

"Yadda", with "Y" = (say) 234, and the terminating "a" = 238.

In the GUI, if you insert "blah" into "Yadda" - "Yadblahda" - .End gracefully adjusts. You also do this in VBA.
Dim r As Range
Set r = ActiveDocument.Bookmarks("there").Range
With r
.Collapse 1
.MoveStart Unit:=wdCharacter, Count:=3
.InsertAfter "blah"
End With
Set r = Nothing
The text "blah" is inserted, and the bookmark persists.

Expanding on this, you can - in fact - use a non-Selection procedure to insert text into a bookmark without the re-creation technique.Sub Kluge_BM(strIn As String)
Dim r As Range
Dim j As Long
j = Len(strIn)
Set r = ActiveDocument.Bookmarks("there").Range
With r
.Collapse 1
.MoveStart unit:=wdCharacter, Count:=1
.InsertAfter strIn
.Collapse 1
.MoveStart unit:=wdCharacter, Count:=-1
.Delete
.End = ActiveDocument.Bookmarks("there").Range.End
.MoveStart unit:=wdCharacter, Count:=j
.Delete
End With
Set r = Nothing
End Sub

Sub TryBM()
Call Kluge_BM("Gerry")
End Sub
This does replace the contents inside the bookmark. HOWEVER, it requires a minimum of two characters inside the bookmark.

The re-creation technique does not.

On the other hand, try this: have the Selection collapsed to apoint. Insert a bookmark. Now try, using the GUI, to put text inside the bookmark. You can NOT.

However, with the re-creation technique, you can.

So it is simply weird, IMO.

With the GUI, you can type text and backspace inside the bookmark, replacing the text, and the bookmark persists. Note though that you can NOT select all the text at once and do this - it deletes the bookmark.

But you can NOT insert text in an "empty" bookmark with the GUI.

With VBA (using the re-creation technique), you CAN insert text into an "empty" bookmark.

Go figure.

macropod
12-13-2010, 03:46 PM
Hi Greg,

Getting back to the original discussion, perhaps:
Sub ScratchMacro()
Dim oRng As Word.Range
Dim oFld As Word.Field
Set oRng = ActiveDocument.Range.Duplicate
Set oFld = oRng.Fields.Add(oRng, wdFieldPage)
oRng.Bookmarks.Add "Test", oRng
End Sub

gmaxey
12-13-2010, 03:59 PM
Paul,

Looking better for sure. Once again that seldom used ".Duplicate" works a wonder.

Thanks.