Форматированный документ

Доброго времени суток, коллеги! Это новый объект, который появился в версии платформы 1С 8.2.11. И конечно же первый вопрос, который возникает, что нового он несет и какой новый функционал он добавляет. На изображении к этой статье продемонстрирован пример работы с этим объектом, само предназначение, я думаю вы можете придумать сами.

  1. Предназначение объекта
  2. Тип данных – форматированный документ
  3. Размещение форматированного документа на форме
  4. Сохранение форматированного документа в БД
  5. Свойства и методы форматированного документа
  6. Вставка текста в форматированный документ

Предназначение объекта

Что можно сделать, выделив некоторую часть этого текста? Можно сделать её жирным шрифтом или курсивом. Кроме того, форматированный документ позволяет сделать любое текстовое или цветовое оформление с помощью стандартной панели. Также можно указать, что вот этот шрифт должен быть именно таким и не иначе.

Вы можете увидеть, что все базовые возможности, которые присущи всем текстовым процессорам стали доступными. До этого релиза (8.2.11) не было возможности этого сделать, используя стандартные средства. А теперь это стандартные возможности платформы, которые очень просто поместить на абсолютно любую форму. В форматированном документе появились номерованные списки, маркированные списки, ссылка, различные шрифты. Также, в этот форматированный документ можно вставить какую-нибудь картинку.

Список элементов форматированного списка
Список всех элементов форматированного списка

Самый часто используемый пример, это редактирование и отправка электронных писем. Если у вас почтовый клиент встроен в одну из конфигураций, то очень удобно использовать форматированный документ для того, чтобы отправлять и принимать письма. То есть читать их в соответствующем оформлении, а также этот объект можно будет применять, чтобы оформить некоторые документы, которые будут печататься. Например: договоры, трудовые договоры и прочие документы. А дальше мы рассмотрим, как работать с объектом типа данных – форматированный документ.

Тип данных – форматированный документ

Итак, у нас появился новый объект и соответственно появился новый тип данных, который поддерживает функционирование этого объекта. Если обратиться к синтаксис-помощнику, то можно найти такой тип данных «ФорматированныйДокумент». Мы увидим, что у него есть ряд методов.

Тип данных форматированный документ в Синтаксис-помощнике
Информация из Синтаксис-помощника о свойствах и методах типа данных ФорматированныйДокумент

Доступны: тонкий клиент, веб-клиент, сервер, толстый клиент. То есть во всех видах приложения мы можем использовать этот форматированный документ. Но есть одна важная особенность — мы не можем указать этот тип данных в качестве типа данных реквизита.

Информация о типах данных реквизита объекта метаданных
Свойства реквизита со всеми доступными ему типами данных

То есть создавая какой-либо реквизит справочника или документа, и выбирая тип мы видим, что здесь нельзя указывать форматированный документ. А как же можно сохранить, те изменения, которые сделал пользователь? Допустим, пользователь хочет сохранить изменения типового договора в базе данных. Для этого применяется специальный объект «ХранилищеЗначения», который может в себе содержать значения любого типа данных, включая бинарные типы. Это могут быть различные растровые изображения и исполняемые файлы. Этот тип данных можно увидеть на иллюстрации выше. В том числе в «ХранилищеЗначения» можно и нужно записывать форматированный документ.

Таким образом, в базе данных сам документ будет храниться, как «ХранилищеЗначения», а далее при открытии формы он будет извлечен из хранилища и отображен на форме. Отображение осуществляется с использованием типа данных «ФорматированныйДокумент». После изменения, при нажатии на кнопку «Записать» нужно снова взять форматированный документ и записать информацию в хранилище. Итак, как видим, нужно делать вот такие взаимные преобразования. Здесь описана общая схема, в следующем разделе будет рассмотрено, как работать с новым объектом «ХранилищеЗначения».

Размещение форматированного документа на форме

