views:

313

answers:

3

I assume v2.0 is better... they have some nice "how to:..." examples but bookmarks don't seem to act as obviously as say a Table... a bookmark is defined by two XML elements BookmarkStart & BookmarkEnd. We have some templates with text in as bookmarks and we simply want to replace bookmarks with some other text... no weird formatting is going on but how do I select/replace bookmark text?

+1  A: 

Here is how I do it in VB.NET:

For Each curBookMark In contractBookMarkStarts

      ''# Get the "Run" immediately following the bookmark and then
      ''# get the Run's "Text" field
      runAfterBookmark = curBookMark.NextSibling(Of Wordprocessing.Run)()
      textInRun = runAfterBookmark.LastChild

      ''# Decode the bookmark to a contract attribute
      lines = DecodeContractDataToContractDocFields(curBookMark.Name, curContract).Split(vbCrLf)

      ''# If there are multiple lines returned then some work needs to be done to create
      ''# the necessary Run/Text fields to hold lines 2 thru n.  If just one line then set the
      ''# Text field to the attribute from the contract
      For ptr = 0 To lines.Count - 1
          line = lines(ptr)
          If ptr = 0 Then
              textInRun.Text = line.Trim()
          Else
              ''# Add a <br> run/text component then add next line
              newRunForLf = New Run(runAfterBookmark.OuterXml)
              newRunForLf.LastChild.Remove()
              newBreak = New Break()
              newRunForLf.Append(newBreak)

              newRunForText = New Run(runAfterBookmark.OuterXml)
              DirectCast(newRunForText.LastChild, Text).Text = line.Trim

              curBookMark.Parent.Append(newRunForLf)
              curBookMark.Parent.Append(newRunForText)
          End If
      Next
Next
Stephen Study
+1  A: 

I just figured this out 10 minutes ago so forgive the hackish nature of the code.

First I wrote a helper recursive helper function to find all the bookmarks:

    private static Dictionary<string, BookmarkEnd> FindBookmarks(OpenXmlElement documentPart, Dictionary<string, BookmarkEnd> results = null, Dictionary<string, string> unmatched = null )
    {
        results = results ?? new Dictionary<string, BookmarkEnd>();
        unmatched = unmatched ?? new Dictionary<string,string>();

        foreach (var child in documentPart.Elements())
        {
            if (child is BookmarkStart)
            {
                var bStart = child as BookmarkStart;
                unmatched.Add(bStart.Id, bStart.Name);
            }

            if (child is BookmarkEnd)
            {
                var bEnd = child as BookmarkEnd;
                foreach (var orphanName in unmatched)
                {
                    if (bEnd.Id == orphanName.Key)
                        results.Add(orphanName.Value, bEnd);
                }
            }

            FindBookmarks(child, results, unmatched);
        }

        return results;
    }

That returns me a Dictionary that I can use to part through my replacement list and add the text after the bookmark:

       var bookMarks = FindBookmarks(doc.MainDocumentPart.Document);

        foreach( var end in bookMarks )
        {
            var textElement = new Text("asdfasdf");
            var runElement = new Run(textElement);

            end.Value.InsertAfterSelf(runElement);
        }

From what I can tell inserting into and replacing the bookmarks looks harder. When I used InsertAt instead of InsertIntoSelf I got: "Non-composite elements do not have child elements." YMMV

jfar
I suppose what I want to do is use start/end bookmark tags to let me select a portion of text (a run?) and modify it. It seems pretty random where the bookmarks are stored though, mine are all in `doc.MainDocumentPart.Document.Body.Descendants`
John
@John They are inside the tree at the place in the document they were added. Nothing random about it at all. Everything is going to be in Body.Descendants. Body.Elements only gets first level children. Wait, maybe I should just be searching Descendants...
jfar
+2  A: 

Here's my approach after using you guys as inspiration:

  IDictionary<String, BookmarkStart> bookmarkMap = 
      new Dictionary<String, BookmarkStart>();

  foreach (BookmarkStart bookmarkStart in file.MainDocumentPart.RootElement.Descendants<BookmarkStart>())
  {
      bookmarkMap[bookmarkStart.Name] = bookmarkStart;
  }

  foreach (BookmarkStart bookmarkStart in bookmarkMap.Values)
  {
      Run bookmarkText = bookmarkStart.NextSibling<Run>();
      if (bookmarkText != null)
      {
          bookmarkText.GetFirstChild<Text>().Text = "blah";
      }
  }
John