При разработке собственных редакторов печатных форм, этикеток или ценников в режиме предприятия на обычных формах (ОФ) разработчики часто сталкиваются со следующей проблемой: при попытке вывести предопределенный макет в элемент управления ПолеТабличногоДокумента на форме стандартным методом Вывести(), все параметры и шаблоны исчезают. Вместо привычных угловых скобок <ИмяПараметра> или квадратных скобок в шаблонах пользователь видит пустые ячейки.
В этой статье мы подробно разберем, почему это происходит, проанализируем внутренние механизмы работы платформы 1С:Предприятие 8 с табличными документами и рассмотрим несколько надежных практических способов реализации полноценного редактора макетов на обычных формах.
Для начала проанализируем поведение платформы. Метод Вывести() (а также аналогичный ему метод Присоединить()) предназначен для генерации готового к печати документа. Когда мы вызываем этот метод, платформа запускает встроенный механизм компоновки табличного документа. Она берет область-источник и пытается сопоставить каждый найденный в ней параметр с реальным значением из структуры параметров.
Если параметры не были заполнены программно перед выводом, платформа по умолчанию заменяет их пустыми строками. В результате вместо шаблона макета мы получаем пустой бланк, в котором пользователь физически не может увидеть, где и какие параметры должны находиться.
Для создания полноценного визуального редактора макетов нам нужно обойти этот стандартный механизм рендеринга и загрузить структуру макета в элемент формы напрямую в "сыром" виде. Разберем рабочие варианты решения этой задачи.
Это простой и самый надежный способ, который позволяет передать структуру макета со всеми свойствами ячеек (включая параметры и шаблоны) напрямую в элемент формы. Суть метода заключается в том, что мы сначала записываем макет во временный файл на диске в формате MXL, а затем считываем его в наше поле табличного документа.
Рассмотрим по шагам программный код для реализации этого подхода:
// 1. Получаем предопределенный макет из конфигурации
МакетИсточника = ПолучитьМакет("Макет");
// 2. Формируем путь к временному файлу на диске
ПутьКФайлу = КаталогВременныхФайлов() + "ВременныйМакет.mxl";
// 3. Записываем макет во временный файл
МакетИсточника.Записать(ПутьКФайлу);
// 4. Считываем файл напрямую в ПолеТабличногоДокумента на форме
ЭлементыФормы.ПолеТабличногоДокумента1.Прочитать(ПутьКФайлу);
// 5. Переводим табличный документ в режим макета для отображения параметров
ЭлементыФормы.ПолеТабличногоДокумента1.Макет = Истина;
// 6. Обязательно удаляем временный файл после чтения
Попытка
УдалитьФайлы(ПутьКФайлу);
Исключение
// Обработка возможных ошибок блокировки файла операционной системой
КонецПопытки;
После выполнения этого кода в элементе формы отобразится макет в том же виде, как в конфигураторе: с разметкой параметров и шаблонов. Однако запись на диск — это дисковая операция, которая при частых вызовах может снизить производительность или вызвать ошибки прав доступа в некоторых сетевых окружениях. Поэтому рассмотрим более элегантный способ.
Мы можем обойтись без использования временных файлов на диске. Для этого перенесем структуру ячеек из макета-источника в поле табличного документа в оперативной памяти.
Критически важный нюанс: если мы воспользуемся методом ПолучитьОбласть(), платформа все равно инициализирует обработку параметров. Чтобы этого избежать, мы должны обращаться к ячейкам макета напрямую по их координатам через свойство Область().
Давайте проанализируем пример кода, реализующий этот алгоритм:
// Получаем макет-источник
ТабДокИсточник = ПолучитьМакет("ШаблонПоУмолчанию");
ТабличныйДокументПриемник = ЭлементыФормы.ТабличныйДокумент;
// Очищаем приемник перед выводом
ТабличныйДокументПриемник.Очистить();
// Определяем границы заполненной области в макете-источнике
Верх = 1;
Лево = 1;
Низ = ТабДокИсточник.ВысотаТаблицы;
Право = ТабДокИсточник.ШиринаТаблицы;
// Если макет не пустой, выполняем копирование областей
Если Низ > 0 И Право > 0 Тогда
// Получаем точные координатные области источника и приемника
ОбластьИсточника = ТабДокИсточник.Область(Верх, Лево, Низ, Право);
ОбластьПриемника = ТабличныйДокументПриемник.Область(Верх, Лево, Низ, Право);
// Вставляем область со всеми метаданными без смещения
ТабличныйДокументПриемник.ВставитьОбласть(
ОбластьИсточника,
ОбластьПриемника,
ТипСмещенияТабличногоДокумента.БезСмещения,
Ложь
);
КонецЕсли;
// Активируем режим макета для отображения служебных символов параметров
ТабличныйДокументПриемник.Макет = Истина;
Этот способ работает исключительно в оперативной памяти компьютера, выполняется мгновенно и не требует взаимодействия с файловой системой.
Чтобы превратить обычное ПолеТабличногоДокумента на форме в полноценный конструктор макетов, одной установки свойства Макет = Истина недостаточно. Нам необходимо настроить комбинацию свойств, которые позволят пользователю интерактивно редактировать данные.
Обязательно установите следующие свойства программно или в палитре свойств элемента формы:
Макет = Истина — активирует специальный режим отрисовки табличного документа. Параметры и шаблоны начинают визуально выделяться спецсимволами, а на полях документа отображаются именованные области.Редактирование = Истина — разрешает интерактивное изменение ячеек пользователем прямо на форме.ТолькоПросмотр = Ложь — снимает ограничение на изменение структуры таблицы, изменение высоты строк и ширины колонок.При такой комбинации настроек пользователю в режиме предприятия при нажатии правой кнопки мыши на ячейке будет доступно стандартное контекстное меню и окно "Свойства". В этом окне пользователь сможет интерактивно изменить тип заполнения ячейки (переключить между "Текст", "Параметр" и "Шаблон") и самостоятельно написать имя нового параметра.
Когда пользователь завершит редактирование макета в режиме предприятия, нам необходимо сохранить результат его работы в базе данных.
Для хранения измененных макетов обычно создают справочник (например, ПользовательскиеМакеты), в котором присутствует реквизит с типом ХранилищеЗначений. Алгоритм сохранения выглядит следующим образом:
// Подготавливаем табличный документ к сохранению
// Рекомендуется временно отключить режим макета, чтобы не сохранять лишние служебные свойства редактора
ЭлементыФормы.ТабличныйДокумент.Макет = Ложь;
// Помещаем объект "ТабличныйДокумент" в ХранилищеЗначений
НовыйМакетХранилище = Новый ХранилищеЗначений(ЭлементыФормы.ТабличныйДокумент.Получить());
// Записываем в справочник
СправочникОбъект = Справочники.ПользовательскиеМакеты.СоздатьЭлемент();
СправочникОбъект.Наименование = "Мой измененный макет";
СправочникОбъект.ДанныеМакета = НовыйМакетХранилище;
СправочникОбъект.Записать();
При последующей печати документов система должна сначала проверить наличие пользовательского макета в этом справочнике. Если он найден, то вместо вызова предопределенного макета из конфигурации ПолучитьМакет("ИмяМакета"), мы извлекаем сохраненный табличный документ из реквизита справочника ДанныеМакета.Получить(), находим нужные области через ПолучитьОбласть(), заполняем параметры программно и выводим на печать стандартным способом.
Если вы предоставили пользователям возможность самостоятельно редактировать макеты, они могут добавлять туда новые параметры, о которых код конфигуратора "не знает". Чтобы избежать ошибок при формировании печатной формы, мы можем перед печатью программно проанализировать табличный документ и узнать, какие параметры в нем присутствуют.
Поскольку стандартного метода получения списка параметров у объекта ТабличныйДокумент нет, применим алгоритм обхода ячеек по координатам:
СписокПараметров = Новый СписокЗначений;
Для Строка = 1 По ТабличныйДокумент.ВысотаТаблицы Цикл
Для Колонка = 1 По ТабличныйДокумент.ШиринаТаблицы Цикл
Ячейка = ТабличныйДокумент.Область(Строка, Колонка);
// Проверяем тип заполнения ячейки
Если Ячейка.Заполнение = ТипЗаполненияОбластиТабличногоДокумента.Параметр Тогда
Если ЗначениеЗаполнено(Ячейка.Текст) Тогда
СписокПараметров.Добавить(Ячейка.Текст);
КонецЕсли;
ИначеЕсли Ячейка.Заполнение = ТипЗаполненияОбластиТабличногоДокумента.Шаблон Тогда
// Из шаблона вида "[Контрагент] договор № [Номер]" нужно вырезать параметры регулярным выражением или поиском подстроки
// Добавляем найденные параметры в общий список
КонецЕсли;
КонецЦикла;
КонецЦикла;
Используя этот список, вы сможете гибко сопоставить новые параметры с реквизитами объектов конфигурации или вывести пользователю форму сопоставления полей перед началом печати.