none
How to put content controls on word bookmarks ranges in Word? RRS feed

  • Question

  • Hi Everyone,

    Does any one knows, how to put content controls on the bookmark ranges using open xml sdk?

    Thanks,


    Shahab Abbasi

    Wednesday, September 4, 2013 1:53 PM

Answers

  • Hi Shahab,

    Which content control do you want to add to the bookmark range? And what location you would like the control to be add? Front, middle or Last?

    I write a sample to add a Check Box Content Control to the end of the paragraph in which I set a bookmark for your reference. In the sample, I find the paragraph of the bookmarks, and generate a “SdtRun” to add a check box to the paragraph.

    private static void getBookMarkRange()
    
    {
        string fileName = @"C:\Documents\testContentControl.docx";
    
        //add this content control to the body of the word document
    
        using (WordprocessingDocument wDoc = WordprocessingDocument.Open(fileName, true)) //path is where your word 2007 file is
        {
            IDictionary<String, BookmarkStart> bookmarkMap = new Dictionary<String, BookmarkStart>();
     
            foreach (BookmarkStart bookmarkStart in wDoc.MainDocumentPart.RootElement.Descendants<BookmarkStart>())
            {
                bookmarkMap[bookmarkStart.Name] = bookmarkStart;
            }
    
            foreach (BookmarkStart bookmarkStart in bookmarkMap.Values)
            {
                DocumentFormat.OpenXml.Wordprocessing.Run bookmarkText = bookmarkStart.NextSibling<DocumentFormat.OpenXml.Wordprocessing.Run>();
                if (bookmarkText != null && bookmarkStart.Name != "_GoBack")
                {               //bookmarkText.GetFirstChild<DocumentFormat.OpenXml.Wordprocessing.Text>().Text = "blah";
    
                    Paragraph pp = bookmarkStart.Parent as Paragraph;
    
                    SdtRun sdtRun1 = GenerateSdtRun();
                    pp.Append(sdtRun1);
                }
    
            }
            wDoc.MainDocumentPart.Document.Save();
        }
    
    }
    
    // Creates an SdtRun instance and adds its children.
    public static SdtRun GenerateSdtRun()
    {
        SdtRun sdtRun1 = new SdtRun(); 
        SdtProperties sdtProperties1 = new SdtProperties();
        SdtId sdtId1 = new SdtId() { Val = 1233740977 }; 
    
        W14.SdtContentCheckBox sdtContentCheckBox1 = new W14.SdtContentCheckBox();
        W14.Checked checked1 = new W14.Checked() { Val = W14.OnOffValues.One };
        W14.CheckedState checkedState1 = new W14.CheckedState() { Font = "MS Gothic", Val = "2612" };
        W14.UncheckedState uncheckedState1 = new W14.UncheckedState() { Font = "MS Gothic", Val = "2610" };
     
        sdtContentCheckBox1.Append(checked1);
        sdtContentCheckBox1.Append(checkedState1);
        sdtContentCheckBox1.Append(uncheckedState1);
    
        sdtProperties1.Append(sdtId1);
        sdtProperties1.Append(sdtContentCheckBox1);
        SdtEndCharProperties sdtEndCharProperties1 = new SdtEndCharProperties();
    
        SdtContentRun sdtContentRun1 = new SdtContentRun();
    
        DocumentFormat.OpenXml.Wordprocessing.Run run1 = new DocumentFormat.OpenXml.Wordprocessing.Run() { RsidRunAddition = "00D243EB" };
    
        DocumentFormat.OpenXml.Wordprocessing.RunProperties runProperties1 = new DocumentFormat.OpenXml.Wordprocessing.RunProperties();
        RunFonts runFonts1 = new RunFonts() { Hint = FontTypeHintValues.EastAsia, Ascii = "MS Gothic", HighAnsi = "MS Gothic", EastAsia = "MS Gothic" };
    
        runProperties1.Append(runFonts1);
        DocumentFormat.OpenXml.Wordprocessing.Text text1 = new DocumentFormat.OpenXml.Wordprocessing.Text();
        text1.Text = "☒";
    
        run1.Append(runProperties1);
        run1.Append(text1);
    
        sdtContentRun1.Append(run1);
    
        sdtRun1.Append(sdtProperties1);
        sdtRun1.Append(sdtEndCharProperties1);
        sdtRun1.Append(sdtContentRun1);
        return sdtRun1;
    }

    Hope it can help you.

    Sunday, September 8, 2013 8:47 AM
  • Hi Shahab

    This is tricky, partly because bookmarks - unlike content controls - can overlap so many things, including paragraphs and each other.

    My feeling is, to first get the content of each bookmark (the node tree). Check it for things that aren't allowed in a content control. For example, if it starts in the middle of a paragraph, but extends into a following paragraph you're going to need to isolate it into its own paragraph or break it up into two (or more) content controls.

    If it contains bookmarks, make sure those don't end "outside" the bookmark you're currently processing. And so on.

    Insert the content control, copy in the "cleaned" node tree. Then you can delete the original bookmark elements + content.

    This will probably involve a lot of detective work and a lot of trial-and-error until you track down all the things that bookmarks support and content controls do not.

    In your place, I would probably even try doing such substitutions as an end-user, in the Word application. Observe what I can and cannot do, then compare the resulting Open XML to that of the original document. The Open XML Productivity tool will also generate code to produce the one from the other, to give you a starting point.


    Cindy Meister, VSTO/Word MVP, my blog

    Thursday, October 3, 2013 4:13 PM
    Moderator