Давайте рассмотрим решение конкретной практической задачи. Представим, что нам необходимо хранить тексты договоров с различными оформлениями, жирным, курсивом и т.д. Понятно, что для этого идеально подходит форматированный документ. И давайте сейчас это реализуем на нашей небольшой каркасной конфигурации. Итак, у нас есть справочник договоры, где нам необходимо будет создать реквизит, который назовем «Текст». Как уже отмечалось, это должен быть реквизит с типом данных «ХранилищеЗначения».

Реквизит объекта ХранилищеЗначения (Недоступен в данных формы)
Список реквизитов формы с типами значений

Дальше необходимо создать форму договора, форму элемента справочника и на ряду со стандартными реквизитами нужно будет разместить данные форматированного документа. Обратите внимание, если попытаться перенести «Текст» из реквизитов на «Форму», то система не позволит этого сделать. Поскольку, этот тип данных недоступен в данных формы напрямую. В скобках рядом с описанием типа реквизита написано: (Недоступен в данных формы). Мы должны извлечь из хранилища значения то, что в нем содержится и только потом можно с ним работать.

Как же можно отобразить содержимое форматированного документа на форме? Нужно создать новый реквизит формы, который можно назвать «ТекстДоговора». В качестве типа данных нужно указать «ФорматированныйДокумент». Обратите внимание, что, создавая реквизит формы можно указать этот тип данных, но в качестве реквизита объекта, форматированный документ выступать не может.

Дальше разместим «ТекстДоговора» на форме. Чтобы посмотреть, что у нас получилось нужно запустить приложение в режиме откладки. Нужно открыть любой договор, любого контрагента. В подсистеме продажа есть справочник «Договоры». Там есть ряд созданных договоров.

Форма договора с текстовым полем типа ФорматированныйДокумент
Редактирование текста договора в форматированном документе

И мы видим, что действительно, можно написать некоторый текст договора. Также здесь можно устанавливать различные выделения, используя горячие клавиши. Но, не все пользователи знают горячие клавиши, поэтому правильно было бы создать командную панель, которая позволит менять размеры шрифта и делать цветовое выделение текста. 

Итак, давайте сделаем эту командную панель. Для этого в форме договора нужно создать новый элемент — «Группа — Командная панель». Итак, укажем, что группа будет располагаться перед договором и в качестве источника команд для этой группы как раз и выступает реквизит, который имеет тип данных «Форматированный Документ». В этом случае у нас появляются соответствующие кнопки в командной панели. И теперь можно работать полноценно с данным текстом.

Вывод командной панели на форму
Командная панель с перечнем кнопок для форматирования документа

Можно проверить, что это действительно работает. Запустим приложение в режиме отладки и вновь обратимся к справочнику договоров. Итак, описывая некоторый текст договора, можно действительно увидеть, что существует возможность использовать все соответствующие функции для форматированного документа.

Но у нас есть ряд проблем. Первая проблема заключается вот в чем: заходя в существующий элемент и меняя текст договора признак модифицированности для формы не возникает. То есть пользователь, случайно нажав на кнопку Esc, получает закрытие элемента без каких-либо вопросов. Для того чтобы такого не возникало в качестве свойств реквизита необходимо установить флаг «Сохраняемые данные».

Форма реквизита с установленным Свойством СохраняемыеДанные
Свойство СохраняемыеДанные влияет на модифицированность формы

Этот флаг как раз и будет влиять на то, что свойства модифицированности будут выставляться в значение Истина, если данный реквизит будет как-то меняться.  Более того, данный флаг означает, что если форма открывается в режиме только для чтения (read only), то и этот реквизит будет открываться в этом же режиме.

Теперь, изменяя, каким-либо образом текст договора, нажать на кнопку Esc, то сейчас система скажет, что нужно принять решение о том нужно ли сохранить изменения, либо от них отказаться. Нажимая на кнопку «Да», мы запишем данные изменения. И вполне очевидно, что система ничего не сделает, поскольку мы делаем изменения в реквизите формы, который у нас пока еще никак не связан с реквизитом объекта. Нужно настроить эту программную связь.

Сохранение форматированного документа в БД

На текущий момент мы создали форматированный документ на форме. Более того, у нас есть реквизит объекта с типом «ХранилищеЗначения». И сейчас предстоит настроить связь между двумя этими объектами, между двумя этими реквизитами.

