Как автоматически сохранять значения программно созданных реквизитов в настройках управляемой формы 1С?

Программист 1С v8.3 (Управляемые формы)
← На главную

При разработке управляемых форм в 1С:Предприятии 8.3 часто возникает необходимость использовать программно созданные реквизиты. Однако, столкнувшись с задачей сохранения их значений между сеансами работы пользователя, мы обнаруживаем, что для таких реквизитов отсутствует привычный флаг "Сохранение", который доступен для реквизитов, созданных визуально в конфигураторе. Это означает, что механизм автоматического сохранения данных формы в настройках не распространяется на динамически добавленные элементы. В этой статье мы подробно рассмотрим, как обойти это ограничение и реализовать программное сохранение и загрузку значений для ваших реквизитов.

Понимание проблемы: почему нет автоматического сохранения?

Начнем с анализа самой сути проблемы. Система 1С:Предприятие разработана таким образом, что автоматическое сохранение настроек формы (положение окон, значения реквизитов, видимость элементов) ориентировано на предопределенную структуру формы, описанную в конфигураторе. Когда мы программно добавляем реквизиты с помощью методов, таких как НовыйРеквизитФормы, эти реквизиты существуют только в рамках текущего сеанса работы пользователя с формой. Система не "знает" о них на уровне метаданных, и поэтому не может автоматически управлять их сохранением. Именно поэтому флаг "Сохранение" (который мы видим, например, в свойствах реквизитов на закладке "Реквизиты" в конфигураторе) недоступен для программно созданных реквизитов.

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

Основное решение: Программное сохранение и загрузка значений в хранилище настроек

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

Механизм работы строится на следующих принципах (при отладке таких механизмов может пригодиться редактор форм в режиме предприятия — поможет инструментарий разработчика для анализа метаданных):

  1. При открытии формы мы загружаем сохраненные ранее значения из хранилища и присваиваем их нашим программным реквизитам.
  2. При закрытии формы (или в другой подходящий момент) мы собираем текущие значения программных реквизитов и сохраняем их в хранилище.

Шаг 1: Использование функций для работы с хранилищем настроек

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

Важный момент: Мы будем сохранять не отдельные значения реквизитов, а целую структуру, которая будет содержать все значения программных реквизитов, которые мы хотим сохранять. Это упрощает управление и позволяет добавлять новые реквизиты без изменения структуры самого хранилища.

Рассмотрим пример создания структуры для сохранения:


// Пример создания структуры для сохранения
&НаСервере
Функция ПолучитьСтруктуруСохраняемыхДанных()
    СтруктураДанных = Новый Структура;
    
    // Допустим, у нас есть программные реквизиты "МойПрограммныйРеквизит1" и "МойПрограммныйРеквизит2"
    // Мы можем получить их значения из реквизитов формы.
    // Важно: на этом этапе реквизиты уже должны быть добавлены в форму.
    Если ЕстьРеквизит("МойПрограммныйРеквизит1") Тогда
        СтруктураДанных.Вставить("МойПрограммныйРеквизит1", ЭтотОбъект.МойПрограммныйРеквизит1);
    КонецЕсли;
    
    Если ЕстьРеквизит("МойПрограммныйРеквизит2") Тогда
        СтруктураДанных.Вставить("МойПрограммныйРеквизит2", ЭтотОбъект.МойПрограммныйРеквизит2);
    КонецЕсли;
    
    Возврат СтруктураДанных;
КонецФункции

Шаг 2: Определение обработчиков событий формы

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

Загрузка настроек в обработчике ПриСозданииНаСервере

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

  1. Мы должны сначала программно создать наши реквизиты (если они еще не созданы).
  2. Затем загрузить данные из хранилища настроек, используя уникальные ключи.
  3. Проверить, были ли настройки найдены. Если да, присвоить их значения соответствующим программным реквизитам формы.

Рассмотрим пример кода для обработчика ПриСозданииНаСервере:


// В форме, в обработчике ПриСозданииНаСервере
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    // --- 1. Программно создаем реквизиты (если еще не созданы) ---
    // Этот шаг должен выполняться перед попыткой присвоения значений
    СоздатьПрограммныеРеквизиты(); // Предполагается, что эта процедура существует и добавляет реквизиты

    // --- 2. Формируем ключи для хранилища настроек ---
    // КлючОбъектаНастроек обычно совпадает с именем формы или объекта метаданных, к которому относится форма
    Перем КлючОбъектаНастроек;
    Перем КлючНастроек;
    ОпределитьКлючиНастроек(КлючОбъектаНастроек, КлючНастроек);
    
    // --- 3. Загружаем сохраненные данные ---
    СохраненныеДанные = ОбщегоНазначения.ХранилищеНастроекДанныхФормЗагрузить(ЭтотОбъект, КлючОбъектаНастроек, КлючНастроек);
    
    Если СохраненныеДанные <> Неопределено Тогда
        // --- 4. Присваиваем значения программным реквизитам ---
        Если СохраненныеДанные.Свойство("МойПрограммныйРеквизит1") Тогда
            ЭтотОбъект.МойПрограммныйРеквизит1 = СохраненныеДанные.МойПрограммныйРеквизит1;
        КонецЕсли;
        
        Если СохраненныеДанные.Свойство("МойПрограммныйРеквизит2") Тогда
            ЭтотОбъект.МойПрограммныйРеквизит2 = СохраненныеДанные.МойПрограммныйРеквизит2;
        КонецЕсли;
        
        // ... и так далее для всех программных реквизитов
    КонецЕсли;
