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