问题
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/)
回答1:
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:
8) 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:
MSDN forum thread: Generating Table of Contents and Applying a Custom Style,
MSDN forum thread: How can i Create dir for word document.
UPDATE 2
Based on our comments below I could propose to use this scenario:
- You manually create an empty DOCX file and insert TOC inside it.
- Then you save this file and open it in OpenXML SDK 2.0 Tool, which provides you with the C# code to generate such empty file with TOC placeholder inside.
- Then you programmatically flush all the data you need to this DOCX file and save it.
- In addition you will need provide the mechanism that will auto update TOC once the data are flushed (or once the document is opened). There are a few options to do that - see screen-casts 3-5 fromthe link to Eric White post I provided above. Especially, I think youshould pay your attention to 5th one - "Shows how to use an AutoOpen macro to update the TOC whenever any document that contains a TOC is opened".
All of that look a bit tricky, but I hope that helps.
回答2:
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>
" }
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!
回答3:
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.
回答4:
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);
来源:https://stackoverflow.com/questions/9762684/how-to-generate-table-of-contents-using-openxml-sdk-2-0