КонецПроцедуры

// Пример процедуры, которая добавляет программные реквизиты
&НаСервере
Процедура СоздатьПрограммныеРеквизиты()
    МассивРеквизитов = Новый Массив;
    
    // Добавляем реквизит "МойПрограммныйРеквизит1" типа Строка
    ОписаниеРеквизита1 = Новый РеквизитФормы("МойПрограммныйРеквизит1", Новый ОписаниеТипов("Строка"), , "Мой первый реквизит", Истина);
    МассивРеквизитов.Добавить(ОписаниеРеквизита1);
    
    // Добавляем реквизит "МойПрограммныйРеквизит2" типа Число
    ОписаниеРеквизита2 = Новый РеквизитФормы("МойПрограммныйРеквизит2", Новый ОписаниеТипов("Число"), , "Мой второй реквизит", Истина);
    МассивРеквизитов.Добавить(ОписаниеРеквизита2);
    
    // Добавляем реквизиты в форму
    ИзменитьРеквизиты(МассивРеквизитов);
КонецПроцедуры

// Пример процедуры для определения ключей настроек
&НаСервере
Процедура ОпределитьКлючиНастроек(КлючОбъектаНастроек, КлючНастроек)
    // Ключ объекта может быть ИменемФормы
    КлючОбъектаНастроек = ЭтотОбъект.ИмяФормы;
    
    // Ключ настроек может быть более специфичным, например, включать пользователя
    // или какие-то другие параметры, если у одной формы несколько вариантов настроек.
    // Если настройки общие для всех пользователей данной формы, можно просто:
    КлючНастроек = "НастройкиПрограммныхРеквизитов"; 
    
    // Или, если нужны персональные настройки:
    // КлючНастроек = "НастройкиПрограммныхРеквизитов_" + Пользователи.ТекущийПользователь().Имя;
КонецПроцедуры
Сохранение настроек в обработчиках ПриЗакрытии или ПриСохраненииДанныхВНастройкахНаСервере

Для сохранения настроек у нас есть несколько вариантов. Выбор обработчика зависит от ваших требований к синхронности и месту выполнения кода:

  1. ПриЗакрытии (выполняется на клиенте):

    Этот обработчик срабатывает непосредственно перед закрытием формы. Если все значения, которые вы хотите сохранить, доступны на клиенте, вы можете собрать их в структуру и затем вызвать серверную процедуру для сохранения.

    Пример кода:

    
    // В форме, в обработчике ПриЗакрытии
    &НаКлиенте
    Процедура ПриЗакрытии()
        СохранитьНастройкиПрограммныхРеквизитовНаСервере();
    КонецПроцедуры
    
    &НаСервере
    Процедура СохранитьНастройкиПрограммныхРеквизитовНаСервере()
        // --- 1. Собираем значения программных реквизитов в структуру ---
        СтруктураДляСохранения = ПолучитьСтруктуруСохраняемыхДанных(); // Используем уже описанную функцию
        
        // --- 2. Формируем ключи для хранилища настроек ---
        Перем КлючОбъектаНастроек;
        Перем КлючНастроек;
        ОпределитьКлючиНастроек(КлючОбъектаНастроек, КлючНастроек);
        
        // --- 3. Сохраняем данные ---
        ОбщегоНазначения.ХранилищеНастроекДанныхФормСохранить(ЭтотОбъект, КлючОбъектаНастроек, КлючНастроек, СтруктураДляСохранения);
    КонецПроцедуры
    

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

  2. ПриСохраненииДанныхВНастройкахНаСервере (выполняется на сервере):

    Этот обработчик является частью стандартного механизма сохранения настроек формы. Он вызывается на сервере, когда система решает сохранить настройки формы (например, при закрытии формы, если были изменения, или при явном вызове сохранения настроек пользователем).

    Ключевой момент: Чтобы этот обработчик был вызван, необходимо установить свойство формы СохраняемыеВНастройкахДанныеМодифицированы = Истина. Это свойство сигнализирует платформе, что в данных формы произошли изменения, которые нужно сохранить. Установить его можно, например, в обработчике ПриОткрытии, если мы хотим, чтобы настройки сохранялись при каждом закрытии, или при изменении любого из программных реквизитов.

    Пример кода:

    
    // В форме, в обработчике ПриОткрытии
    &НаКлиенте
    Процедура ПриОткрытии(Отказ)
        // Устанавливаем флаг, чтобы при закрытии формы система вызвала ПриСохраненииДанныхВНастройкахНаСервере
        ЭтотОбъект.СохраняемыеВНастройкахДанныеМодифицированы = Истина;
    КонецПроцедуры
    
    // В форме, в обработчике ПриСохраненииДанныхВНастройкахНаСервере
    &НаСервере
    Процедура ПриСохраненииДанныхВНастройкахНаСервере(Настройки)
        // --- 1. Собираем значения программных реквизитов в структуру ---
        СтруктураДляСохранения = ПолучитьСтруктуруСохраняемыхДанных();
        
        // --- 2. Формируем ключи для хранилища настроек ---
        Перем КлючОбъектаНастроек;
        Перем КлючНастроек;
        ОпределитьКлючиНастроек(КлючОбъектаНастроек, КлючНастроек);
        
        // --- 3. Сохраняем данные ---
        ОбщегоНазначения.ХранилищеНастроекДанныхФормСохранить(ЭтотОбъект, КлючОбъектаНастроек, КлючНастроек, СтруктураДляСохранения);
    КонецПроцедуры
    

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

