ms-worddocx

How to use XML Parts in a hyperlink?


I need to create a URL/Hyperlink in a Word doc being used as a template, using an XML Content Control. All of this is being created in a Word DOCX file, using Word 365 desktop app. The template is being used by another program, and other than the hyperlink, XML Content Controls work as expected.

The URL seems static with the name of the XML Element. The "Text to display" even updates, but not the URL itself, it's always the element name. This seems similar to using mailmerge in URL's, but the solutions I've found don't apply as XML field dont use the << >> functions.

I don'd believe I can use VBA to build the hyperlink after the fact, I need it done in standard Word.

Example: XML Content Control called LINKPART. It’s value would be EXAMPLE. If I type test.com/ and insert LINKPART , the result will be test.com/EXAMPLE, in plaintext (no hyperlink)

If I use test.com/, insert LINKPARK, and hyperlink test.com/LINKPART, result is a hyperlinked test.com/LINKPART. Not usuable and the common problem.

Also tried using { HYPERLINK "test.com/LINKPART" "Text to display"} and variations, but at best I get the plaintext again.

Edit: corrections


Solution

  • It seems to be possible to do this using a HYPERLINK field and techniques described elsewhere - it sounds as if you have found some and tried them.

    It has been asserted that you can't use content controls nested in fields in any circumstances but I do not believe that is correct. The one thing you always have to do with most field code types including HYPERLINK is that you have to select them and Update them (e.g. F9) before they will reflect any changes you have made in the field code. In other words, even though Word will propagate any change to the value of LINKPART automatically, the HYERLINK doesn't change until you update its field code.

    In this case, what you need to do is:

    (i.e. the displaytext is just inserted in the field result and there is no field instruction such as *dt that lets you enter a different text and display that by updating the field).

    with the word EXAMPLE in a content control.

    In other words, that should deal with the actual link. But if you save, close, and re-open the document, you may find that the content control has been replaced by its result and you are now stuck with http://test.com/SECOND/

    Bizarrely, what actually happens depends on where the insertion point was when you saved/closed the document. If it was in the HYPERLINK field (which is quite likely in cases where you only have one such link in the document), the content control should be left and your link should work as you hope. If it was outside the HYPERLINK field, the content control is replaced by its result. This is actually because Word inserts a GoBack bookmark at the latest position of the Insertion point. And luckily, if you insert your own bookmark in the HYPERLINK field, it has the same effect.

    (where {} are the proper field code braces that you can insert using ctrl-F9 on Windows Word). Make sure you update the SET field or the HYPERLINK field before closing the document or the bookmark "fix1" will not be created.

    (with proper {} field code braces for the SEQ). Again, make sure you update the SET field before saving/closing.

    If you also want the display text to reflect the change in the XML element, then you have to edit the HYPERLINK field result very carefully using whatever plain text and content control you need. It's very easy to get this wrong and delete the text. I can't go into all of that right now but will do so later.

    Finally, the content control value must contain the link text exactly as it needs to be (e.g., if the value contained a space, the URL would need an escaped version of that space with ^32 .

    Here's some crude sample VBA that would create a HYPERLINK field with the example URLs etc. given in the Question. You only need the VBA to insert the hyperlink. The resulting document does not need it. But the user must update the field when they change the value of the XML Element, or the URL that Word will actually open will not change.

    Option Explicit
    Sub Macro1()
    Const HyperlinkFieldCodeBookmark As String = "HyperlinkFieldCode"
    Const SetFieldCodeBookmark = "SetFieldCode"
    Const LinkAddressBookmark As String = "LinkAddress"
    Const HyperlinkFieldPre As String = "HYPERLINK """
    
    ' put the values you need in here
    Const AddressTextPre As String = "https://test.com/"
    Const AddressTextPost As String = "/"""
    Const CCTitle As String = "LinkPart"
    Const CCTag As String = "LinkPartTag"
    ' You should be able to get these for your Custom XML Part Element using VBA, e.g.
    ' by inserting a plain text control mapped to the Element, then looking at the CC's
    ' .XMLMapping.XPath and .XMLMapping.PrefixMapping
    ' using the standard values for the built-in Document Property "keywords"
    Const ccXPath As String = "/ns1:coreProperties[1]/ns1:keywords[1]"
    Const ccPrefixMapping As String _
      = "xmlns:ns0='http://purl.org/dc/elements/1.1/' xmlns:ns1='http://schemas.openxmlformats.org/package/2006/metadata/core-properties'"
    Const SetFieldCodePre As String = "SET ""fix"
    Const SetFieldCodePost As String = """ 1 "
    Const SeqFieldCode As String = "SEQ fix"
    
    ' First we build the CODE of the HYPERLINK fielda t the selelction point, and bookmark the pieces we
    ' need to copy, e.g. into the RESULT of the HYPERLINK field.
    ' we leave "@" markers in places where we need to insert something such as a COntent Control or a field,
    ' but we need to be able to work out exactly where these are.
    
    Dim CCStart As Long
    Dim AddressStart As Long
    Dim SetFieldStart As Long
    Dim SeqFieldStart As Long
    Dim f As Word.Field
    Dim r As Word.Range
    Set r = Selection.Range
    r.Collapse direction:=wdCollapseStart
    r.Text = HyperlinkFieldPre & AddressTextPre & "@" & AddressTextPost & " " & SetFieldCodePre & "@" & SetFieldCodePost
    ' mark that as the thing we will eventually turn into a HYPERLINK field. We can't just use a range variable
    ' because we're going to insert more stuff.
    
    r.Bookmarks.Add HyperlinkFieldCodeBookmark
    
    ' Mark the part that will be the Link Address
    
    AddressStart = r.Start + Len(HyperlinkFieldPre)
    CCStart = AddressStart + Len(AddressTextPre)
    ActiveDocument.Range(AddressStart, CCStart + Len(AddressTextPost)).Bookmarks.Add LinkAddressBookmark
    
    ' similarly, mark the point where we will insert the SEQ field
    
    SeqFieldStart = r.End - Len(SetFieldCodePost) - 1
    
    ' and the range of the SET field
    ActiveDocument.Range(SeqFieldStart - Len(SetFieldCodePre), r.End).Bookmarks.Add SetFieldCodeBookmark
    
    ' Insert stuff starting at the end and working towards the beginning
    
    ' SEQ field
    
    r.Start = SeqFieldStart
    r.End = r.Start + 1
    With r.Fields.Add(Range:=r, Type:=wdFieldEmpty, Text:=SeqFieldCode, preserveformatting:=False)
      .Update
    End With
    
    ' Insert the SET
    
    With ActiveDocument.Bookmarks(SetFieldCodeBookmark)
      With .Range.Fields.Add(Range:=.Range, Type:=wdFieldEmpty, preserveformatting:=False)
        .Update
      End With
    End With
    
    ' Insert the Content Control
    
    r.Start = CCStart
    r.End = r.Start + 1
    With r.ContentControls.Add(wdContentControlText, r)
      .Title = CCTitle
      .Tag = CCTag
      .XMLMapping.SetMapping ccXPath, ccPrefixMapping
    End With
    
    ' Turn that into a HYPERLINK field
    
    With ActiveDocument.Bookmarks(HyperlinkFieldCodeBookmark)
      With .Range.Fields.Add(Range:=.Range, Type:=wdFieldEmpty, preserveformatting:=False)
        .Update
        .Result.FormattedText = ActiveDocument.Bookmarks(LinkAddressBookmark).Range.FormattedText
      End With
    End With
    
    ' Tidy up
    
    With ActiveDocument
      .Bookmarks(HyperlinkFieldCodeBookmark).Delete
      .Bookmarks(SetFieldCodeBookmark).Delete
      .Bookmarks(LinkAddressBookmark).Delete
    End With
      
    Set r = Nothing
    End Sub