0
votes

I am trying to insert a table of contents into a Word document using one of the built-in building blocks that define a table of contents. When I record a macro and insert a table of contents the macro recorder gives this line of code:

 Application.Templates( _
        "C:\Users\me\AppData\Roaming\Microsoft\Document Building Blocks\1033\16\Built-In Building Blocks.dotx" _
        ).BuildingBlockEntries("Automatic Table 1").Insert Where:=Selection.Range, RichText:=True

I have verified that this name – Automatic Table 1 - exists in the building block organizer. And of course, the table of contents does get inserted properly when I use the menu bar ribbon buttons to insert the table of contents.

But when I take that same line of code and put it into a VBA macro, I get an error saying that the requested item does not exist. Is it possible to reference building block items from VBA code? Can anyone tell me what I am doing wrong, or how to achieve my goal? Thank you.

3
By the way, if possible, create your own building block for a TOC using the Custom TOC choice to start. The ones that come with Word are inside Content Controls that slow things down. You would likely want to store that Building Block in the same template that holds your macro.Charles Kenyon
Thank you - I will try that!Kevin

3 Answers

2
votes

The macro recorder is just a starting point. The sample you posted is dependent on a very specific path to the building block template, one that includes your user name and the language you are using (1033 is U.S. English) and the version of Word (16 for Word 2016 and 2019). In addition, the building blocks template location does not have a VBA shortcut to reach it.

What will work more reliably is to insert the table to your macro template or a document based based on that template. Select the table, then choose Insert>Quick Parts>AutoText>Save Selection to AutoText Gallery. You can set the name to whatever you prefer. Set the Gallery to Table of Contents. OK out, then delete the table sample.

Now you can use simpler, more reliable code like this:

ActiveDocument.AttachedTemplate.BuildingBlockEntries("TOC1").Insert Where:=Selection.Range, RichText:=True
2
votes

Word doesn't load the Building Blocks at startup, they are loaded on demand. When you click on one of the Building Block galleries you may notice a short pause before the gallery is displayed, which is Word loading them.

You can safely instruct Word to load the Building Blocks by using Application.Templates.LoadBuildingBlocks in your code without an error being generated if they are already loaded.

The path to the built-in Building Blocks is also only valid for you, but you can get around that by using Environ to return the first part of the path.

Sub InsertTOC()
   Dim path As String
   Application.Templates.LoadBuildingBlocks
   path = Environ$("USERPROFILE") & "\AppData\Roaming\Microsoft\Document Building Blocks\1033\16\Built-In Building Blocks.dotx"
   Application.Templates(path).BuildingBlockEntries("Automatic Table 1").Insert Where:=Selection.Range, RichText:=True
End Sub
2
votes

A recorded macro will seldom do what you want, especially if you are sharing the template.

Writing a Macro

To do this, you need to know:

  • The name of the building block

  • The name (and location) of the template that holds the building block unless the macro is in the same template

  • How to insert a macro. See Installing Macros and Install/Employ VBA Procedures (Macros).

  • Building Block Name = "MyBB" (example in this macro, change to fit)

Situation 1 and 1a have the Building Block and the macro in the same template. This simplifies coding because a macro can always tell the name and location of the template that holds it. That information is required to use a macro to insert a building block.

Situation 1 - template holds both the building block and the macro

Here is the macro to insert that uniquely-named building block at the insertion point in the document:

Sub InsertMyBB()
'  Will not work if there are multiple building blocks with the same name in the template! See below.
'
   Dim sBBName As String
   sBBName = "MyBB"
   On Error GoTo Oops
   Application.Templates.LoadBuildingBlocks ' Thank you Timothy Rylatt!
   Application.Templates(ThisDocument.FullName).BuildingBlockEntries(sBBName).Insert _
      Where:=Selection.Range, _
      RichText:=True ' Insert MyBB Building Block
   Exit Sub ' We're done here
Oops: ' Didn't work - building block not there!
   MsgBox Prompt:="The Building Block " & sBBName & " cannot be found in " & _
      ThisDocument.Name & ".", Title:="Didn't Work!"
   On Error GoTo 0
End Sub

This and the following macro are both contained in a demonstration template that can be downloaded from my downloads page.

Situation 1a - template holding building blocks and macro in same template - multiple building blocks with the same name

In this situation, the previous macro would confuse Word and give unpredictable (to the user) results. In this case, the macro needs to know both the gallery and category of the building block. The following macro assumes that the building block is stored in the AutoText gallery and in the General category. You can find the name of the gallery and category using the Building Blocks Organizer. Category names are plain text. Galleries are referenced in vba as Building Block Types and use constants. You can find a list of the constants for the different galleries here.

