Поделиться через


Удалить комментарии всех или определенного автора в документе обработки текста

В этом разделе показано, как использовать классы в пакете SDK Open XML для Office для программного удаления комментариев всех или определенных авторов в текстовом документе без необходимости загружать документ в microsoft Word. Он содержит пример DeleteComments метода для иллюстрации этой задачи.


Метод DeleteComments

Метод можно использовать для DeleteComments удаления всех комментариев из текстового документа или только тех, которые написаны определенным автором. Как показано в следующем коде, этот метод принимает два параметра, задающие имя изменяемого документа (строковый параметр) и, дополнительно, имя автора, чьи комментарии требуется удалить (строковый параметр). Если предоставить имя автора, код удаляет комментарии, написанные заданным автором. Если не предоставить имя автора, код удаляет все комментарии.

// 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 = "")

Вызов метода DeleteComments

Чтобы вызвать DeleteComments метод, укажите необходимые параметры, как показано в следующем коде.

if (args is [{ } fileName, { } author])
{
    DeleteComments(fileName, author);
}
else if (args is [{ } fileName2])
{
    DeleteComments(fileName2);
}

Принципы работы кода

Следующий код начинается с открытия документа с помощью WordprocessingDocument.Open метода и указания, что документ должен быть открыт для чтения и записи (конечное true значение параметра). Затем код извлекает ссылку на часть комментариев, используя WordprocessingCommentsPart свойство main части документа, после получения ссылки на часть документа main из MainDocumentPart свойства текстового документа. Если часть комментариев отсутствует, продолжение не имеет смысла, так как нет комментариев (и удалять нечего).

// Get an existing Wordprocessing document.
using (WordprocessingDocument document = WordprocessingDocument.Open(fileName, true))
{

    if (document.MainDocumentPart is null)
    {
        throw new ArgumentNullException("MainDocumentPart 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;
    }

Создание списка комментариев

Далее код выполняет две задачи: создание списка всех удаляемых комментариев и создание списка идентификаторов комментариев, соответствующих удаляемой примечания. Учитывая эти списки, код может как удалить комментарии из части примечаний, содержащей примечания, так и ссылки на комментарии из части документа. Следующий код начинается с получения списка Comment элементов. Чтобы получить список, он преобразует коллекцию Elements() , предоставленную переменной, commentPart в список Comment объектов.

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

Пока что список комментариев содержит все комментарии. Если параметр author не является пустой строкой, следующий код ограничивает список только теми комментариями, где Author свойство соответствует указанному параметру.

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

Перед удалением комментариев код получает список со значениями идентификаторов (ID) комментариев, чтобы позже удалить совпадающие элементы из части документа. Вызов Select метода эффективно проецирует список примечаний, получая из IEnumerable<T> строк, содержащих все значения идентификатора комментария.

IEnumerable<string?> commentIds = commentsToDelete.Where(r => r.Id is not null && r.Id.HasValue).Select(r => r.Id?.Value);

Удаление комментариев и сохранение части

Учитывая коллекцию commentsToDelete , следующий код циклически просматривает все комментарии, требующие удаления, и выполняет удаление.

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

Удаление ссылок на комментарии из документа

Хотя код успешно удалил все комментарии к этому моменту, этого недостаточно. Код также должен удалить ссылки на примечания из части документа. Для этого действия требуется три шага, так как ссылка на комментарий включает CommentRangeStartэлементы , CommentRangeEndи CommentReference , а код должен удалить все три для каждого комментария. Перед удалением код сначала извлекает ссылку на корневой элемент части документа main, как показано в следующем коде.

    Document doc = document.MainDocumentPart.Document;

Имея ссылку на элемент документа, следующий код выполняет цикл удаления три раза: по одному разу для каждого из элементов, предназначенных для удаления. В каждом случае код ищет всех потомков правильного типа (CommentRangeStart, CommentRangeEndили CommentReference) и ограничивает список теми, чье Id значение свойства содержится в списке идентификаторов комментариев, которые необходимо удалить. Имея список удаляемых элементов, код удаляет каждый из них. Наконец, код завершает работу, сохраняя документ.

// 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();
}

Пример кода

Ниже приведен полный пример кода на C# и Visual Basic.

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


// 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)
        {
            throw new ArgumentNullException("MainDocumentPart 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)
        {
            if (c is not null)
            {
                c.Remove();
            }
        }

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

См. также