PDA

View Full Version : Using InsertFile method to insert into a BookMark



kayfelix
02-13-2021, 12:07 PM
Hi All,

I have a word document where I use "building blocks" being seperate word documents that I insert into the main document. I can InsertFile the text, I can even place it at a bookmark - but I want the option of eing able to delete the block using VBA, but I can't seem to encapsulate the insertfile text into a bookmark.

The first example is what I get, the second is what I want: https://pasteboard.co/JOcISUG.jpg

Does anyone have any ideas how I can insert the text into the bookmark, or set the bookmark afterwards (but how do I get the range), or some other way of achieving this functionality?

Thanks

gmaxey
02-13-2021, 02:34 PM
If your bookmark stands alone in a paragraph, you can do it like this:


Sub ScratchMacroII()
'A basic Word macro coded by Greg Maxey
Dim oRng As Range
Set oRng = ActiveDocument.Bookmarks("bmTarget").Range
oRng.InsertFile "D:\FileToInsert.docm"
Set oRng = oRng.Paragraphs(1).Range
oRng.End = oRng.End - 1
ActiveDocument.Bookmarks.Add "bmTarget", oRng
lbl_Exit:
Exit Sub
End Sub

gmaxey
02-13-2021, 03:16 PM
Or maybe this:


Sub ScratchMacro()
'A basic Word macro coded by Greg Maxey
Dim oRng As Range
Dim oRngNC As Range
Set oRng = ActiveDocument.Bookmarks("bmTarget").Range
Set oRngNC = oRng.Characters.Last
oRng.InsertFile ("D:\FileToInsert.docm")
oRngNC.End = oRngNC.End - 1
ActiveDocument.Bookmarks.Add "bmTarget", oRngNC
lbl_Exit:
Exit Sub
End Sub

kayfelix
02-14-2021, 06:40 AM
Hi Greg,

Wow, that's had me stumped for the longest time, definitely works for me - I can now insert and delete blocks. Thank you!

For some reason it adds a new line with every time I run the code, but the new line character is outside the bookmark. I'm not sure if it is being imported from the file and then ignored or if the bookmark range is just a little off?

Actually, I do think its something to do with the range. What does this line do, why subtract the one? I changed it to + 1 and now it encapuslates the new line character that was previously left out, but I'm not sure if that is the correct fix.

oRng.End = oRng.End - 1

kayfelix
02-14-2021, 08:13 AM
ah, I see what you mean about the paragraph - lots of my documents do have multiple paragraphs, tables - some even have a book mark or two of their own. Can Word handle such insertions into bookmarks?

gmaxey
02-14-2021, 09:05 AM
That extra line is a result of the final paragraph in the file to insert. You could trim it as follows. Yes, files to insert can have multiple paragraphs and bookmarks (as long a one of the bookmarks is not named bmTarget).


Sub ScratchMacro()
'A basic Word macro coded by Greg Maxey
Dim oRng As Range
Dim oRngNC As Range
Set oRng = ActiveDocument.Bookmarks("bmTarget").Range
Set oRngNC = oRng.Characters.Last
oRng.InsertFile ("D:\FileToInsert.docm")
oRngNC.Characters.Last.Previous.Delete 'Added
oRngNC.End = oRngNC.End - 1
ActiveDocument.Bookmarks.Add "bmTarget", oRngNC
lbl_Exit:
Exit Sub
End Sub

kayfelix
02-16-2021, 03:08 PM
Hi Greg,

Thank you! Yes that was exactly it, they are now inserting perfectly, I can edit, remove and select the blocks beautifully.

Wish I'd had found https://gregmaxey.com/word_tip_pages/insert_text_at_or_in_bookmark.html prior to asking here!

I do have an issue where some of the blocks don't contain a double paragraph mark at the end (which was giving me the problem initally), but I think the solution will be to simply add it to the few that are missing it, or remove all of them. I think the former will be easier as it appears far more often.

So far I've been testing whether "bm_target" exists, with numbers appended for multiple bookmarks, and if it doesn't then I insert a new bookmark:


