2009-12-21 4 views

ответ

24

Списки в OpenXML немного запутываются.

Существует NumberingDefinitionsPart, который описывает все списки в документе. Он содержит информацию о том, как должны отображаться списки (маркированные, пронумерованные и т. Д.), А также присваивать и идентификатор каждому из них.

Затем в MainDocumentPart для каждого элемента в списке, который вы хотите создать, вы добавляете новый абзац и присваиваете идентификатор списка, который вы хотите, к этому абзацу.

Таким образом, чтобы создать маркированный список, таких как:

  • Здравствуйте,
  • мира!

Вы должны сначала создать NumberingDefinitionsPart:

NumberingDefinitionsPart numberingPart = 
    mainDocumentPart.AddNewPart<NumberingDefinitionsPart>("someUniqueIdHere"); 

Numbering element = 
    new Numbering(
    new AbstractNum(
     new Level(
     new NumberingFormat() {Val = NumberFormatValues.Bullet}, 
     new LevelText() {Val = "·"} 
    ) {LevelIndex = 0} 
    ){AbstractNumberId = 1}, 
    new NumberingInstance(
     new AbstractNumId(){Val = 1} 
    ){NumberID = 1}); 

element.Save(numberingPart); 

Затем создать MainDocumentPart, как обычно, за исключением свойств абзаца, назначить нумерацию ID:

MainDocumentPart mainDocumentPart = 
    package.AddMainDocumentPart(); 

Document element = 
    new Document(
    new Body(
     new Paragraph(
     new ParagraphProperties(
      new NumberingProperties(
      new NumberingLevelReference(){ Val = 0 }, 
      new NumberingId(){ Val = 1 })), 
     new Run(
      new RunProperties(), 
      new Text("Hello, "){ Space = "preserve" })), 
     new Paragraph(
     new ParagraphProperties(
      new NumberingProperties(
      new NumberingLevelReference(){ Val = 0 }, 
      new NumberingId(){ Val = 1 })), 
     new Run(
      new RunProperties(), 
      new Text("world!"){ Space = "preserve" })))); 

element.Save(mainDocumentPart); 

Существует более подробное объяснение вариантов, доступных в разделе OpenXML reference guide в разделе 2.9.

3

