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