Create a multi-level list of markers using Word.Interop

I need to create a multi-level bullet list through Microsoft.Office.Interop.Word , and I'm currently struggling with its (terrible) API (again).

I just created the following example (not yet dynamic, for demo purposes only) in a VSTO level project for Microsoft Office Word 2010 in the C # programming language:

 Word.Paragraph paragraph = null; Word.Range range = this.Content; paragraph = range.Paragraphs.Add(); paragraph.Range.Text = "Item 1"; paragraph.Range.ListFormat.ApplyBulletDefault(Word.WdDefaultListBehavior.wdWord10ListBehavior); // ATTENTION: We have to outdent the paragraph AFTER its list format has been set, otherwise this has no effect. // Without this, the the indent of "Item 2" differs from the indent of "Item 1". paragraph.Outdent(); paragraph.Range.InsertParagraphAfter(); paragraph = range.Paragraphs.Add(); paragraph.Range.Text = "Item 1.1"; // ATTENTION: We have to indent the paragraph AFTER its text has been set, otherwise this has no effect. paragraph.Indent(); paragraph.Range.InsertParagraphAfter(); paragraph = range.Paragraphs.Add(); paragraph.Range.Text = "Item 1.2"; paragraph.Range.InsertParagraphAfter(); paragraph = range.Paragraphs.Add(); paragraph.Range.Text = "Item 2"; paragraph.Outdent(); 

The code does exactly what I want (after many attempts and errors!), But it is terrible in my opinion. The format must be applied at a VERY specific point, and I must manually back off and go beyond the paragraphs I created.

So my question is: Is there a better approach for creating a multi-level bullet list through Word.Interop , for example. through shorthand methods that I have not yet discovered?

My goal is to create a multi-level list from XML data (a more specific CustomXMLNode object)

In Qaru, there are two other questions related to bullet lists, but both do not help me (the source code above is one answer to the second question):

EDIT (2013-08-08):

I just hacked something together that outputs two arrays as a bullet list with two levels (an array with sub-items is used for each root element to make it simple). By introducing recursion, one could create a bullet list with infinite levels (theoretically). But the problem remains, the code is a mess ...

 string[] rootItems = new string[] { "Root Item A", "Root Item B", "Root Item C" }; string[] subItems = new string[] { "Subitem A", "Subitem B" }; Word.Paragraph paragraph = null; Word.Range range = this.Content; bool appliedListFormat = false; bool indented = false; for (int i = 0; i < rootItems.Length; ++i) { paragraph = range.Paragraphs.Add(); paragraph.Range.Text = rootItems[i]; if (!appliedListFormat) { paragraph.Range.ListFormat.ApplyBulletDefault(Word.WdDefaultListBehavior.wdWord10ListBehavior); appliedListFormat = true; } paragraph.Outdent(); paragraph.Range.InsertParagraphAfter(); for (int j = 0; j < subItems.Length; ++j) { paragraph = range.Paragraphs.Add(); paragraph.Range.Text = subItems[j]; if (!indented) { paragraph.Indent(); indented = true; } paragraph.Range.InsertParagraphAfter(); } indented = false; } // Delete the last paragraph, since otherwise the list ends with an empty sub-item. paragraph.Range.Delete(); 

EDIT (2013-08-12):

