Как решить проблему с доступностью заблокированной формы и повторным срабатыванием внешних событий в 1С 8.3?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

Переход от старого модального режима работы интерфейса к асинхронным вызовам в управляемых формах 1С:Предприятия 8.3 принес разработчикам множество преимуществ, но вместе с тем добавил неочевидных проблем. Одной из типичных трудностей является «недостаточная» блокировка формы-владельца при открытии вспомогательного (блокирующего) окна.

Разработчики часто сталкиваются с двумя неприятными симптомами:

  1. Пользователь может перемещаться по элементам заблокированной формы-владельца с помощью клавиши Tab и даже вводить в них данные.
  2. Внешние события (например, сканирование штрихкода) прорываются через блокировку, вызывая повторное открытие окон или некорректную логику обработки. Особенно остро это проявляется, когда один и тот же штрихкод должен закрывать блокирующее окно и тут же обрабатываться формой-владельцем.

В этой статье мы подробно разберем природу этих проблем и рассмотрим надежные технические решения для их устранения.

Часть 1. Почему заблокированная форма остается частично доступной?

Рассмотрим стандартный механизм блокировки. Когда мы открываем форму немодально, мы передаем в параметры открытия режим блокировки. Платформа предлагает два варианта блокировки:

Если мы используем режим блокировки окна владельца, то на уровне операционной системы и оконного менеджера платформы 1С фокус ввода может вести себя нестабильно (особенно в тонком клиенте Windows). При нажатии клавиши Tab фокус может «просачиваться» на элементы формы-владельца. Пользователь получает возможность активировать текстовые поля и даже редактировать их.

Выясним, как это исправить.

Решение 1. Изменение режима блокировки

Самый простой и правильный способ полностью исключить навигацию по родительской форме — использовать блокировку всего интерфейса. В этом случае операционная система жестко перехватывает фокус ввода на активное окно.

Посмотрим на пример открытия формы с полной блокировкой интерфейса:


ПараметрыФормы = Новый Структура;
ПараметрыФормы.Вставить("РежимОткрытияОкна", РежимОткрытияОкнаФормы.БлокироватьВесьИнтерфейс);

ОписаниеОповещения = Новый ОписаниеОповещения("ПослеЗакрытияОкнаПредупреждения", ЭтотОбъект);

ОткрытьФорму("ОбщаяФорма.Предупреждение", ПараметрыФормы, ЭтотОбъект, , , , ОписаниеОповещения);

Решение 2. Программный перевод формы-владельца в режим просмотра

Если бизнес-логика требует заблокировать только одно конкретное окно, оставив остальные части интерфейса доступными, мы можем программно ограничить ввод на форме-владельце перед открытием блокирующего окна:


&НаКлиенте
Процедура ОткрытьОкноЗапросаДанных()
    // Делаем форму-владельца временно доступной только для чтения
    ЭтотОбъект.ТолькоПросмотр = Истина;
    
    ПараметрыФормы = Новый Структура;
    ПараметрыФормы.Вставить("РежимОткрытияОкна", РежимОткрытияОкнаФормы.БлокироватьОкноВладельца);
    
    ОписаниеОповещения = Новый ОписаниеОповещения("ПослеЗакрытияОкна", ЭтотОбъект);
    ОткрытьФорму("ОбщаяФорма.ВводЗначения", ПараметрыФормы, ЭтотОбъект, , , , ОписаниеОповещения);
КонецПроцедуры

&НаКлиенте
Процедура ПослеЗакрытияОкна(РезультатЗакрытия, ДополнительныеПараметры) Экспорт
    // Возвращаем доступность формы в исходное состояние
    ЭтотОбъект.ТолькоПросмотр = Ложь;
    
    Если РезультатЗакрытия <> Неопределено Тогда
        // Обработка полученного результата
    КонецЕсли;
КонецПроцедуры

---

Часть 2. Борьба с «двойным» сканированием и очередью событий

Проанализируем более сложную проблему: бесконечный цикл открытия окон при сканировании штрихкодов. Сценарий выглядит так:

  1. Пользователь сканирует штрихкод товара на основной форме.
  2. Программа обнаруживает ошибку/предупреждение и открывает блокирующее окно (например: «Подтвердите действие штрихкодом администратора»).
  3. Пользователь сканирует управляющий штрихкод. Блокирующее окно считывает штрихкод, закрывается и разблокирует форму-владельца.
  4. Но так как событие сканирования штрихкода в операционной системе генерирует сигналы асинхронно, форма-владелец сразу после активации перехватывает остатки данных или повторно инициированное событие и открывает то же самое блокирующее окно заново.

