Share via


Eliminar comentarios de todos los autores o de un autor específico 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 eliminar mediante programación los comentarios de todos o de un autor específico en un documento de procesamiento de texto, sin tener que cargar el documento en Microsoft Word. Incluye un método DeleteComments de ejemplo para ilustrar esta tarea.


Método DeleteComments

Puede usar el método DeleteComments para eliminar todos los comentarios de un documento de procesamiento de texto, o solo los escritos por un autor específico. Tal como se muestra en el código siguiente, el método acepta dos parámetros que indican el nombre del documento que se desea transformar (cadena) y, opcionalmente, el nombre del autor cuyos comentarios se desean eliminar (cadena). Si se proporciona el nombre de un autor, el código eliminará los comentarios escritos por el autor especificado. Si no se proporciona el nombre de un autor, el código eliminará todos los comentarios.

    // Delete comments by a specific author. Pass an empty string for the 
    // author to delete all comments, by all authors.
    public static void DeleteComments(string fileName, 
        string author = "")

Llamar al método DeleteComments

Para llamar al método DeleteComments, proporcione los parámetros necesarios, tal como se muestra en el código siguiente.

    DeleteComments(@"C:\Users\Public\Documents\DeleteComments.docx",
    "David Jones");

Funcionamiento del código

El código siguiente empieza abriendo el documento, mediante el método WordprocessingDocument.Open e indicando que el documento se debe abrir para acceso de lectura y escritura (el valor de parámetro true final). A continuación, el código recupera una referencia a la parte de comentarios, utilizando la propiedad WordprocessingCommentsPart del elemento de documento principal, después de haber recuperado una referencia a la parte principal del documento de la propiedad MainDocumentPart del documento de procesamiento de texto. Si falta la parte de comentarios, no tiene sentido continuar, puesto que no puede haber ningún comentario por eliminar.

    // Get an existing Wordprocessing document.
    using (WordprocessingDocument document =
      WordprocessingDocument.Open(fileName, true))
    {
        // Set commentPart to the document WordprocessingCommentsPart, 
        // if it exists.
        WordprocessingCommentsPart commentPart =
          document.MainDocumentPart.WordprocessingCommentsPart;

        // If no WordprocessingCommentsPart exists, there can be no 
        // comments. Stop execution and return from the method.
        if (commentPart == null)
        {
            return;
        }
        // Code removed here…
    }

Crear la lista de comentarios

A continuación, el código realiza dos tareas: crear una lista de todos los comentarios que se van a eliminar y crear una lista de identificadores de comentario que corresponden a los comentarios que se van a eliminar. Dadas estas listas, el código puede eliminar los comentarios de la parte de comentarios que contiene los comentarios y eliminar las referencias a los comentarios de la parte del documento. El código siguiente comienza recuperando una lista de elementos Comment . Para recuperar la lista, convierte la colección Elements expuesta por la variable commentPart en una lista de objetos Comment .

    List<Comment> commentsToDelete =
        commentPart.Comments.Elements<Comment>().ToList();

Hasta ahora, la lista de comentarios contiene todos los comentarios. Si el parámetro de autor no es una cadena vacía, el código siguiente limita la lista únicamente a los comentarios en los que la propiedad Author coincide con el parámetro proporcionado.

    if (!String.IsNullOrEmpty(author))
    {
        commentsToDelete = commentsToDelete.
        Where(c => c.Author == author).ToList();
    }

Antes de eliminar cualquier comentario, el código recupera una lista de valores de identificador de comentario, para poder eliminar más adelante los elementos coincidentes de la parte del documento. La llamada al método Select proyecta eficazmente la lista de comentarios, recuperando un IEnumerable<T> de cadenas que contienen todos los valores de identificador de comentario.

    IEnumerable<string> commentIds = 
        commentsToDelete.Select(r => r.Id.Value);

Eliminar comentarios y guardar la parte

Según la colección commentsToDelete, el código siguiente recorre en bucle todos los comentarios que se deben eliminar y los elimina. A continuación, el código guarda la parte del comentario.

    // Delete each comment in commentToDelete from the 
    // Comments collection.
    foreach (Comment c in commentsToDelete)
    {
        c.Remove();
    }

    // Save the comment part changes.
    commentPart.Comments.Save();

Eliminar referencias a comentarios en el documento

Aunque en este punto el código ha quitado correctamente todos los comentarios, no resulta suficiente. El código también debe quitar las referencias a comentarios de la parte del documento. Esta acción requiere tres pasos porque la referencia de comentario incluye los elementos CommentRangeStart, CommentRangeEnd y CommentReference , y el código debe quitar los tres para cada comentario. Antes de cualquier eliminación, el código recupera primero una referencia al elemento raíz de la parte principal del documento, tal como se muestra en el código siguiente.

    Document doc = document.MainDocumentPart.Document;