Во-первых, при открытии формы необходимо будет обращаться к реквизиту объекта и заполнять тем значением, которое там хранится – значением форматированного документа. Во-вторых, при записи элемента мы берем содержимое форматированного документа и помещаем в хранилище значения. Таким образом у нас будет возникать эффект того, что все действия, которые пользователь делает с нашим форматированным документом сохраняются в базе данных.

Теперь давайте разберемся какие же обработчики событий нам необходимо будет выбрать. В частности, первое событие. В момент открытия формы мы должны обратиться к значению, которое содержится в хранилище значения. Итак, какие у нас обработчики есть вообще.

Список обработчиков событий в порядке вызова
Обработчики событий в порядке их вызова для модуля формы элемента справочника

Как видим, обработчики событий указаны в порядке вызова. Первый обработчик «ПриСозданииНаСервере», потом «ПриОткрытии». Отличаются они тем, что первый исполняется на сервере, а второй на клиенте. Возможно, что возникнет желание использовать обработчик события «ПриСозданииНаСервере». Но этот обработчик не подходит для нашего случая. Нужно не забывать, что на форме есть кнопка «Перечитать» (Еще -> Перечитать).

Допустим, пользователь открыл форму договора. И начинал что-то в ней менять. И вдруг понял, что неплохо бы вернуться к исходной версии. Нажал на кнопку «Перечитать», чтобы вернуться к исходным данным. И система возвращает то значение, которое было изначально.

Так вот, нам нужно будет тоже присоединиться к этому событию, то есть к чтению данных из базы. Поэтому идеальным вариантом для этого действия было бы событие «ПриЧтенииНаСервере». Это событие будет возникать и в момент создания формы, и в момент нажатия на кнопку «Перечитать».

&НаСервере
Процедура ПриЧтенииНаСервере(ТекущийОбъект)
	ТекстДоговора = ТекущийОбъект.Текст.Получить();
КонецПроцедуры

Обратите внимание, что сам текущий объект передается через параметр, то есть мы указываем текст договора. Далее обращаемся к текущему объекту и свойству текста. Обратите внимание: чтобы получить данные, содержащиеся в хранилище, нужно использовать метод «Получить». Тогда система извлечет данные, которые хранятся внутри хранилища.

В тот момент, когда пользователь решит записать сделанные изменения, нам нужно записать данные в хранилище. Для этого нужно использовать обработчик событий «ПередЗаписьюНаСервере». Сначала нужно обратиться к параметру «ТекущийОбъект.Текст» и присвоить ему значение. Какое значение можно присвоить? Есть реквизит формы «ТекстДоговора».  К нему можно обращаться напрямую. И это присваивание будет неправильным.

То есть, нельзя взять и напрямую присвоить реквизиту с типом «ХранилищеЗначения» какой-либо тип данных. Нужно сделать преобразование. Нужно поместить объект в хранилище и для этого нужно воспользоваться следующим приёмом: использовать конструктор для типа данных «ХранилищеЗначения».

&НаСервере
Процедура ПередЗаписьюНаСервере(Отказ, ТекущийОбъект, ПараметрыЗаписи)
	ТекущийОбъект.Текст = Новый ХранилищеЗначения(ТекстДоговора);
КонецПроцедуры

Итак, с помощью конструктора «Новый ХранилищеЗначения» создан объект с типом «ХранилищеЗначения», который будет содержать переданные в параметре данные. И теперь это сохранение будет корректно отрабатываться.

Свойства и методы форматированного документа

Можно ли сделать шаблоны, которые заменяются значениями из реквизитов справочника? Например такие: «%контрагент%», «%организация%» и так далее. То есть вместо этих значений подставлялись бы какие-то значения, соответствующие текущему конкретному контрагенту и текущей организации. К сожалению, в явном виде шаблоны описать нельзя, но все же кое-какие возможности по программной модификации форматированного документа у нас все же имеются.

Давайте, прежде всего, рассмотрим, какой существует программный интерфейс для работы с форматированным документом. Начнем с того, что для отображения форматированного документа на форме используется самый обыкновенный элемент управления формы поля.