ответ Адам выше правильно, кроме него новый NumberingInstance (вместо нового Num (как отмечено в комментарии.

Кроме того, если у вас есть несколько списков, вы должны иметь несколько элементов нумерации (каждый со своим собственным идентификатором например, 1, 2, 3 и т. д. - по одному для каждого списка в документе. Это, похоже, не проблема с списками пулей, но пронумерованные списки будут продолжать использовать одну и ту же последовательность нумерации (в отличие от повторного запуска в 1) . потому что он будет думать, что это тот же самый список NumberingId должен ссылаться в своем пункте, как это:

ParagraphProperties paragraphProperties1 = new ParagraphProperties(); 
ParagraphStyleId paragraphStyleId1 = new ParagraphStyleId() { Val = "ListParagraph" }; 
NumberingProperties numberingProperties1 = new NumberingProperties(); 
NumberingLevelReference numberingLevelReference1 = new NumberingLevelReference() { Val = 0 }; 

NumberingId numberingId1 = new NumberingId(){ Val = 1 }; //Val is 1, 2, 3 etc based on your numberingid in your numbering element 
numberingProperties1.Append(numberingLevelReference1); 
numberingProperties1.Append(numberingId1); 
paragraphProperties1.Append(paragraphStyleId1); 
paragraphProperties1.Append(numberingProperties1); 

Детский элемент уровня будет влиять на тип пули и отступ. Моих пуль были слишком малы, пока я не добавил это к элементу уровня:

new NumberingSymbolRunProperties(
    new RunFonts() { Hint = FontTypeHintValues.Default, Ascii = "Symbol", HighAnsi = "Symbol" }) 

вдавливание было проблемой, пока я не добавил этот элемент в элемент уровня, а также:

new PreviousParagraphProperties(
    new Indentation() { Left = "864", Hanging = "360" }) 
3

И если вы похожи я - создание документа из шаблона, то вы можете использовать этот код, чтобы обрабатывать обе ситуации - когда ваш шаблон, или не содержит каких-либо определений нумерации:

// Introduce bulleted numbering in case it will be needed at some point 
NumberingDefinitionsPart numberingPart = document.MainDocumentPart.NumberingDefinitionsPart; 
if (numberingPart == null) 
{ 
    numberingPart = document.MainDocumentPart.AddNewPart<NumberingDefinitionsPart>("NumberingDefinitionsPart001"); 
} 
7

Я хотел кое-что, что позволило бы мне добавить в документ более одного списка маркеров. Некоторое время ударяя головой о стол, мне удалось собрать кучу разных сообщений и изучить мой документ с помощью Open XML SDK 2.0 Productity Tool и вычислил некоторые вещи. Документ, который он производит, теперь проходит проверку по версии 2.0 и 2.5 инструментария производительности SDK.

Вот код; надеюсь, это сэкономит кому-то время и обострение.

Использование:

const string fileToCreate = "C:\\temp\\bulletTest.docx"; 

if (File.Exists(fileToCreate)) 
    File.Delete(fileToCreate); 

var writer = new SimpleDocumentWriter(); 
List<string> fruitList = new List<string>() { "Apple", "Banana", "Carrot"}; 
writer.AddBulletList(fruitList); 
writer.AddParagraph("This is a spacing paragraph 1."); 

List<string> animalList = new List<string>() { "Dog", "Cat", "Bear" }; 
writer.AddBulletList(animalList); 
writer.AddParagraph("This is a spacing paragraph 2."); 

List<string> stuffList = new List<string>() { "Ball", "Wallet", "Phone" }; 
writer.AddBulletList(stuffList); 
writer.AddParagraph("Done."); 

writer.SaveToFile(fileToCreate); 

Использование заявления:

using System; 
using System.Collections.Generic; 
using System.IO; 
using System.Linq; 
using DocumentFormat.OpenXml; 
using DocumentFormat.OpenXml.Packaging; 
using DocumentFormat.OpenXml.Wordprocessing;  

Код

public class SimpleDocumentWriter : IDisposable 
{ 
    private MemoryStream _ms; 
    private WordprocessingDocument _wordprocessingDocument; 

    public SimpleDocumentWriter() 
    { 
     _ms = new MemoryStream(); 
     _wordprocessingDocument = WordprocessingDocument.Create(_ms, WordprocessingDocumentType.Document); 
     var mainDocumentPart = _wordprocessingDocument.AddMainDocumentPart(); 
     Body body = new Body(); 
     mainDocumentPart.Document = new Document(body); 
    } 

    public void AddParagraph(string sentence) 
    { 
     List<Run> runList = ListOfStringToRunList(new List<string> { sentence}); 
     AddParagraph(runList); 
    } 
    public void AddParagraph(List<string> sentences) 
    { 
     List<Run> runList = ListOfStringToRunList(sentences); 
     AddParagraph(runList); 
    } 

    public void AddParagraph(List<Run> runList) 
    { 
     var para = new Paragraph(); 
     foreach (Run runItem in runList) 
     { 
      para.AppendChild(runItem); 
     } 

     Body body = _wordprocessingDocument.MainDocumentPart.Document.Body; 
     body.AppendChild(para); 
    } 

    public void AddBulletList(List<string> sentences) 
    { 
     var runList = ListOfStringToRunList(sentences); 

     AddBulletList(runList); 
    } 


    public void AddBulletList(List<Run> runList) 
    { 
     // Introduce bulleted numbering in case it will be needed at some point 
     NumberingDefinitionsPart numberingPart = _wordprocessingDocument.MainDocumentPart.NumberingDefinitionsPart; 
     if (numberingPart == null) 
     { 
      numberingPart = _wordprocessingDocument.MainDocumentPart.AddNewPart<NumberingDefinitionsPart>("NumberingDefinitionsPart001"); 
      Numbering element = new Numbering(); 
      element.Save(numberingPart); 
     } 

     // Insert an AbstractNum into the numbering part numbering list. The order seems to matter or it will not pass the 
     // Open XML SDK Productity Tools validation test. AbstractNum comes first and then NumberingInstance and we want to 
     // insert this AFTER the last AbstractNum and BEFORE the first NumberingInstance or we will get a validation error. 
     var abstractNumberId = numberingPart.Numbering.Elements<AbstractNum>().Count() + 1; 
     var abstractLevel = new Level(new NumberingFormat() {Val = NumberFormatValues.Bullet}, new LevelText() {Val = "·"}) {LevelIndex = 0}; 
     var abstractNum1 = new AbstractNum(abstractLevel) {AbstractNumberId = abstractNumberId}; 

     if (abstractNumberId == 1) 
     { 
      numberingPart.Numbering.Append(abstractNum1); 
     } 
     else 
     { 
      AbstractNum lastAbstractNum = numberingPart.Numbering.Elements<AbstractNum>().Last(); 
      numberingPart.Numbering.InsertAfter(abstractNum1, lastAbstractNum); 
     } 

     // Insert an NumberingInstance into the numbering part numbering list. The order seems to matter or it will not pass the 
     // Open XML SDK Productity Tools validation test. AbstractNum comes first and then NumberingInstance and we want to 
     // insert this AFTER the last NumberingInstance and AFTER all the AbstractNum entries or we will get a validation error. 
     var numberId = numberingPart.Numbering.Elements<NumberingInstance>().Count() + 1; 
     NumberingInstance numberingInstance1 = new NumberingInstance() {NumberID = numberId}; 
     AbstractNumId abstractNumId1 = new AbstractNumId() {Val = abstractNumberId}; 
     numberingInstance1.Append(abstractNumId1); 

     if (numberId == 1) 
     { 
      numberingPart.Numbering.Append(numberingInstance1); 
     } 
     else 
     { 
      var lastNumberingInstance = numberingPart.Numbering.Elements<NumberingInstance>().Last(); 
      numberingPart.Numbering.InsertAfter(numberingInstance1, lastNumberingInstance); 
     } 

     Body body = _wordprocessingDocument.MainDocumentPart.Document.Body; 

     foreach (Run runItem in runList) 
     { 
      // Create items for paragraph properties 
      var numberingProperties = new NumberingProperties(new NumberingLevelReference() {Val = 0}, new NumberingId() {Val = numberId}); 
      var spacingBetweenLines1 = new SpacingBetweenLines() { After = "0" }; // Get rid of space between bullets 
      var indentation = new Indentation() { Left = "720", Hanging = "360" }; // correct indentation 

      ParagraphMarkRunProperties paragraphMarkRunProperties1 = new ParagraphMarkRunProperties(); 
      RunFonts runFonts1 = new RunFonts() { Ascii = "Symbol", HighAnsi = "Symbol" }; 
      paragraphMarkRunProperties1.Append(runFonts1); 

      // create paragraph properties 
      var paragraphProperties = new ParagraphProperties(numberingProperties, spacingBetweenLines1, indentation, paragraphMarkRunProperties1); 

      // Create paragraph 
      var newPara = new Paragraph(paragraphProperties); 

      // Add run to the paragraph 
      newPara.AppendChild(runItem); 

      // Add one bullet item to the body 
      body.AppendChild(newPara); 
     } 
    } 


    public void Dispose() 
    { 
     CloseAndDisposeOfDocument(); 
     if (_ms != null) 
     { 
      _ms.Dispose(); 
      _ms = null; 
     } 
    } 

    public MemoryStream SaveToStream() 
    { 
     _ms.Position = 0; 
     return _ms; 
    } 

    public void SaveToFile(string fileName) 
    { 
     if (_wordprocessingDocument != null) 
     { 
      CloseAndDisposeOfDocument(); 
     } 

     if (_ms == null) 
      throw new ArgumentException("This object has already been disposed of so you cannot save it!"); 

     using (var fs = File.Create(fileName)) 
     { 
      _ms.WriteTo(fs); 
     } 
    } 

    private void CloseAndDisposeOfDocument() 
    { 
     if (_wordprocessingDocument != null) 
     { 
      _wordprocessingDocument.Close(); 
      _wordprocessingDocument.Dispose(); 
      _wordprocessingDocument = null; 
     } 
    } 

    private static List<Run> ListOfStringToRunList(List<string> sentences) 
    { 
     var runList = new List<Run>(); 
     foreach (string item in sentences) 
     { 
      var newRun = new Run(); 
      newRun.AppendChild(new Text(item)); 
      runList.Add(newRun); 
     } 

     return runList; 
    } 
} 
+0

Спасибо большое за показывая мне, как добавить список в документ. Я использовал Numbering.Append (abstactNum, numberingInstance) и не мог понять, почему он не работает – Dan

+0

Из будущего: Спасибо вам за это! Вроде, действительно! :) –

+0

@MariusConjeaud Добро пожаловать. Я создал что-то для написания простых текстовых документов, которые вы можете использовать (см. Https://github.com/madcodemonkey/SimpleDocument.OpenXML) –

Смежные вопросы