Sub InsertMyBB()
'
' Assumes that the Building Block is of the type AutoText (wdTypeAutoText) in Category "General"
' See https://msdn.microsoft.com/en-us/library/bb243303(v=office.12).aspx
'
' This is based in part upon contributions from Greg Maxey and Jay Freedman - any errors remain mine
' Written by Charles Kenyon February 2016
'
   Dim sBBName As String
   Dim sTempName As String
   Dim oBB As BuildingBlock
   sBBName = "MyBB" 'use the name of your building block instead of "MyBB"
   sTempName = ThisDocument.FullName ' puts name and full path of template in string variable
   On Error Resume Next
   Application.Templates.LoadBuildingBlocks  ' thank you Timothy Rylatt
   Set oBB = Application.Templates(sTempName).BuildingBlockTypes(wdTypeAutoText) _
      .Categories("General").BuildingBlocks(sBBName)
   If Err.Number = 0 Then
      oBB.Insert Selection.Range, True
   Else
      MsgBox Prompt:="The Building Block '" & sBBName & "' cannot be found in " & _
         ThisDocument.Name & ".", Title:="Didn't Work!"
   End If
   On Error GoTo 0
lbl_Exit:
   Exit Sub
End Sub

This and the preceding macro are both contained in a demonstration template that can be downloaded from my downloads page.

Situation 2 - template holding building block is in Word Startup Folder and named MyBBTemplate.dotx

This template, for some reason, does not hold the macro, it is in a separate template. We know the name of the container template. The name of the template containing the macro does not matter for our purposes.

Sub InsertMyBB()
'  Will not work if the Startup Folder is the root directory of a drive, i.e. C:\
'  For use with building block stored in a template loaded in the Word Startup Folder that does NOT hold this macro
'  Will not work if there are multiple building blocks with the same name in the template!
'
   Dim sBBName As String
   Dim sTemplateName as String
   Dim sStartupPath as String
   sBBName = "MyBB"
   sTemplateName="MyBBTemplate.dotx"
   sStartupPath = Application.Options.DefaultFilePath(wdStartupPath)
   On Error GoTo Oops ' error handler
   Application.Templates.LoadBuildingBlocks  ' thank you Timothy Rylatt
   Application.Templates(sStartupPath & "\" & sTemplateName).BuildingBlockEntries(sBBName) _
      .Insert Where:=Selection.Range, RichText:=True ' Insert MyBB Building Block
   Exit Sub ' We're done here
Oops: ' Didn't work - building block not there!
   MsgBox Prompt:="The Building Block " & sBBName & " cannot be found in " & _
      sTemplateName".", Title:="Didn't Work!"
   On Error GoTo 0
End Sub

Situation 3 - template holding building block is "Building Blocks.dotx" in the building blocks storage location

This template also does not hold the macro because templates in the building blocks folder do not contribute macros to Word even if they contain them. This macro incorporates code from Greg Maxey and Jay Freedman given in this thread. Building Blocks.dotx is the name of the template used, by default, to store custom building blocks other than AutoText. It is stored, by user, in a language-dependent, version-dependent folder. This macro is intended to retrieve the building block regardless of the language or version.

Sub InsertMyBB()
'  Based on code by Greg Maxey and Jay Freedman
'  For use with building block stored in the default custom building blocks file "Building Blocks.dotx"
'  Will not work if there are multiple building blocks with the same name in the template!
'
   Templates.LoadBuildingBlocks ' in case building blocks not yet accessed
   Dim sBBName As String
   Dim sStartupPath as String
   Dim sTemplateName as String
   sBBName = "MyBB"
   sTemplateName="Building Blocks.dotx"
   sStartupPath = Application.Options.DefaultFilePath(wdStartupPath)
   On Error GoTo Oops ' error handler
   Application.Templates(sStartupPath & "\" & sTemplateName).BuildingBlockEntries(sBBName) _
      .Insert Where:=Selection.Range, RichText:=True ' Insert MyBB Building Block
   Exit Sub ' We're done here
Oops: ' Didn't work - building block not there!
   MsgBox Prompt:="The Building Block " & sBBName & " cannot be found in " & _
      sTemplateName & ".", Title:="Didn't Work!"
   On Error GoTo 0
End Sub

The various code for inserting building blocks is from my page on AutoText, Building Blocks and AutoFormat.