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