All replies

  • Hi Shahab,

    Which content control do you want to add to the bookmark range? And what location you would like the control to be add? Front, middle or Last?

    I write a sample to add a Check Box Content Control to the end of the paragraph in which I set a bookmark for your reference. In the sample, I find the paragraph of the bookmarks, and generate a “SdtRun” to add a check box to the paragraph.

    private static void getBookMarkRange()
    
    {
        string fileName = @"C:\Documents\testContentControl.docx";
    
        //add this content control to the body of the word document
    
        using (WordprocessingDocument wDoc = WordprocessingDocument.Open(fileName, true)) //path is where your word 2007 file is
        {
            IDictionary<String, BookmarkStart> bookmarkMap = new Dictionary<String, BookmarkStart>();
     
            foreach (BookmarkStart bookmarkStart in wDoc.MainDocumentPart.RootElement.Descendants<BookmarkStart>())
            {
                bookmarkMap[bookmarkStart.Name] = bookmarkStart;
            }
    
            foreach (BookmarkStart bookmarkStart in bookmarkMap.Values)
            {
                DocumentFormat.OpenXml.Wordprocessing.Run bookmarkText = bookmarkStart.NextSibling<DocumentFormat.OpenXml.Wordprocessing.Run>();
                if (bookmarkText != null && bookmarkStart.Name != "_GoBack")
                {               //bookmarkText.GetFirstChild<DocumentFormat.OpenXml.Wordprocessing.Text>().Text = "blah";
    
                    Paragraph pp = bookmarkStart.Parent as Paragraph;
    
                    SdtRun sdtRun1 = GenerateSdtRun();
                    pp.Append(sdtRun1);
                }
    
            }
            wDoc.MainDocumentPart.Document.Save();
        }
    
    }
    
    // Creates an SdtRun instance and adds its children.
    public static SdtRun GenerateSdtRun()
    {
        SdtRun sdtRun1 = new SdtRun(); 
        SdtProperties sdtProperties1 = new SdtProperties();
        SdtId sdtId1 = new SdtId() { Val = 1233740977 }; 
    
        W14.SdtContentCheckBox sdtContentCheckBox1 = new W14.SdtContentCheckBox();
        W14.Checked checked1 = new W14.Checked() { Val = W14.OnOffValues.One };
        W14.CheckedState checkedState1 = new W14.CheckedState() { Font = "MS Gothic", Val = "2612" };
        W14.UncheckedState uncheckedState1 = new W14.UncheckedState() { Font = "MS Gothic", Val = "2610" };
     
        sdtContentCheckBox1.Append(checked1);
        sdtContentCheckBox1.Append(checkedState1);
        sdtContentCheckBox1.Append(uncheckedState1);
    
        sdtProperties1.Append(sdtId1);
        sdtProperties1.Append(sdtContentCheckBox1);
        SdtEndCharProperties sdtEndCharProperties1 = new SdtEndCharProperties();
    
        SdtContentRun sdtContentRun1 = new SdtContentRun();
    
        DocumentFormat.OpenXml.Wordprocessing.Run run1 = new DocumentFormat.OpenXml.Wordprocessing.Run() { RsidRunAddition = "00D243EB" };
    
        DocumentFormat.OpenXml.Wordprocessing.RunProperties runProperties1 = new DocumentFormat.OpenXml.Wordprocessing.RunProperties();
        RunFonts runFonts1 = new RunFonts() { Hint = FontTypeHintValues.EastAsia, Ascii = "MS Gothic", HighAnsi = "MS Gothic", EastAsia = "MS Gothic" };
    
        runProperties1.Append(runFonts1);
        DocumentFormat.OpenXml.Wordprocessing.Text text1 = new DocumentFormat.OpenXml.Wordprocessing.Text();
        text1.Text = "☒";
    
        run1.Append(runProperties1);
        run1.Append(text1);
    
        sdtContentRun1.Append(run1);
    
        sdtRun1.Append(sdtProperties1);
        sdtRun1.Append(sdtEndCharProperties1);
        sdtRun1.Append(sdtContentRun1);
        return sdtRun1;
    }

    Hope it can help you.

    Sunday, September 8, 2013 8:47 AM
  • Thanks for the reply.

    I have a bookmarked document where all the contents are inside bookmarks. I need Rich Text Content control on the bookmark ranges. Lets say, my input document.xml (using open xml sdk) looks like this with Bookmarks:

    This is my Sample document.xml contents with Bookmarks over there:

    <w:p> <w:pPr/> <w:bookmarkStart w:id="1" w:name="elm_001"/> <w:bookmarkStart w:id="2" w:name="elm_002"/> <w:bookmarkStart w:id="3" w:name="elm_003"/> <w:r> <w:t>This is Sample test 1</w:t> </w:r> <w:bookmarkEnd w:id="3"/> </w:p> <w:p> <w:pPr/> <w:bookmarkStart w:id="4" w:name="elm_004"/> <w:r> <w:t>This is Sample test 2</w:t> </w:r> <w:bookmarkEnd w:id="4"/> <w:bookmarkEnd w:id="2"/> <w:bookmarkEnd w:id="1"/> </w:p>

    // This is the required output after placing content controls over the bookmark ranges: <w:sdt> <w:sdtPr> <w:id w:val="elm_001" /> </w:sdtPr> <w:sdtContent> <w:sdt> <w:sdtPr> <w:id w:val="elm_002" /> </w:sdtPr> <w:sdtContent> <w:p> <w:pPr/> <w:sdt> <w:sdtPr> <w:id w:val="elm_003" /> </w:sdtPr> <w:sdtContent> <w:r> <w:t>This is Sample test 1</w:t> </w:r> </w:sdtContent> </w:sdt> </w:p> <w:p> <w:pPr/> <w:sdt> <w:sdtPr> <w:id w:val="elm_004" /> </w:sdtPr> <w:sdtContent> <w:r> <w:t>This is Sample test 2</w:t> </w:r> </w:sdtContent> </w:sdt> </w:p> </w:sdtContent </w:sdt> </w:sdtContent> </w:sdt>

    I think my above example shows the input and the target to achieve. Let me know, if you have any questions on the approach.

    Thanks,


     


    Shahab Abbasi

    Monday, September 9, 2013 6:56 AM
  • Hi Shahab

    This is tricky, partly because bookmarks - unlike content controls - can overlap so many things, including paragraphs and each other.

    My feeling is, to first get the content of each bookmark (the node tree). Check it for things that aren't allowed in a content control. For example, if it starts in the middle of a paragraph, but extends into a following paragraph you're going to need to isolate it into its own paragraph or break it up into two (or more) content controls.

    If it contains bookmarks, make sure those don't end "outside" the bookmark you're currently processing. And so on.

    Insert the content control, copy in the "cleaned" node tree. Then you can delete the original bookmark elements + content.

    This will probably involve a lot of detective work and a lot of trial-and-error until you track down all the things that bookmarks support and content controls do not.

    In your place, I would probably even try doing such substitutions as an end-user, in the Word application. Observe what I can and cannot do, then compare the resulting Open XML to that of the original document. The Open XML Productivity tool will also generate code to produce the one from the other, to give you a starting point.


    Cindy Meister, VSTO/Word MVP, my blog

    Thursday, October 3, 2013 4:13 PM
    Moderator