Compartir a través de


Insertar un comentario en un documento de procesamiento de texto

En este tema se muestra cómo usar las clases del SDK de Open XML para Office para agregar mediante programación un comentario al primer párrafo de un documento de procesamiento de texto.


Abrir el documento existente para su edición

Para abrir un documento existente, cree una instancia de la WordprocessingDocument clase como se muestra en la instrucción siguiente using . En la misma instrucción, abra el archivo de procesamiento de texto en la ruta de archivo especificada mediante el Open(String, Boolean) método , con el parámetro booleano establecido true en para habilitar la edición en el documento.

using (WordprocessingDocument document = WordprocessingDocument.Open(fileName, true))

Con v3.0.0+ el Close() método se ha quitado en favor de confiar en la instrucción using. Garantiza que se llama automáticamente al Dispose() método cuando se alcanza la llave de cierre. El bloque que sigue a la instrucción using establece un ámbito para el objeto que se crea o se nombra en la instrucción using. Dado que la WordprocessingDocument clase del SDK de Open XML guarda y cierra automáticamente el objeto como parte de su IDisposable implementación y, dado Dispose() que se llama automáticamente al salir del bloque, no es necesario llamar Save() explícitamente o siempre que Dispose() use una using instrucción.


Funcionamiento del código de ejemplo

Una vez abierto el documento, puede buscar el primer párrafo para adjuntar un comentario. El código busca el primer párrafo llamando al First método de extensión en todos los elementos descendientes del elemento de documento que son de tipo Paragraph. El First método es miembro de la Enumerable clase . La System.Linq.Enumerable clase proporciona métodos de extensión para los objetos que implementan la IEnumerable<T> interfaz.

Paragraph firstParagraph = document.MainDocumentPart.Document.Descendants<Paragraph>().First();
wordprocessingCommentsPart.Comments ??= new Comments();
string id = "0";

El código determina primero si existe una WordprocessingCommentsPart parte. Para ello, llame al MainDocumentPart método genérico , GetPartsCountOfTypey especifique un tipo de WordprocessingCommentsPart.

Si existe un WordprocessingCommentsPart elemento, el código obtiene un nuevo Id valor para el Comment objeto que agregará al objeto de colección existente WordprocessingCommentsPartComments . Para ello, busca el valor de atributo más alto Id que se proporciona a en Comment el Comments objeto de colección, incrementa el valor en uno y, a continuación, lo almacena como valor Id . Si no existe ninguna WordprocessingCommentsPart parte, el código crea una mediante el AddNewPart método del MainDocumentPart objeto y, a continuación, le agrega un Comments objeto de colección.

if (document.MainDocumentPart.GetPartsOfType<WordprocessingCommentsPart>().Count() > 0)
{
    if (wordprocessingCommentsPart.Comments.HasChildren)
    {
        // Obtain an unused ID.
        id = (wordprocessingCommentsPart.Comments.Descendants<Comment>().Select(e =>
        {
            if (e.Id is not null && e.Id.Value is not null)
            {
                return int.Parse(e.Id.Value);
            }
            else
            {
                throw new ArgumentNullException("Comment id and/or value are null.");
            }
        })
            .Max() + 1).ToString();
    }
}

Los Comment objetos y Comments representan elementos comment y comments, respectivamente, en el esquema Wordprocessing de Open XML. Comment Debe agregarse a un Comments objeto para que el código cree primero una instancia de un Comments objeto (mediante los argumentos authorde cadena , initialsy comments que se pasaron al AddCommentOnFirstParagraph método ).

El comentario se representa mediante el siguiente ejemplo de código WordprocessingML. .

    <w:comment w:id="1" w:initials="User">
      ...
    </w:comment>

A continuación, el código anexa al CommentComments objeto . Esto crea la estructura de árbol del modelo de objetos de documento XML (DOM) necesaria en la memoria, que consta de un comments elemento primario con comment elementos secundarios bajo él.

Paragraph p = new Paragraph(new Run(new Text(comment)));
Comment cmt =
    new Comment()
    {
        Id = id,
        Author = author,
        Initials = initials,
        Date = DateTime.Now
    };
cmt.AppendChild(p);
wordprocessingCommentsPart.Comments.AppendChild(cmt);

El siguiente ejemplo de código de WordprocessingML representa el contenido de una parte de comentarios en un documento WordprocessingML.

    <w:comments>
      <w:comment … >
        …
      </w:comment>
    </w:comments>