ActiveDocument.Bookmarks.Add Name:=BMVAR, Range:=ActiveDocument.Paragraphs(ActiveDocument.Paragraphs.Count).Range

But that places the new bookmark at the last paragraph. If I insert a block that doesn't contain that last paragraph, the next bookmark is inserted within the last part of the previous bookmark and the whole process breaks down (partially). But that brought me to another issue, since I didn't really want to just insert blocks at the end - wanting to allow users to insert blocks at arbitrary locations, as some pre-defined templates have several pages and blocks need to be inserted in the middle. As I can check whether a bookmark at selection exists, how can I insertparagraphafter and also select that paragraph as range for bookmark insertion?


On Error Resume Next
Dim BMRng As Word.Range


If Selection.Bookmarks(1).Name <> "" Then 'we are in a bookmark, so make a paragraph and then insert the next BM.
ActiveDocument.Bookmarks.Item("Test_BM").Range.InsertParagraphAfter 'works but how do I select this
Selection.TypeText "Test" 'currently types inside my bookmark

End If


If Err.Number = 5941 Then MsgBox "We are not in a BM" ' We are not in a bookmark can insert new block at selection


Any pointers would be great! I haven't responded until now as I was trying to find a solution on my own...

gmaxey
02-16-2021, 03:31 PM
I don't really understand the problem. Bookmarks are a bit dated in the modern world. Why don't you use a richtext content control:


Sub ScratchMacro()
'A basic Word macro coded by Greg Maxey
Dim oRng As Range
Dim oRngNC As Range
Set oRng = ActiveDocument.SelectContentControlsByTitle("CCTarget").Item(1).Range
oRng.InsertFile ("D:\FileToInsert.docm")
Set oRng = ActiveDocument.SelectContentControlsByTitle("CCTarget").Item(1).Range
oRng.Characters.Last.Delete
lbl_Exit:
Exit Sub
End Sub

kayfelix
02-16-2021, 04:39 PM
Thanks! I haven't used them much, but they are quite cool - I do like that I can set a title, should make management a little easier.

However, I have the same issue as I do with bookmarks, when I place the insertion point in the middle of my document, how can I insert multiple blocks one after another? As it stands the insertion point remains at the top / front and a subsequent content control is inserted inside the recent control. Say I want to quickly import three user selected documents:


'A basic Word macro coded by Greg Maxey / modified by Kay
Dim oRng As ContentControl
Set oRng = ActiveDocument.ContentControls.Add(wdContentControlRichText)
oRng.Range.InsertFile ("E:\Templates\Template Building Blocks\block1.docx")
oRng.Title = "This is a great block!"
oRng.Range.Select
ActiveDocument.Fields.Update

Thank you
Kay

gmayor
02-16-2021, 11:18 PM
For two or more controls you need to reset the range position after inserting the controls e.g. as follows. You can follow the process for more controls as required.


Dim oCC As ContentControl
Dim oRng As Range
'Set a range where you want the first control
Set oRng = Selection.Range
'Ensure that the range does not include a selection of text
oRng.Collapse 0


'add a content control to the range
Set oCC = ActiveDocument.ContentControls.Add(wdContentControlRichText, oRng)
oCC.Title = "This is a great block!"
oCC.Tag = oCC.Title
Set oRng = oCC.Range
oRng.InsertFile ("E:\Templates\Template Building Blocks\block1.docx")


'Move the end of the range out of the control
oRng.End = oRng.End + 1


'Collapse the range and type a space
oRng.Collapse 0
oRng.Text = " "


'Collapse the range again and add another content control
oRng.Collapse 0
Set oCC = ActiveDocument.ContentControls.Add(wdContentControlRichText, oRng)
oCC.Title = "This is another CC"
oCC.Tag = oCC.Title
Set oRng = oCC.Range
oRng.InsertFile ("E:\Templates\Template Building Blocks\block2.docx")

If you want to insert both texts in the same control, the method is similar e.g.

Dim oCC As ContentControl
Dim oRng As Range
'Set a range where you want the control
Set oRng = Selection.Range
'Ensure that the range does not include a selection of text
oRng.Collapse 0


