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