Last Friday, I thought I had achieved what I wanted, but this morning I noticed that my solution only works if the entry point is at the end of the document. I created the following simple example to demonstrate (erroneous) behavior. To conclude my problem: I can create multi-level bullet lists at the end of the document only . As soon as I change the current selection (for example, before the start of the document), the list will be destroyed. As far as I can see, this is due to (automatic or non-automatic) extension of Range objects. I have tried a lot so far (I almost lose it), but for me this is the whole burden of cult. The only thing I want to do is insert one element after another (is it impossible to create a content control inside ) so that the paragraph text is accompanied by a content control?) And to which in any Range a Document . Tonight I will create a Gist on GitHub with my actual CustomXMLPart binding CustomXMLPart . In the end, someone can help me fix this obsessive problem.

 private void buttonTestStatic_Click(object sender, RibbonControlEventArgs e) { Word.Range range = Globals.ThisDocument.Application.Selection.Range; Word.ListGallery listGallery = Globals.ThisDocument.Application.ListGalleries[Word.WdListGalleryType.wdBulletGallery]; Word.Paragraph paragraph = null; Word.ListFormat listFormat = null; // TODO At the end of the document, the ranges are automatically expanded and inbetween not? paragraph = range.Paragraphs.Add(); listFormat = paragraph.Range.ListFormat; paragraph.Range.Text = "Root Item A"; this.ApplyListTemplate(listGallery, listFormat, 1); paragraph.Range.InsertParagraphAfter(); paragraph = paragraph.Range.Paragraphs.Add(); listFormat = paragraph.Range.ListFormat; paragraph.Range.Text = "Child Item A.1"; this.ApplyListTemplate(listGallery, listFormat, 2); paragraph.Range.InsertParagraphAfter(); paragraph = paragraph.Range.Paragraphs.Add(); listFormat = paragraph.Range.ListFormat; paragraph.Range.Text = "Child Item A.2"; this.ApplyListTemplate(listGallery, listFormat, 2); paragraph.Range.InsertParagraphAfter(); paragraph = paragraph.Range.Paragraphs.Add(); listFormat = paragraph.Range.ListFormat; paragraph.Range.Text = "Root Item B"; this.ApplyListTemplate(listGallery, listFormat, 1); paragraph.Range.InsertParagraphAfter(); } private void ApplyListTemplate(Word.ListGallery listGallery, Word.ListFormat listFormat, int level = 1) { listFormat.ApplyListTemplateWithLevel( listGallery.ListTemplates[level], ContinuePreviousList: true, ApplyTo: Word.WdListApplyTo.wdListApplyToSelection, DefaultListBehavior: Word.WdDefaultListBehavior.wdWord10ListBehavior, ApplyLevel: level); } 

EDIT (2013-08-12): I installed the GitHub repository here , which demonstrates my problem with Word.Range objects. The OnClickButton method in the OnClickButton file calls my own mapping class. The comments describe the problem. I know that my problems are related to the reference to the Word.Range object, but all the other solutions that I tried (for example, changing the range inside the class) turned out to be even more complicated. The best solution I've reached so far is to define the Document.Content range as an argument to the MapToCustomControlsIn method. This inserts a well-formatted layered list of markers (with custom XML parts associated with content controls) to the end of the document. I want to insert this list into a user position in the document (for example, the current selection through Word.Selection.Range ).

+5
source share
2 answers

There is almost an example of Florian Walters, but the numbering of the first children is always not correct when I tried.

Someone inspired me by suggesting to use a Macro and VBA script, and then convert to C #.

Below is an example of tested code on my side. Hope it helps.

 using Microsoft.Office.Interop.Word; using System.Reflection; namespace OfficeUtility { public class NumberListGenerate { public void GenerateList() { Application app = null; Document doc = null; string filePath = "c:\\output.docx"; string pdfPath = "c:\\export.pdf"; try { app = new Application(); app.Visible = false; // Open Microsoft Office in background doc = app.Documents.Open(filePath, Missing.Value, false); Range range = doc.Range(); string search = "$list"; // Find in document to generate list while (range.Find.Execute(search)) { ListGallery listGallery = app.ListGalleries[WdListGalleryType.wdNumberGallery]; // Select found location range.Select(); // Apply multi level list app.Selection.Range.ListFormat.ApplyListTemplateWithLevel( listGallery.ListTemplates[1], ContinuePreviousList: false, ApplyTo: WdListApplyTo.wdListApplyToWholeList, DefaultListBehavior: WdDefaultListBehavior.wdWord10ListBehavior); // First level app.Selection.TypeText("Root Item A"); // Set text to key in app.Selection.TypeParagraph(); // Simulate typing in MS Word // Go to 2nd level app.Selection.Range.ListFormat.ListIndent(); app.Selection.TypeText("Child Item A.1"); app.Selection.TypeParagraph(); app.Selection.TypeText("Child Item A.2"); app.Selection.TypeParagraph(); // Back to 1st level app.Selection.Range.ListFormat.ListOutdent(); app.Selection.TypeText("Root Item B"); app.Selection.TypeParagraph(); // Go to 2nd level app.Selection.Range.ListFormat.ListIndent(); app.Selection.TypeText("Child Item B.1"); app.Selection.TypeParagraph(); app.Selection.TypeText("Child Item B.2"); app.Selection.TypeParagraph(); // Delete empty item generated by app.Selection.TypeParagraph(); app.Selection.TypeBackspace(); } // Save document doc.Save(); // Export to pdf doc.ExportAsFixedFormat(pdfPath, WdExportFormat.wdExportFormatPDF); } catch (System.Exception ex) { LogError(ex); } finally { if (doc != null) { // Need to close the document to prevent deadlock doc.Close(false); System.Runtime.InteropServices.Marshal.FinalReleaseComObject(doc); } if (app != null) { app.Quit(); System.Runtime.InteropServices.Marshal.FinalReleaseComObject(app); } } } } } 
0
source

You can try under the code block:

 static void Main(string[] args) { try { Application app = new Application(); Document doc = app.Documents.Add(); Range range = doc.Range(0, 0); range.ListFormat.ApplyNumberDefault(); range.Text = "Birinci"; range.InsertParagraphAfter(); ListTemplate listTemplate = range.ListFormat.ListTemplate; //range.InsertAfter("Birinci"); //range.InsertParagraphAfter(); //range.InsertAfter("İkinci"); //range.InsertParagraphAfter(); //range.InsertAfter("Üçüncü"); //range.InsertParagraphAfter(); Range subRange = doc.Range(range.StoryLength - 1); subRange.ListFormat.ApplyBulletDefault(); subRange.ListFormat.ListIndent(); subRange.Text = "Alt Birinci"; subRange.InsertParagraphAfter(); ListTemplate sublistTemplate = subRange.ListFormat.ListTemplate; Range subRange2 = doc.Range(subRange.StoryLength - 1); subRange2.ListFormat.ApplyListTemplate(sublistTemplate); subRange2.ListFormat.ListIndent(); subRange2.Text = "Alt İkinci"; subRange2.InsertParagraphAfter(); Range range2 = doc.Range(range.StoryLength - 1); range2.ListFormat.ApplyListTemplateWithLevel(listTemplate,true); WdContinue isContinue = range2.ListFormat.CanContinuePreviousList(listTemplate); range2.Text = "İkinci"; range2.InsertParagraphAfter(); Range range3 = doc.Range(range2.StoryLength - 1); range3.ListFormat.ApplyListTemplate(listTemplate); range3.Text = "Üçüncü"; range3.InsertParagraphAfter(); string path = Environment.CurrentDirectory; int totalExistDocx = Directory.GetFiles(path, "test*.docx").Count(); path = Path.Combine(path, string.Format("test{0}.docx", totalExistDocx + 1)); app.ActiveDocument.SaveAs2(path, WdSaveFormat.wdFormatXMLDocument); doc.Close(); Process.Start(path); } catch (Exception exception) { throw; } } 

Note: If you do not know the length of the input, you should not determine the value of the end of the range, for example:

 static void Main(string[] args) { try { Application app = new Application(); Document doc = app.Documents.Add(); Range range = doc.Range(0, 0); range.ListFormat.ApplyNumberDefault(); range.Text = "Birinci"; range.InsertParagraphAfter(); ListTemplate listTemplate = range.ListFormat.ListTemplate; //range.InsertAfter("Birinci"); //range.InsertParagraphAfter(); //range.InsertAfter("İkinci"); //range.InsertParagraphAfter(); //range.InsertAfter("Üçüncü"); //range.InsertParagraphAfter(); Range subRange = doc.Range(range.StoryLength - 1, range.StoryLength - 1); subRange.ListFormat.ApplyBulletDefault(); subRange.ListFormat.ListIndent(); subRange.Text = "Alt Birinci"; subRange.InsertParagraphAfter(); ListTemplate sublistTemplate = subRange.ListFormat.ListTemplate; Range subRange2 = doc.Range(subRange.StoryLength - 1, range.StoryLength - 1); subRange2.ListFormat.ApplyListTemplate(sublistTemplate); subRange2.ListFormat.ListIndent(); subRange2.Text = "Alt İkinci"; subRange2.InsertParagraphAfter(); Range range2 = doc.Range(range.StoryLength - 1, range.StoryLength - 1); range2.ListFormat.ApplyListTemplateWithLevel(listTemplate,true); WdContinue isContinue = range2.ListFormat.CanContinuePreviousList(listTemplate); range2.Text = "İkinci"; range2.InsertParagraphAfter(); Range range3 = doc.Range(range2.StoryLength - 1, range.StoryLength - 1); range3.ListFormat.ApplyListTemplate(listTemplate); range3.Text = "Üçüncü"; range3.InsertParagraphAfter(); string path = Environment.CurrentDirectory; int totalExistDocx = Directory.GetFiles(path, "test*.docx").Count(); path = Path.Combine(path, string.Format("test{0}.docx", totalExistDocx + 1)); app.ActiveDocument.SaveAs2(path, WdSaveFormat.wdFormatXMLDocument); doc.Close(); Process.Start(path); } catch (Exception exception) { throw; } } 
-one
source

Source: https://habr.com/ru/post/1495942/


All Articles