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