0

I would like to make a VBA macro for Word 2021 that will insert an IF field with STYLEREF fields inside it, to show the beginning and ending paragraph numbers in a page header.

I am able to insert the STYLEREF fields to get the first paragraph number on the page and a STYLEREF field with \l argument to get the last paragraph number on the page, but can't work out how to write a macro to nest these two files within an IF field. (I need the IF field, because if the first number on the page is the same as the last number on the page, I want to show just one number, but if they are different, I want to show both numbers, with a hyphen between them.)

2
  • 1
    This question is similar to: Setting up a nested field in Word using VBA. If you believe it’s different, please edit the question, make it clear how it’s different and/or how the answers on that question are not helpful for your problem. Commented Nov 24 at 9:59
  • You really shouldn't be doing any of this with VBA. Rather, you should be using a template with the required field construction in-situ. Presumably you want something like: {IF{STYLEREF "MyStyle" \w}= {STYLEREF "MyStyle" \l \w} {STYLEREF "MyStyle" \w} "{STYLEREF "MyStyle" \w}-{STYLEREF "MyStyle" \l \w}"}. If you're wedded to using VBA, you could write the corresponding text string to your document and use the Convert Text Representations of Fields to Working Fields macro at msofficeforums.com/185301-post2.html Commented Nov 24 at 10:35

1 Answer 1

1

Arguably the simplest way to insert nested fields into a Word document that you want to construct on-the-fly is to construct the field coding in XML and use Range.InsertXML or Selection.InsertXML to insert it. Once you've got used to how field coding is represented in XML, it becomes quite easy to construct your own.

For example, if the field coding you need is

{ IF { STYLEREF 1 \w } = { STYLEREF 1 \l \w } { STYLEREF 1 \w } "{ STYLEREF 1 \w }-{STYLEREF 1 \l \w }" }

which would work if the style you are using is the standard "Heading 1" style, then the following WordDocument XML (it's the older Word 2003 XML standard) would be enough:

<w:wordDocument xmlns:w='http://schemas.microsoft.com/office/word/2003/wordml' xml:space='preserve'>
  <w:body><w:p>
    <w:fldChar w:fldCharType='begin'/>
       <w:r><w:instrText> IF </w:instrText></w:r>
        <w:fldSimple w:instr=' STYLEREF 1 \w '/><w:r><w:instrText> = </w:instrText></w:r><w:fldSimple w:instr=' STYLEREF 1 \l \w '/>
      <w:r><w:instrText> </w:instrText></w:r>
        <w:fldSimple w:instr=' STYLEREF 1 \w '/>
      <w:r><w:instrText> &quot;</w:instrText></w:r>
        <w:fldSimple w:instr=' STYLEREF 1 \w '/><w:r><w:instrText>-</w:instrText></w:r><w:fldSimple w:instr=' STYLEREF 1 \l \w '/>
      <w:r><w:instrText>&quot; </w:instrText></w:r>
    <w:fldChar w:fldCharType='end'/>
  </w:p></w:body>
</w:wordDocument>`

You could insert that using the following VBA:

Sub insertParagraphNumberRange()

' For debug, so you can do, e.g. Debug.Print sa and see a neat layout
Const le As String = vbCrLf

' optionally you can omit the CrLfs by using this line instead:
'Const le As String = ""

Dim sa As String
Dim sz As String
Dim sb As String

' construct the beginning of a Word 2003 XML document, stripped down.
' This should not need to change

sa = ""

' these are optional
'sa = sa & "<?xml version='1.0' encoding='UTF-8' standalone='yes'?>" & le
'sa = sa & "<?mso-application progid='Word.Document'?>" & le

' the xml:space='preserve' may not be needed. Leave for now
sa = sa & "<w:wordDocument xmlns:w='http://schemas.microsoft.com/office/word/2003/wordml' xml:space='preserve'>" & le
sa = sa & "  <w:body><w:p>" & le

' construct the end of the document. This should not need to change

sz = ""
sz = sz & "  </w:p></w:body>" & le
sz = sz & "</w:wordDocument>" & le

' Now the bit with the fields we want.

' We need compound field coding for any field that has other fields
' nested inside it, but otherwise we can use fldSimple.

' We can omit all "result text" elements. However, that means that
' you will need to execute (i.e. .Update) the fields to generate
' the results. This is not actually obvious if you are inserting
' the fields in a header/footer, because Word will typically update
' headers/footers "in the background"

sb = ""
sb = sb & "    <w:fldChar w:fldCharType='begin'/>" & le
sb = sb & "       <w:r><w:instrText> IF </w:instrText></w:r>" & le

' the IF condition
sb = sb & "        <w:fldSimple w:instr=' STYLEREF 1 \w '/><w:r><w:instrText> = </w:instrText></w:r>" & _
          "<w:fldSimple w:instr=' STYLEREF 1 \l \w '/>" & le

sb = sb & "      <w:r><w:instrText> </w:instrText></w:r>" & le

' The IF TRUE result
sb = sb & "        <w:fldSimple w:instr=' STYLEREF 1 \w '/>" & le

' The beginning of the IF FALSE result. NB, using an actual double
' quotation mark seems to work OK (you need to double it up as usual
' inside a VBA string) but &quot; feels a little safer.
' That said, strings that have the form of a character entity name
' actually cause problems.

sb = sb & "      <w:r><w:instrText> &quot;</w:instrText></w:r>" & le

' The main IF FALSE code
sb = sb & "        <w:fldSimple w:instr=' STYLEREF 1 \w '/><w:r><w:instrText>-</w:instrText></w:r>" & _
          "<w:fldSimple w:instr=' STYLEREF 1 \l \w '/>" & le

sb = sb & "      <w:r><w:instrText>&quot; </w:instrText></w:r>" & le
sb = sb & "    <w:fldChar w:fldCharType='end'/>" & le

'Debug.Print sa & sb & sz

' To test, assume the XML should be inserted at the selection
' In reality, you may want to pass a range object as a parameter
' and insert into that range.

Selection.InsertXML sa & sb & sz
Selection.Fields.Update
End Sub

That works here on up-to-date Windows and Mac versions of Word.

Some notes:-

If you need to use a style name such as "Heading 1" you'll probably need to replace

STYLEREF 1 with either

STYLEREF &quot;Heading 1&quot; or

STYLEREF ""Heading 1"" throughout the code.

It might in principle be better to use the modern XML representation ("Flat OPC"), but the older 2003 version is a bit simpler and functions well enough in this scenario.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks everyone for your helpful comments. I found the one from @macropod easiest to use.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.