Описание задачи
На формах есть событие ОбработкаОповещения. Может возникнуть ситуация, когда нам требуется создать глобальную обработку оповещения. Чтобы она не была привязана к какой-то форме. Для примера возьмем случай, когда мы хотим независимо от открытых форм обрабатывать результат сканирования штрихкода. Обработка этого результата согласно документации Библиотеки Подключаемого Оборудования (Далее "БПО") происходит в обработчике события ОбработкаОповещения формы, подключенной к БПО.
Решение
Очевидно, нам нужна форма, т.к. только в контексте формы мы сможем использовать возможности БПО. Но если мы создадим служебную форму, роль которой будет только обрабатывать оповещения, то она будет висеть в пользовательском интерфейсе, мешаться. Наша цель сделать все максимально незаметно для пользователя.
Создадим форму, которая будет жить в контексте сеанса (приложения 1С) и обрабатывать оповещения, но открывать её не будем.
Для этого в конфигурации на основе библиотеки стандартных подсистем (Далее "БСП") расширим метод ОбщегоНазначенияКлиентПереопределяемый.ПриНачалеРаботыСистемы
&После("ПриНачалеРаботыСистемы")
Процедура расш_ПриНачалеРаботыСистемы(Параметры)
ОбработчикВводаСканераШтрихкодов = ПолучитьФорму("ОбщаяФорма.расш_ОбработчикВводаСканераШтрихкодов");
// ПараметрыПриложения - в БСП это экспортная переменная модуля приложения с типом Соответствие
ПараметрыПриложения.Вставить("ОбработчикВводаСканераШтрихкодов", ОбработчикВводаСканераШтрихкодов);
// Инициализируем работу с оборудованием. Этот код по документации необходимо помещать в обработчике ПриОткрытии.
// Но он работает даже если не открывать форму, а только создать.
МенеджерОборудованияКлиент.НачатьПодключениеОборудованиеПриОткрытииФормы(Неопределено, ОбработчикВводаСканераШтрихкодов, "СканерШтрихкода");
КонецПроцедуры
В нашем примере мы будем использовать расширение с префиксом "расш_"
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
ТипыПодключаемогоОборудования = Новый Массив;
ТипыПодключаемогоОборудования.Добавить(Перечисления.ТипыПодключаемогоОборудования.СканерШтрихкода);
ИспользоватьПодключаемоеОборудование = ОбщегоНазначенияУТ.ИспользоватьПодключаемоеОборудование(ТипыПодключаемогоОборудования);
КонецПроцедуры
&НаКлиенте
Процедура ОбработкаОповещения(ИмяСобытия, Параметр, Источник)
Если Источник = "ПодключаемоеОборудование" Тогда
Если ИмяСобытия = "ScanData"
И МенеджерОборудованияУТКлиент.ЕстьНеобработанноеСобытие()
И Не АктивнаФормаСоСканеромШтрихкодов() Тогда
ОбработатьШтрихкоды(МенеджерОборудованияУТКлиент.ПреобразоватьДанныеСоСканераВСтруктуру(Параметр));
КонецЕсли;
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура ОбработатьШтрихкоды(Данные)
Если Не ШтрихкодированиеНоменклатурыКлиент.ШтрихкодыВалидны(Данные) Тогда
Возврат;
КонецЕсли;
МассивСсылок = ШтрихкодированиеПечатныхФормКлиент.ПолучитьСсылкуПоШтрихкодуТабличногоДокумента(Данные.Штрихкод);
Если МассивСсылок.Количество() > 0 Тогда
ПоказатьЗначение(Неопределено, МассивСсылок[0]);
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Функция АктивнаФормаСоСканеромШтрихкодов()
АктивноеОкно = АктивноеОкно();
Если АктивноеОкно = Неопределено Тогда
Возврат Ложь;
КонецЕсли;
Для каждого АктивнаяФорма Из АктивноеОкно.Содержимое Цикл
Если СуществуютРеквизитыНаФорме(АктивнаяФорма, "ИспользоватьПодключаемоеОборудование, ПоддерживаемыеТипыПодключаемогоОборудования")
И АктивнаяФорма.ИспользоватьПодключаемоеОборудование
И СтрНайти(АктивнаяФорма.ПоддерживаемыеТипыПодключаемогоОборудования, "СканерШтрихкода") <> 0 Тогда
Возврат Истина;
КонецЕсли;
КонецЦикла;
Возврат Ложь;
КонецФункции
&НаКлиенте
Функция СуществуютРеквизитыНаФорме(Форма, ИменаРеквизитов)
МассивРеквизитов = СтрРазделить(ИменаРеквизитов,",", Ложь);
ЗначенияСвойств = Новый Структура(ИменаРеквизитов);
Для Каждого ИмяРеквизита Из МассивРеквизитов Цикл
ЗначенияСвойств.Вставить(ИмяРеквизита, Null);
КонецЦикла;
ЗаполнитьЗначенияСвойств(ЗначенияСвойств, Форма);
Для Каждого КлючЗначение Из ЗначенияСвойств Цикл
Если КлючЗначение.Значение = Null Тогда
Возврат Ложь;
КонецЕсли;
КонецЦикла;
Возврат Истина;
КонецФункции
Думаю, что суть глобального обработчика оповещений приведенным выше кодом мы раскрыли. А вот детали реализации работы именно со сканером штрихкода, думаю, нужно раскрыть детальнее, чтобы было понимание, что и зачем происходит.
Весь этот код вы можете найти в любых формах, которые умеют работать со штрихкодом - форма списка документа "Заказ клиента", табличная часть товаров документа "Заказ клиента" и т.д.
Конкретно данный код открывает объект (документ, справочник и т.д.) по его штрихкоду, который, как правило, добавляется в шапку печатной формы (см. ШтрихкодированиеПечатныхФорм.ВывестиШтрихкодВТабличныйДокумент).
Проблема в том, что у нас может быть открыто множество форм, поддерживающих сканирование штрихкодов. И, как правило, в обработчиках оповещений этих форм стоит подобная проверка:
&НаКлиенте
Процедура ОбработкаОповещения(ИмяСобытия, Параметр, Источник)
...
Если Источник = "ПодключаемоеОборудование" И ВводДоступен() Тогда
// Работа со штрихкодом
КонецЕсли;
...
КонецПроцедуры
То есть используется метод формы "ВводДоступен()", отвечающий на вопрос, активна ли текущая форма. Но наша форма расш_ОбработчикВводаСканераШтрихкодов никогда не будет открыта, и соответственно никогда не будет активна. И такую проверку мы делать не можем и не будем, т.к. хотим, чтобы сканер работал всегда и везде.
Так же обратим внимание на функцию МенеджерОборудованияУТКлиент.ЕстьНеобработанноеСобытие(). Как только мы что-то отсканировали нашим сканером - конфигурация зафиксировала, что произошло это "событие". И как только мы обработали отсканированные данные, конфигурация фиксирует, что событие обработано. Благодаря этой проверке не допускается обработка одного и того же события разными формами.
- Возникновение события сканирования
- Обработка данных
- Событие отмечается обработанным
Мы понимаем, что последовательность вызова события ОбработкаОповещения у разных форм неопределенная. Будем считать, что она случайна. Поэтому если во всех этих формах используется проверка МенеджерОборудованияУТКлиент.ЕстьНеобработанноеСобытие(), то будет действовать правило "Кто первый встал, того и тапки". Если первым оповещение сработало в нашем глобальном обработчике, то наш алгоритм сработает, а алгоритм открытой формы, работающий со штрихкодами, нет. И наоборот. Наша задача поставить в приоритет "локальные" алгоритмы текущей открытой формы, а не нашего глобального обработчика.
Для этого среди активных форм находим те, которые поддерживают работу со штрихкодами (см. АктивнаФормаСоСканеромШтрихкодов()). И если такие формы нашлись - отменяем наш глобальный обработчик, и отдаем таким образом эстафету по обработке штрихкода активной форме.
Итог
Мы разобрали возможность создания глобального обработчика оповещений и использовали при этом интересный подход, когда форма живет в переменной модуля управляемого приложения и при этом не открывается. По сути мы создали объект на стороне клиента со своим состоянием, методами (к которым мы сможем обращаться, если сделаем их экспортными), живущий все время от открытия приложения 1С до его закрытия. Возможности подобного метода использования форм почти безграничны и не ограничиваются одним лишь "глобальным обработчиком оповещений".
Используемые при написании статьи версии конфигураций и платформы:
- Управление торговлей, редакция 11 (11.5.21.106)
- СтандартныеПодсистемы (БСП) 3.1.10.467
- БиблиотекаПодключаемогоОборудования (БПО) 3.2.6.8
- 1С:Предприятие 8.3 (8.3.25.1546)