Это происходит потому, что события от торгового оборудования выстраиваются в очередь сообщений. Как только форма-владелец становится активной, метод ВводДоступен() возвращает Истина, и накопившееся событие передается в форму.

Рассмотрим эффективные методы решения этой проблемы.

Решение 1. Использование обработчика ожидания для закрытия формы (рекомендуется)

Чтобы избежать мгновенной передачи фокуса форме-владельцу в момент получения внешнего события, закроем блокирующую форму через минимальный таймаут. За это время очередь событий операционной системы и драйвера торгового оборудования успеет очиститься.

Посмотрим на пример реализации в коде блокирующей формы:


&НаКлиенте
Процедура ВнешнееСобытие(Источник, Событие, Данные)
    
    Если ПроверитьУправляющийШтрихкод(Данные) Тогда
        // Вместо мгновенного Закрыть() подключаем обработчик ожидания
        // Интервал 0.1 секунды достаточен для очистки буфера сообщений
        ПодключитьОбработчикОжидания("ВыполнитьАсинхронноеЗакрытие", 0.1, Истина);
    КонецЕсли;
    
КонецПроцедуры

&НаКлиенте
Процедура ВыполнитьАсинхронноеЗакрытие()
    Закрыть(Истина);
КонецПроцедуры

Решение 2. Проверка доступности ввода с фильтрацией времени

В модуле формы-владельца мы можем внедрить проверку «дребезга» событий. Для этого будем запоминать точное системное время закрытия блокирующей формы и игнорировать любые внешние события, пришедшие в течение короткого промежутка времени (например, 500 миллисекунд) после этого момента.

Разберем реализацию этого алгоритма:


&НаКлиенте
Перем ВремяПоследнегоЗакрытияБлокировки;

&НаКлиенте
Процедура ВнешнееСобытие(Источник, Событие, Данные)
    
    // Проверяем доступность ввода на форме
    Если Не ВводДоступен() Тогда
        Возврат;
    КонецЕсли;
    
    // Защита от дребезга и ложных срабатываний после закрытия модальных окон
    Если ВремяПоследнегоЗакрытияБлокировки <> Неопределено Тогда
        ТекущееВремя = ТекущаяУниверсальнаяДатаВМиллисекундах();
        Если (ТекущееВремя - ВремяПоследнегоЗакрытияБлокировки) < 500 Тогда
            // Игнорируем штрихкод, так как он пришел сразу после закрытия окна
            Возврат;
        КонецЕсли;
    КонецЕсли;
    
    // Обработка полезного штрихкода
    ОбработатьШтрихкод(Данные);
    
КонецПроцедуры

&НаКлиенте
Процедура ПослеЗакрытияОкнаПредупреждения(РезультатЗакрытия, ДополнительныеПараметры) Экспорт
    // Фиксируем время закрытия окна в миллисекундах
    ВремяПоследнегоЗакрытияБлокировки = ТекущаяУниверсальнаяДатаВМиллисекундах();
КонецПроцедуры

Решение 3. Перевод оборудования из режима клавиатуры в COM-порт

Если ваш сканер штрихкодов работает в режиме эмуляции клавиатуры (Keyboard Wedge), он отправляет данные посимвольно. Это главная причина рассинхронизации событий. Перевод оборудования в режим работы через виртуальный COM-порт (или NativeAPI-драйвер БПО) решает проблему концептуально. В таком режиме штрихкод посылается в систему единым целостным пакетом-событием, адресованным строго активному окну.

Резюме

Для создания надежного и стабильного интерфейса при работе со сканерами штрихкодов и блокирующими окнами придерживайтесь следующих правил:

  1. Используйте РежимОткрытияОкнаФормы.БлокироватьВесьИнтерфейс для гарантированного отключения клавиатурной навигации на заблокированных формах.
  2. Если используете блокировку окна-владельца, временно переводите владельца в режим ТолькоПросмотр = Истина.
  3. Для закрытия окон по внешнему событию всегда используйте кратковременный обработчик ожидания (ПодключитьОбработчикОжидания с интервалом 0.1), чтобы дать платформе и операционной системе время очистить очереди ввода до активации предыдущей формы.
← На главную