Según una referencia al elemento de documento, el código siguiente realiza su bucle de eliminación tres veces, una para cada uno de los distintos elementos que debe eliminar. En cada caso, el código busca todos los descendientes del tipo correcto (CommentRangeStart, CommentRangeEnd o CommentReference) y limita la lista a aquellos cuyo valor de propiedad Id está incluido en la lista de identificadores de comentario que se van a eliminar. Según la lista de elementos por eliminar, el código quita los elementos de uno en uno. Por último, el código finaliza guardando el documento.

    // Delete CommentRangeStart for each
    // deleted comment in the main document.
    List<CommentRangeStart> commentRangeStartToDelete =
        doc.Descendants<CommentRangeStart>().
        Where(c => commentIds.Contains(c.Id.Value)).ToList();
    foreach (CommentRangeStart c in commentRangeStartToDelete)
    {
        c.Remove();
    }

    // Delete CommentRangeEnd for each deleted comment in the main document.
    List<CommentRangeEnd> commentRangeEndToDelete =
        doc.Descendants<CommentRangeEnd>().
        Where(c => commentIds.Contains(c.Id.Value)).ToList();
    foreach (CommentRangeEnd c in commentRangeEndToDelete)
    {
        c.Remove();
    }

    // Delete CommentReference for each deleted comment in the main document.
    List<CommentReference> commentRangeReferenceToDelete =
        doc.Descendants<CommentReference>().
        Where(c => commentIds.Contains(c.Id.Value)).ToList();
    foreach (CommentReference c in commentRangeReferenceToDelete)
    {
        c.Remove();
    }

    // Save changes back to the MainDocumentPart part.
    doc.Save();

Código de ejemplo

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

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

DeleteComments(args[0], args[1]);

// Delete comments by a specific author. Pass an empty string for the 
// author to delete all comments, by all authors.
static void DeleteComments(string fileName, string author = "")
{
    // Get an existing Wordprocessing document.
    using (WordprocessingDocument document = WordprocessingDocument.Open(fileName, true))
    {

        if (document.MainDocumentPart is null || document.MainDocumentPart.WordprocessingCommentsPart is null)
        {
            throw new ArgumentNullException("MainDocumentPart and/or WordprocessingCommentsPart is null.");
        }

        // Set commentPart to the document WordprocessingCommentsPart, 
        // if it exists.
        WordprocessingCommentsPart commentPart =
            document.MainDocumentPart.WordprocessingCommentsPart;

        // If no WordprocessingCommentsPart exists, there can be no 
        // comments. Stop execution and return from the method.
        if (commentPart is null)
        {
            return;
        }

        // Create a list of comments by the specified author, or
        // if the author name is empty, all authors.
        List<Comment> commentsToDelete =
            commentPart.Comments.Elements<Comment>().ToList();
        if (!String.IsNullOrEmpty(author))
        {
            commentsToDelete = commentsToDelete.
            Where(c => c.Author == author).ToList();
        }
        IEnumerable<string?> commentIds =
            commentsToDelete.Where(r => r.Id is not null && r.Id.HasValue).Select(r => r.Id?.Value);

        // Delete each comment in commentToDelete from the 
        // Comments collection.
        foreach (Comment c in commentsToDelete)
        {
            c.Remove();
        }

        // Save the comment part change.
        commentPart.Comments.Save();

        Document doc = document.MainDocumentPart.Document;

        // Delete CommentRangeStart for each
        // deleted comment in the main document.
        List<CommentRangeStart> commentRangeStartToDelete =
            doc.Descendants<CommentRangeStart>().
            Where(c => c.Id is not null && c.Id.HasValue && commentIds.Contains(c.Id.Value)).ToList();
        foreach (CommentRangeStart c in commentRangeStartToDelete)
        {
            c.Remove();
        }

        // Delete CommentRangeEnd for each deleted comment in the main document.
        List<CommentRangeEnd> commentRangeEndToDelete =
            doc.Descendants<CommentRangeEnd>().
            Where(c => c.Id is not null && c.Id.HasValue && commentIds.Contains(c.Id.Value)).ToList();
        foreach (CommentRangeEnd c in commentRangeEndToDelete)
        {
            c.Remove();
        }

        // Delete CommentReference for each deleted comment in the main document.
        List<CommentReference> commentRangeReferenceToDelete =
            doc.Descendants<CommentReference>().
            Where(c => c.Id is not null && c.Id.HasValue && commentIds.Contains(c.Id.Value)).ToList();
        foreach (CommentReference c in commentRangeReferenceToDelete)
        {
            c.Remove();
        }

        // Save changes back to the MainDocumentPart part.
        doc.Save();
    }
}

Vea también