Вид этого поля «Поле форматированного документа». Исходя из этого вида система для элемента «ТекстДоговора» может выполнять определенные функции. В Синтаксис-помощнике видно, какие есть специальные функции для работы с форматированным документом на форме.

Интерфейс (управляемый) -> Поле формы -> Расширение форматированного документа.

Обратите внимание, что есть ряд свойств. Высота документа, выделенный текст и так далее. И в частности, мы можем получить границы выделения. То есть можно заменить выделенный пользователем текст на какое-либо значение. Давайте продемонстрируем как это можно сделать.

Итак, создадим команду, неважно как она будет называться. Поместим эту команду в командную панель и опишем обработчик действия. Естественно, данный метод получит границы выделения доступные клиенту поскольку он относится непосредственно к полю.

&НаКлиенте
Процедура ПолучитьГраницы(Команда)
	Перем Начало, Конец;
	А = Элементы.ТекстДоговора.ПолучитьГраницыВыделения(Начало, Конец);
КонецПроцедуры

Обратите внимание на некоторую странность. Система говорит, что метод «ПолучитьГраницыВыделения» это процедура, хотя Синтаксис-помощник упорно утверждает, что это функция, поскольку есть возвращаемое значение. Если оставить это код как есть, то при нажатии на кнопку «ПолучитьГраницы» будет выдана ошибка:

«Обращение к процедуре как к функции (ПолучитьГраницыВыделения)». 

То есть метод «ПолучитьГраницыВыделения» все-таки процедура, а не функция. Давайте простим разработчикам платформы такой Синтаксис-помощник. В эту процедуру нужно передать два параметра любого типа данных.  То есть, скорее все здесь имелось в виду то, что в переданные параметры возвращается выделенный фрагмент. В них и вернется выделенный фрагмент.

Тип данных закладка форматированного документа
Табло показывает, что возвращаемое значение является специальным типом форматированного документа

Если запустим отладчик, то увидим, что содержится в переменных: Начало и Конец. Обратите внимание, что возвращается специализированный тип данных форматированного документа. К сожалению, мы не можем посмотреть, что содержится внутри этой закладки. Но условно можно считать, что эта позиция начала выделения и позиция конца выделения.

То есть вся работа с текстом форматированного документа производится на уровне закладок. Для того чтобы изменить какой-либо текст нам нужно получение закладок. Для того, чтобы вставить кусочек текста в определенное место, нам нужна закладка. То есть мы должны получить эту позицию. Причем, позицией в форматированном документе считается любой символ. Картинка считается одной позицией тоже и перевод строки, это тоже позиция.

Итак, давайте сейчас сначала выполним очень простые действия. Сообщим на экран пользователя тот текст, который был выделен. Для этого потребуется обратится к форматированному документу, то есть к реквизиту формы. Обращу ваше внимание, что в форматированном документе тоже есть методы, которые получают закладку по позиции символа, либо наоборот позицию по закладке.

Для того, чтобы получить текст между закладками, существует метод, который называется «ПолучитьТекст». Обращаясь к реквизиту «ТекстДоговора.ПолучитьТекст()» выведем на экран выделенный фрагмент текста. Также, в этом методе указывается начальная и конечная закладки.

&НаКлиенте
Процедура ПолучитьГраницы(Команда)
	Перем Начало, Конец;
	Элементы.ТекстДоговора.ПолучитьГраницыВыделения(Начало, Конец);
	Сообщить(ТекстДоговора.ПолучитьТекст(Начало, Конец));
КонецПроцедуры

Вставка текста в форматированный документ

Сейчас давайте решим задачу вставки в форматированный документ. Допустим, вставим в документ наименование текущего владельца. Сначала вставим конкретную позицию, начало выделения. А потом попытаемся вставить наименование текущего владельца вместо наших специальных маркеров. Итак, решая данную задачу, обратимся к Синтаксис-помощнику.

Описание метода Вставить() объекта форматированный документ
Подробное описание метода Вставить() из Синтаксис-помощника