'add a content control to the range
Set oCC = ActiveDocument.ContentControls.Add(wdContentControlRichText, oRng)
oCC.Title = "This is a great block!"
oCC.Tag = oCC.Title
Set oRng = oCC.Range
'Insert the first file
oRng.InsertFile ("E:\Templates\Template Building Blocks\block1.docx")

'Collapse the range and type a space
oRng.Collapse 0
oRng.Text = " "


'Collapse the range again and add another text
oRng.Collapse 0
oRng.InsertFile ("E:\Templates\Template Building Blocks\block2.docx")

gmaxey
02-17-2021, 06:42 AM
This is just a variation of Graham's working solution. It does address the extra paragraph that results when inserting a file:


Sub ScratchMacro()
'A basic Word macro coded by Greg Maxey
Dim oCC As ContentControl
Dim oRng As Range
Dim lngFile As Long
Dim varFiles
'Set a range where you want the first control
Set oRng = Selection.Range
'Ensure that the range does not include a selection of text
oRng.Collapse 0
varFiles = Split("Block1|Block2|Block3", "|")
For lngFile = 0 To UBound(varFiles)
'Add a content control to the range
Set oCC = ActiveDocument.ContentControls.Add(wdContentControlRichText, oRng)
oCC.Title = varFiles(lngFile)
oCC.Range.InsertFile "E:\Templates\Template Building Blocks\" & varFiles(lngFile) & ".docx"
Set oRng = oCC.Range
oRng.Characters.Last.Delete
'Move the end of the range out of the control
oRng.End = oRng.End + 2
oRng.Collapse 0
Next lngFile
lbl_Exit:
Exit Sub
End Sub

kayfelix
02-17-2021, 04:05 PM
Thank you Graham and Greg, I am extremely grateful for your help with this!

Consider this scenario - I have a userform with a treeview control that I populate with 10 files for textblock selection. A user selects an area on their document that they would like to insert the textblock, clicks on one of the blocks and it either insers a bookmark or richtext CC, then inserts the textblock text from an external document. The most likely scenario is the user then selects the next textblock from the treeview to insert below the first. Unfortunately with both your examples I am working relative to the original content control with each subsequent control - however when the sub exits I also lose that object, the selection.range is not updated to be outside of the control.

I was thinking I could store the last object range as public, so that I can refer to it on subsequent insertions, then step out, collapse and insert new CC, update public range - but what if the user selects another location, or wants to insert a content control between two others. Is there any way to set the selection.range to be just following my last contentCC or bookmark, so that I can reference at a later time, or use the current selection.range should the user have changed it?

I could potentially return my userform to modal, thereby not allowing the user to change the selection once they've opened the userform, and on the next userform load I can reset the current range to selection.range, then save the last range.end so I can continue from there?

kayfelix
02-17-2021, 04:28 PM
I put that last thought I had into code and this definitely will work for me (as long as the user is prevented from moving the insertion point, expecting it to be inserted there, so will need a modal form. That way I can also undo the last insertion (to be used as a preview feature).


Public TestRng As Range


Private Sub UserForm_Initialize()
Set TestRng = Selection.Range
End Sub


Private Sub TreeView1_click()
Dim oCC As ContentControl
Dim oRng As Range

If TreeView1.SelectedItem.Tag = "" Then Exit Sub 'only tagged items are files


'Ensure that the range does not include a selection of text
TestRng.Collapse 0

'Add a content control to the range
Set oCC = ActiveDocument.ContentControls.Add(wdContentControlRichText, TestRng)
oCC.Title = TreeView1.SelectedItem.Text
oCC.Range.InsertFile TreeView1.SelectedItem.Key
Set TestRng = oCC.Range

'oRng.Characters.Last.Delete
'Move the end of the range out of the control
TestRng.End = TestRng.End + 2
TestRng.Collapse 0


End Sub


Unless you can think of a better way? I would like to prevent a modal form if possible, and give the users more control over where they can place text boxes without having to come out and back into the 'program'.