Шаг 3: Идентификация настроек

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

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

Альтернативный подход: Механизм "СохраняемыеДанные"

Помимо хранилища настроек, платформа 1С предоставляет механизм "СохраняемыеДанные" (ManagedFormSavedData), который может быть полезен в более сложных сценариях, когда требуется сохранять данные не просто в настройки формы, а, например, в файл, в таблицу базы данных или передавать между сеансами работы пользователя или даже между информационными базами. Хотя этот механизм и упоминался в контексте флага модифицированности, его основное предназначение — это сериализация произвольных данных.

Принцип работы с СохраняемыеДанные следующий:

  1. Создается экземпляр объекта СохраняемыеДанные.
  2. В него помещаются данные, которые необходимо сохранить (обычно в виде структуры или соответствия).
  3. Данные записываются с помощью метода Записать() в поток или в двоичные данные.
  4. Для загрузки используется метод Прочитать(), который восстанавливает объект из сохраненных данных.

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

Важные особенности и ограничения

При работе с программно создаваемыми реквизитами и их сохранением, следует учитывать несколько нюансов:

  1. Отсутствие автоматики: Как мы уже выяснили, автоматическое сохранение данных формы в настройках (которое включается свойством АвтоматическоеСохранениеДанныхФормыВНастройках) не применимо к программно созданным реквизитам. Колонка "Сохранение" у них недоступна, и все операции приходится выполнять вручную.
  2. Типы данных для сохранения: В данных формы (включая те, что сохраняются в хранилище настроек) могут храниться данные только тех типов, для которых указана возможность XDTO-сериализации. Это означает, что прямое присваивание реквизитам формы значений типа Массив и Соответствие может быть проблематичным. Если вам нужно сохранить такие структуры, их следует преобразовать в сериализуемый вид (например, в ТаблицаЗначений или Структура, содержащую простые типы).
  3. Изменение метаданных: Если вы сохранили значения программных реквизитов, а затем изменили их тип или имя в коде, при последующей загрузке могут возникнуть ошибки или некорректное восстановление значений. Всегда проверяйте наличие свойства в загруженной структуре перед попыткой присвоения значения (как показано в примере с СохраненныеДанные.Свойство(...)). В случае значительных изменений, возможно, придется удалять старые настройки или предусматривать миграцию данных.
  4. Применение программных реквизитов: Программно созданные реквизиты, как правило, используются для модификации типовых решений без изменения конфигурации — для этого есть конструктор произвольных форм и реквизитов для 1С. Это значительно упрощает будущие обновления и предотвращает конфликты с типовой функциональностью. Реализуя собственный механизм сохранения, вы поддерживаете этот принцип, не полагаясь на стандартные механизмы, которые не предназначены для таких динамических элементов.
  5. Табличные части: Для табличных частей объектов также недоступен флажок "Сохранение". Если вам необходимо сохранять данные табличной части, потребуется аналогичная программная реализация с использованием хранилища настроек или других механизмов персистентности.

Заключение

Несмотря на отсутствие прямого флага "Сохранение" для программно созданных реквизитов в управляемых формах 1С, мы имеем мощные инструменты для реализации аналогичного поведения. Путем использования функций общего модуля ОбщегоНазначения.ХранилищеНастроекДанныхФорм... в сочетании с обработчиками событий формы, такими как ПриСозданииНаСервере и ПриСохраненииДанныхВНастройкахНаСервере, мы можем эффективно сохранять и восстанавливать значения наших динамических реквизитов. Помните о необходимости использования уникальных ключей для идентификации настроек и особенностях работы с типами данных. Реализуя этот подход, вы обеспечите стабильность и удобство использования ваших модифицированных форм.

← На главную