Видим, что у нас есть метод «Вставить», который принимает на себя три параметра. Первый параметр, это закладка, после которой нужно будет вставить текст, второй параметр само значение, то есть это может быть обычная строка и третий параметр, тип элемента это может быть картинка, перевод строки, либо обычный текст. У нас этот текст является необязательным, поэтому мы третий параметр попросту указывать не будем.

Поступаем следующим образом. В форматированный документ вставляем в позицию «Начало выделения» определенную строчку. Вместо метода «ПолучитьТекст» мы будем использовать метод «Вставить».

&НаКлиенте
Процедура ПолучитьГраницы(Команда)
	Перем Начало, Конец;
	Элементы.ТекстДоговора.ПолучитьГраницыВыделения(Начало, Конец);
	ТекстДоговора.Вставить(Начало, Объект.Владелец);
КонецПроцедуры

Если запустить приложение и обратиться к договору, выделив фрагмент текста, затем нажав на кнопку «Получить границы», то произойдет вставка названия контрагента в начало строки. Но нужно, чтобы система анализировала данную строчку и вместо неё вставляла название контрагента. Как это сделать? Если посмотреть в Синтаксис-помощник, то увидим, что метода поиска какого-либо текста нет, но удалить какой-либо текст мы можем.

Идея заключается в следующем: выгружаем текст, который находится в форматированном документе, с помощью метода «ПолучитьТекст». В строке ищем нужную последовательность символов и понимаем какие символы нужно удалить, с какого по какой и в какую позицию нужно вставить новый текст. Обратите внимание, что существуют методы получения начала и конца закладок: «ПолучитьЗакладкуНачала» и «ПолучитьЗакладкуКонца».

Обращаясь к реквизиту «ТекстДоговора» получим закладку «Начало» и закладку «Конец». Затем получим текст между двумя закладками и заменим его на наименование владельца договора.

&НаКлиенте
Процедура ПолучитьГраницы(Команда)
	Перем Начало, Конец;
	
	Начало = Текстдоговора.ПолучитьЗакладкуНачала(); 
	Конец = ТекстДоговора.ПолучитьЗакладкуКонца(); 
	СтрокаДоговора = ТекстДоговора.ПолучитьТекст(Начало, Конец); 
	Шаблон = "%контрагент%"; 
	ПозицияНачало = Найти(СтрокаДоговора, Шаблон); 
	Если ПозицияНачало <> 0 Тогда 
		ПозицияНачало = ПозицияНачало - 1;
		ПозицияКонец = ПозицияНачало + СтрДлина(Шаблон); 
		ЗакладкаНачало = ТекстДоговора.ПолучитьЗакладкуПоПозиции(ПозицияНачало); 
		ЗакладкаКонец = ТекстДоговора.ПолучитьЗакладкуПоПозиции(ПозицияКонец); 
		
		ТекстДоговора.Удалить(ЗакладкаНачало, ЗакладкаКонец); 
		ТекстДоговора.Вставить(ЗакладкаНачало, Объект.Владелец); 
	КонецЕсли;
КонецПроцедуры

Итак, переменной «Шаблон» присвоено значение %контрагент%. Именно такую строчку будем искать в строке договора. Если обратиться к Синтаксис-помощнику, то можно увидеть какие существуют функции по работе со строками. Есть метод «Найти», который в строке договора поможет найти нужную подстроку и вернет значение, то есть позицию.

Переменной «ПозицияНачало» присваиваем позицию начала шаблона из строки «СтрокаДоговора», используя метод «Найти». Если значение не будет найдено, то позиция будет равна нулю. Если позиция не ноль, то выполним замену.

Удалим из форматированного документа данные между двумя закладками, но при этом закладки нужно получить. Для этого будет использоваться метод «ПолучитьЗакладкуПоПозиции «. Из текста удаляем фрагмент между закладками «ЗакладкаНачало» и «ЗакладкаКонец» и вставляем наименование владельца договора с помощью метода «Вставить».

На следующем видео (2 минуты) вы можете увидеть, как работает, то что мы сделали:

ПОДПИСКА