Переход с обычных форм на управляемые часто вызывает трудности даже у опытных разработчиков. Одной из классических проблем является программное изменение реквизита и ожидание того, что форма «сама догадается» обновить связанные с ним элементы интерфейса (например, скроет поля или заблокирует кнопки). Разберем подробно, почему это не происходит автоматически и как правильно организовать взаимодействие между данными и интерфейсом в среде управляемого приложения.
Проанализируем ключевое отличие управляемых форм: событие ПриИзменении, привязанное к полю ввода на форме, срабатывает только при интерактивном изменении данных пользователем. Если вы программно присваиваете значение реквизиту в коде (например, Объект.Статус = Перечисления.Статусы.Завершено;), клиентское событие ПриИзменении не вызовется. Это сделано для оптимизации производительности и предотвращения бесконечных рекурсивных вызовов.
Следовательно, если за изменением реквизита должна следовать логика визуального обновления (скрытие групп, изменение доступности), разработчик обязан вызвать эту логику явно. Рассмотрим, как это сделать правильно.
Многие начинающие разработчики полагают, что на сервере невозможно управлять визуальным состоянием формы. Это заблуждение. Если процедура объявлена с директивой &НаСервере (контекстный вызов), ей доступен весь контекст формы, включая коллекцию Элементы.
Рассмотрим пример ситуации, когда при изменении типа договора нам нужно скрыть поле «Срок рассрочки» прямо из серверного кода:
&НаСервере
Процедура ИзменитьТипДоговораНаСервере()
// Изменяем данные
Объект.ТипДоговора = ПредопределенноеЗначение("Перечисление.ТипыДоговоров.Кредитный");
// Прямое управление интерфейсом через контекст формы
Элементы.СрокРассрочки.Видимость = (Объект.ТипДоговора = Перечисления.ТипыДоговоров.Кредитный);
КонецПроцедуры
В данном случае, после завершения серверной процедуры и возврата управления на клиент, платформа автоматически применит изменения в коллекции Элементы и пользователь увидит обновленный интерфейс. Это самый простой и прямой путь.
Чтобы не дублировать код в разных частях формы (в обработчиках событий и серверных процедурах), разберем стандарт «хорошего тона» в разработке 1С. Мы создаем единую процедуру управления состоянием формы, которую вызываем отовсюду.
Посмотрим на пример структуры модуля формы:
&НаКлиенте
Процедура ПриИзмененииРеквизита(Элемент)
// Вызываем серверную логику, если нужны данные из БД
ОбработатьИзменениеНаСервере();
// Обновляем визуальное состояние
УстановитьСостояниеФормы();
КонецПроцедуры
&НаСервере
Процедура УстановитьСостояниеФормы()
// Единое место для управления видимостью и доступностью
ЛогикаДоступности = (Объект.Сумма > 100000);
Элементы.ГруппаСогласование.Видимость = ЛогикаДоступности;
Элементы.ПричинаОтклонения.ТолькоПросмотр = Не ЛогикаДоступности;
КонецПроцедуры
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
// Вызываем ту же логику при открытии формы
УстановитьСостояниеФормы();
КонецПроцедуры
Такой подход позволяет избежать ситуации, когда при интерактивном вводе форма ведет себя одним образом, а при программном заполнении — другим.
Выясним причину, по которой иногда нужно обновить форму, если данные изменились «извне» (например, в другой открытой форме или в результате работы фонового задания). Если одна форма изменила объект в базе данных, другие открытые формы этого объекта об этом не узнают мгновенно.
Для решения этой задачи проанализируем механизм ОповеститьОбИзменении. Этот метод глобального контекста посылает сигнал системе о том, что данные определенного типа или конкретная ссылка были изменены.
Пример использования:
ОповеститьОбИзменении(СсылкаНаОбъект);ОбработкаОповещения на клиенте.Если нам нужно передать конкретную команду из одной формы в другую (например, из формы подбора в форму документа), используем пару Оповестить() и ОбработкаОповещения. Это позволяет избежать жестких ссылок между объектами.
Рассмотрим пример кода в форме-источнике:
&НаКлиенте
Процедура ВыполнитьДействие()
ПараметрыСобытия = Новый Структура("ИмяСобытия, Значение", "ОбновитьСкидки", 10);
Оповестить("ДействиеЗавершено", ПараметрыСобытия, ЭтаФорма);
КонецПроцедуры
И в форме-приемнике:
&НаКлиенте
Процедура ОбработкаОповещения(ИмяСобытия, Параметр, Источник)
Если ИмяСобытия = "ДействиеЗавершено" И Параметр.ИмяСобытия = "ОбновитьСкидки" Тогда
// Здесь мы можем вызвать серверную процедуру для пересчета данных
ОбновитьДанныеНаСервере(Параметр.Значение);
КонецЕсли;
КонецПроцедуры
Важно помнить, что каждый контекстный вызов &НаСервере передает на сервер все данные формы (весь Объект и другие реквизиты). Если форма очень «тяжелая», это замедляет работу приложения.
Проанализируем ситуацию: если для изменения видимости нам не нужно обращаться к базе данных, а достаточно простых расчетов, лучше выполнить это полностью на клиенте. Если же данные для логики интерфейса нужно получить с сервера, используйте &НаСервереБезКонтекста. В этом случае функция вернет структуру с правилами (например, Результат.ВидимостьПоляХ = Истина), и вы примените эти настройки на клиенте.
Подведем итог: для сообщения форме об изменении реквизита в управляемом приложении используйте либо прямой контекстный вызов &НаСервере с обращением к коллекции Элементы, либо архитектурный подход с выносом визуальной логики в отдельную процедуру, вызываемую после программного изменения данных.