При формировании печатных форм в формате MS Word через механизм Active Document или COM-соединение часто возникает задача динамического изменения структуры документа. Одной из самых распространенных проблем является необходимость полного удаления строки или целого абзаца, если в нем встречается определенный текст или если переменная (параметр) оказалась пустой. Простое замещение текста пустой строкой "" не решает задачу, так как в документе остается пустой абзац (пустая строка), что портит визуальное оформление.
В этой статье мы подробно разберем, как программно найти и полностью удалить абзац, чтобы текст снизу автоматически подтянулся вверх, и какие нюансы работы с объектами Word.Application следует учитывать.
Прежде всего, проанализируем важный технический нюанс: в объектной модели Microsoft Word на программном уровне отсутствует строгое понятие «строка», так как текст динамически переносится в зависимости от шрифта, полей и размера страницы. То, что мы видим как строку, является либо Абзацем (Paragraph), либо частью текста внутри него. Поэтому, когда мы говорим об удалении «строки», мы чаще всего имеем в виду удаление всего абзаца, который содержит искомый текст.
Рассмотрим наиболее популярный способ, основанный на использовании объекта Selection (выделение). Логика работы заключается в том, чтобы найти нужный фрагмент текста, «захватить» весь абзац, в котором он находится, и выполнить команду удаления.
Для реализации этого метода мы будем использовать следующие константы VBA (их нужно определить в 1С или использовать числовые значения):
wdParagraph = 4 — единица измерения «Абзац».wdLine = 5 — единица измерения «Линейная строка».wdExtend = 1 — режим расширения выделения.Посмотрим на пример кода:
// Предположим, Word — это уже созданный COM-объект Word.Application
Selection = Word.Selection;
Find = Selection.Find;
// Настраиваем параметры поиска
Find.Text = "ТекстКоторыйНужноНайти";
Find.ClearFormatting();
Find.Forward = Истина;
// Выполняем поиск. Если текст найден, метод Execute вернет Истина
Если Find.Execute() Тогда
// Перемещаемся в начало абзаца, где найден текст
Selection.MoveUp(4, 1); // 4 — это wdParagraph
// Выделяем весь абзац целиком
Selection.MoveDown(4, 1, 1); // Третий параметр 1 — это wdExtend
// Удаляем выделенный фрагмент
Selection.Delete();
КонецЕсли;
Выясним причину использования MoveUp и MoveDown: метод Find.Execute выделяет только сам искомый текст. Чтобы удалить всю строку (абзац), нам нужно расширить это выделение до границ абзаца.
Если искомый текст встречается в документе несколько раз и нужно удалить все строки с ним, проанализируем ситуацию с использованием цикла Пока. Рассмотрим готовую процедуру, которую можно встроить в любой модуль 1С:
Процедура Word_УдалитьАбзацСодержащийСтрокуПоиска(WordApplication, СтрокаПоиска)
Find = WordApplication.Selection.Find;
Find.Text = СтрокаПоиска;
// Цикл по всем найденным вхождениям
Пока Find.Execute() Цикл
// Получаем текущее выделение (оно нашло текст)
ТекущееВыделение = WordApplication.Selection;
// Расширяем и удаляем
ТекущееВыделение.MoveUp(4, 1); // wdParagraph
ТекущееВыделение.MoveDown(4, 1, 1); // wdParagraph, 1 шаг, wdExtend
ТекущееВыделение.Delete();
// После удаления выделение сбрасывается, Find продолжит с новой позиции
КонецЦикла;
КонецПроцедуры
Как показывает практика, работа с поиском текста может быть нестабильной, если текст формируется динамически или содержит спецсимволы. Самый надежный и профессиональный метод — использование Закладок (Bookmarks). Вы можете заранее в макете Word выделить всю строку (абзац) и назначить ей имя закладки.
Рассмотрим подробнее этот подход:
УдаляемаяСтрока1.
// Проверяем существование закладки перед удалением
ИмяЗакладки = "УдаляемаяСтрока1";
Если Word.ActiveDocument.Bookmarks.Exists(ИмяЗакладки) Тогда
// Выделяем область закладки
Word.ActiveDocument.Bookmarks.Item(ИмяЗакладки).Select();
// Удаляем выделение (вместе с абзацем)
Word.Application.Selection.Delete();
КонецЕсли;
Этот метод считается лучшим, так как он не зависит от содержания текста и гарантированно удаляет именно ту область, которую вы разметили в макете.
Часто в макетах используются поля типа { DOCVARIABLE "ИмяПеременной" }. Если вы программно установили значение переменной Word.Variables.Item("ИмяПеременной").Value = "Удалить";, обычный поиск Find.Execute может не найти этот текст, так как он видит «код поля», а не его результат.
Чтобы решить эту проблему, выполним следующие шаги:
Во-первых, необходимо обновить поля в документе перед поиском: Word.ActiveDocument.Fields.Update();.
Во-вторых, можно использовать замену спецсимволов. Например, замену текста вместе со знаком абзаца на «ничего».
Find = Word.Application.Selection.Find;
Find.Text = "УдалитьСтроку" + Символы.ВК; // Добавляем символ возврата каретки
Find.Replacement.Text = "";
Find.Execute(,,,,,,,,,, 2); // 2 — это wdReplaceAll
Иногда удобнее сначала заменить весь ненужный текст на пустые строки, а в самом конце одним действием «схлопнуть» документ, удалив все лишние переходы на новую строку. В Word знак абзаца обозначается кодом ^p.
Посмотрим на пример удаления двойных абзацев:
Selection = Word.Application.Selection;
Selection.Find.ClearFormatting();
Selection.Find.Replacement.ClearFormatting();
Selection.Find.Text = "^p^p"; // Ищем два знака абзаца подряд
Selection.Find.Replacement.Text = "^p"; // Заменяем на один
Selection.Find.Execute(,,,,,,,,,, 2); // wdReplaceAll
При работе с 1С и Word важно помнить о «гигиене» памяти. После выполнения всех операций обязательно закрывайте приложение и освобождайте переменные:
Word.Quit() для закрытия процесса.Неопределено (например, Word = Неопределено;).Active Document на форме 1С, помните, что методы Selection могут воздействовать на визуальный фокус пользователя.Подведем итог: если структура документа жесткая — используйте закладки. Если структура гибкая и заранее неизвестно, что придется удалять — используйте поиск с расширением области выделения до абзаца (wdParagraph). Это обеспечит корректное смещение текста вверх и сохранит профессиональный вид вашего печатного документа.