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