Pregunta

Utilizando el SDK OpenXML, 2,0 CTP, que estoy tratando de crear mediante programación un documento de Word. En mi documento tengo que insertar una lista con viñetas, una parte de los elementos de la lista debe ser subrayado. ¿Cómo puedo hacer esto?

¿Fue útil?

Solución

Listas en OpenXML son un poco confuso.

No es un NumberingDefinitionsPart que describe todas las listas en el documento. Contiene información sobre cómo deben aparecer las listas (viñetas, numeradas, etc.) y también asigna y ID a cada uno.

A continuación, en el MainDocumentPart , para cada elemento de la lista que desea crear, se añade un nuevo párrafo y asignar el ID de la lista que desea que el párrafo.

Así que para crear una lista de viñetas como por ejemplo:

  • Hola,
  • mundo!

primero tendría que crear un 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);

A continuación, se crea el MainDocumentPart como lo haría normalmente, excepto en las propiedades de párrafo, asignar la identificación de numeración:

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);

Hay una mejor explicación de las opciones disponibles en el OpenXML guía de referencia en la Sección 2.9.

Otros consejos

Yo quería algo que me permitiría añadir más de una lista de bala en un documento. Después de golpearse la cabeza contra mi escritorio por un rato, pude combinar un montón de diferentes mensajes y examinar mi documento con el Open XML SDK 2.0 Productity herramienta y pensamos que algunas cosas fuera. El documento que produce ahora pasa la validación por versión 2,0 y 2,5 de la herramienta SDK Productividad.

Este es el código; es de esperar que alguien ahorra algo de tiempo y molestias.

Uso:

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);

Uso de declaraciones:

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

Código

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;
    }
}

La respuesta de Adán anterior es correcto, excepto que es nueva NumberingInstance (en lugar de nuevo Num (como se señala en un comentario.

Además, si tiene varias listas, debe tener varios elementos de Codificación (, 3, etc cada uno con su propia identificación, por ejemplo 1, 2 -. Uno para cada lista en el documento Esto no parece ser un problema listas de bala, pero las listas numeradas seguirán usando la misma secuencia de numeración (en lugar de empezar de nuevo en 1) porque va a pensar que es la misma lista el NumberingId tiene que ser referenciado en el párrafo de la siguiente manera:.

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);

Los niños del elemento Nivel tendrán un efecto sobre el tipo de bala, y la sangría. Mis balas eran demasiado pequeños hasta que he añadido esto al elemento Nivel:

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

La sangría era un problema hasta que he añadido este elemento al elemento de nivel, así:

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

Y si usted es como yo - la creación de un documento de una plantilla, entonces es posible que desee utilizar este código, para manejar ambas situaciones - cuando su plantilla hace o no contiene ninguna definición de numeración:

// 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");
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top