Con la instancia del Comment objeto , el código asocia con Comment un intervalo en el documento Wordprocessing. CommentRangeStart y CommentRangeEnd los objetos corresponden a los commentRangeStart elementos y commentRangeEnd del esquema Wordprocessing de Open XML. Se CommentRangeStart da un objeto como argumento al InsertBefore método del Paragraph objeto y se pasa un CommentRangeEnd objeto al InsertAfter método . Esto crea un intervalo de comentarios que se extiende desde antes del primer carácter del primer párrafo del documento de procesamiento de texto hasta después del último carácter del primer párrafo.

Un CommentReference objeto representa un commentReference elemento del esquema Wordprocessing de Open XML. CommentReference vincula un comentario específico de la WordprocessingCommentsPart parte (el archivo Comments.xml del paquete Wordprocessing) a una ubicación específica en el cuerpo del documento (la MainDocumentPart parte contenida en el archivo Document.xml del paquete Wordprocessing). El atributo id de comment, commentRangeStart, commentRangeEnd y commentReference es el mismo para un comentario determinado, por lo que el atributo id de commentReference debe coincidir con el valor del atributo id de comment vinculado. En el ejemplo, el código agrega un commentReference elemento mediante la API y crea una instancia de un CommentReference objeto, especifica el Id valor y, a continuación, lo agrega a un Run objeto.

firstParagraph.InsertBefore(new CommentRangeStart()
{ Id = id }, firstParagraph.GetFirstChild<Run>());

// Insert the new CommentRangeEnd after last run of paragraph.
var cmtEnd = firstParagraph.InsertAfter(new CommentRangeEnd()
{ Id = id }, firstParagraph.Elements<Run>().Last());

// Compose a run with CommentReference and insert it.
firstParagraph.InsertAfter(new Run(new CommentReference() { Id = id }), cmtEnd);

Código de ejemplo

En el siguiente ejemplo de código se muestra cómo crear un comentario y cómo asociarlo con un intervalo en un documento de procesamiento de texto. Para llamar al método AddCommentOnFirstParagraph , pase la ruta de acceso del documento, el nombre, las iniciales y el texto del comentario.

string fileName = args[0];
string author = args[1];
string initials = args[2];
string comment = args[3];

AddCommentOnFirstParagraph(fileName, author, initials, comment);

A continuación se incluye el código de ejemplo completo en C# y Visual Basic.

// Insert a comment on the first paragraph.
static void AddCommentOnFirstParagraph(string fileName, string author, string initials, string comment)
{
    // Use the file name and path passed in as an 
    // argument to open an existing Wordprocessing document. 
    using (WordprocessingDocument document = WordprocessingDocument.Open(fileName, true))
    {
        if (document.MainDocumentPart is null)
        {
            throw new ArgumentNullException("MainDocumentPart and/or Body is null.");
        }

        WordprocessingCommentsPart wordprocessingCommentsPart = document.MainDocumentPart.WordprocessingCommentsPart ?? document.MainDocumentPart.AddNewPart<WordprocessingCommentsPart>();

        // Locate the first paragraph in the document.
        Paragraph firstParagraph = document.MainDocumentPart.Document.Descendants<Paragraph>().First();
        wordprocessingCommentsPart.Comments ??= new Comments();
        string id = "0";

        // Verify that the document contains a 
        // WordProcessingCommentsPart part; if not, add a new one.
        if (document.MainDocumentPart.GetPartsOfType<WordprocessingCommentsPart>().Count() > 0)
        {
            if (wordprocessingCommentsPart.Comments.HasChildren)
            {
                // Obtain an unused ID.
                id = (wordprocessingCommentsPart.Comments.Descendants<Comment>().Select(e =>
                {
                    if (e.Id is not null && e.Id.Value is not null)
                    {
                        return int.Parse(e.Id.Value);
                    }
                    else
                    {
                        throw new ArgumentNullException("Comment id and/or value are null.");
                    }
                })
                    .Max() + 1).ToString();
            }
        }

        // Compose a new Comment and add it to the Comments part.
        Paragraph p = new Paragraph(new Run(new Text(comment)));
        Comment cmt =
            new Comment()
            {
                Id = id,
                Author = author,
                Initials = initials,
                Date = DateTime.Now
            };
        cmt.AppendChild(p);
        wordprocessingCommentsPart.Comments.AppendChild(cmt);

        // Specify the text range for the Comment. 
        // Insert the new CommentRangeStart before the first run of paragraph.
        firstParagraph.InsertBefore(new CommentRangeStart()
        { Id = id }, firstParagraph.GetFirstChild<Run>());

        // Insert the new CommentRangeEnd after last run of paragraph.
        var cmtEnd = firstParagraph.InsertAfter(new CommentRangeEnd()
        { Id = id }, firstParagraph.Elements<Run>().Last());

        // Compose a run with CommentReference and insert it.
        firstParagraph.InsertAfter(new Run(new CommentReference() { Id = id }), cmtEnd);
    }
}

Recursos adicionales