Using the SDK I'm building Word documents that contain reports. These documents need to have TOC. Does anybody have a complete solution that I can follow in order to understand how to do this?
(I've read everything on http://openxmldeveloper.org/)
Using the SDK I'm building Word documents that contain reports. These documents need to have TOC. Does anybody have a complete solution that I can follow in order to understand how to do this?
(I've read everything on http://openxmldeveloper.org/)
Have a look at Fourth and Final Screen-Cast in Series on Adding/Updating the TOC in OpenXML WordprocessingML Documents by Eric White.
Hope that helps!
UPDATE:
According FAQ from MSDN Forums I see that this feature is not supported:
- How to generate TOC (table of contents) in Word document?
Open XML SDK 2.0 does not have this feature supported. But you can generate a small TOC through Word app, and reflect the TOC parts with Document Reflector component in Open XML SDK Productivity Tool to see how to generate a TOC programmatically. For more detailed information, please refer to:
UPDATE 2
Based on our comments below I could propose to use this scenario:
All of that look a bit tricky, but I hope that helps.
UPDATE 3
Thanks to @TScott - updated the link to Updating the TOC in a WordprocessingML Document using an AutoOpen Macro
Use auto Table of contents (clickable)
Set Heading
public static Paragraph SetHeading1(this Paragraph p)
{
var pPr = p.Descendants<ParagraphProperties>().First();
pPr.ParagraphStyleId = new ParagraphStyleId() { Val = "Heading1" };
return p;
}
Generate TOC from empty document, you can use this:
private static string GetTOC(string title, int titleFontSize)
{
return $@"<w:sdt>
<w:sdtPr>
<w:id w:val=""-493258456"" />
<w:docPartObj>
<w:docPartGallery w:val=""Table of Contents"" />
<w:docPartUnique />
</w:docPartObj>
</w:sdtPr>
<w:sdtEndPr>
<w:rPr>
<w:rFonts w:asciiTheme=""minorHAnsi"" w:eastAsiaTheme=""minorHAnsi"" w:hAnsiTheme=""minorHAnsi"" w:cstheme=""minorBidi"" />
<w:b />
<w:bCs />
<w:noProof />
<w:color w:val=""auto"" />
<w:sz w:val=""22"" />
<w:szCs w:val=""22"" />
</w:rPr>
</w:sdtEndPr>
<w:sdtContent>
<w:p w:rsidR=""00095C65"" w:rsidRDefault=""00095C65"">
<w:pPr>
<w:pStyle w:val=""TOCHeading"" />
<w:jc w:val=""center"" />
</w:pPr>
<w:r>
<w:rPr>
<w:b />
<w:color w:val=""2E74B5"" w:themeColor=""accent1"" w:themeShade=""BF"" />
<w:sz w:val=""{titleFontSize * 2}"" />
<w:szCs w:val=""{titleFontSize * 2}"" />
</w:rPr>
<w:t>{title}</w:t>
</w:r>
</w:p>
<w:p w:rsidR=""00095C65"" w:rsidRDefault=""00095C65"">
<w:r>
<w:rPr>
<w:b />
<w:bCs />
<w:noProof />
</w:rPr>
<w:fldChar w:fldCharType=""begin"" />
</w:r>
<w:r>
<w:rPr>
<w:b />
<w:bCs />
<w:noProof />
</w:rPr>
<w:instrText xml:space=""preserve""> TOC \o ""1-3"" \h \z \u </w:instrText>
</w:r>
<w:r>
<w:rPr>
<w:b />
<w:bCs />
<w:noProof />
</w:rPr>
<w:fldChar w:fldCharType=""separate"" />
</w:r>
<w:r>
<w:rPr>
<w:noProof />
</w:rPr>
<w:t>No table of contents entries found.</w:t>
</w:r>
<w:r>
<w:rPr>
<w:b />
<w:bCs />
<w:noProof />
</w:rPr>
<w:fldChar w:fldCharType=""end"" />
</w:r>
</w:p>
</w:sdtContent>
</w:sdt>"
}
Create SdtBlock, and set TOC xml
var sdtBlock = new SdtBlock();
sdtBlock.InnerXml = GetTOC(Translations.ResultsBooksTableOfContentsTitle, 16);
document.MainDocumentPart.Document.Body.AppendChild(sdtBlock);
Set UpdateFieldsOnOpen
var settingsPart = document.MainDocumentPart.AddNewPart<DocumentSettingsPart>();
settingsPart.Settings = new Settings { BordersDoNotSurroundFooter = new BordersDoNotSurroundFooter() { Val = true } };
settingsPart.Settings.Append(new UpdateFieldsOnOpen() { Val = true });
if you need generate pdf file from docx, it's working fine!
Thanks to Dmitri Pavlov (@DmitryPavlov) for the help.
I don't want to give an answer to my own question, but this is just to illustrate the steps that I’ve taken.
The advice for anyone interested is to watch the 5-part screen-cast by Eric White - Exploring Tables-of-Contents in Open XML WordprocessingML Documents. This has all the info with respect to adding and updating a TOC (am much more).
My solution was to use a Template (just a regular empty document that had styles for everything I needed: Header 1-5, TOC style, etc.). This is particularly useful as a quick fix for the styles issue (the new document, that has the TOC, will have a new style.xml created; this file has some additional data; as a result the hierarchy in the TOC isn’t as expected – i.e., header 2 is the child of header 1, header 3 is a child of header 2, etc.).
Therefore:
Create a Word document and add all the elements that you expect to be added later programmatically (e.g., Header 1-5, Table of Contents, etc.). Delete all the contents and save the document (the reason for this is to create styles for all the necessary elements).
I personally added the template (the file created at step #1) as a resource in my project.
In your code, create a new copy of the template (this will be the actual file that you will work on). I used:
byte[] stream = Properties.Resources.Template;
File.WriteAllBytes(@"D:\Template.docx", stream);
File.Copy(@"D:\Template.docx", @"D:\New.docx");
Flush all the data to this document.
Add the source files from screen-cast 2, 3 or 4 to your project (for this please see screen-cast 3) - at the end of those posts you will find a link to download TocAdder.zip. Or just add a reference to TocAdder.dll.
Insert the TOC. Just an example:
using (WordprocessingDocument wdoc = WordprocessingDocument.Open(@"D:\New.docx", true))
{
XElement firstPara = wdoc
.MainDocumentPart
.GetXDocument()
.Descendants(W.p)
.FirstOrDefault();
TocAdder.AddToc(wdoc, firstPara,
@"TOC \o '1-3' \h \z \u", null, null);
}
Replace the styles in the newly created document with the ones from the template. You can use this resource from MSDN: Replacing the Styles Parts in Word 2010 Documents by Using the Open XML SDK 2.0. Again, an example:
string fromDoc = @"D:\Template.docx";
string toDoc = @"D:\New.docx";
var node = WDExtractStyles(fromDoc, false);
if (node != null)
WDReplaceStyles(toDoc, node, false);
node = WDExtractStyles(fromDoc);
if (node != null)
WDReplaceStyles(toDoc, node);
Optionally use one of the methods described in screen-cast 3, 4 or 5 in order to get around the problem with the modal dialog box that Word puts up.
Hope this will be useful for somebody.
If you have a TOC field, this will cause it to be updated when the document is opened in Word (body is a reference to the document body):
DocumentFormat.OpenXml.Wordprocessing.SimpleField f;
f = new SimpleField();
f.Instruction="sdtContent";
f.Dirty = true;
body.Append(f);
© 2022 - 2024 — McMap. All rights reserved.