Почему при вызове ПолучитьФорму повторно создается управляемая форма и как это исправить

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

При разработке интерфейсных механизмов, таких как дисплей покупателя в рабочем месте кассира (РМК), часто требуется обращаться к одному и тому же экземпляру общей формы. Стандартный механизм платформы 1С:Предприятие 8.3 предполагает использование методов ПолучитьФорму() или ОткрытьФорму() с указанием пользовательского ключа уникальности или объекта типа УникальныйИдентификатор. По документации платформа должна находить уже открытую форму и возвращать ссылку на нее. Однако на практике разработчики сталкиваются с ситуацией, когда вместо возврата существующего окна открывается новое дублирующее окно.

Разберем причины такого поведения платформы и рассмотрим надежные способы работы с единственным экземпляром формы.

Причина поведения платформы: изоляция модального контекста

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

Главная причина создания дубликатов заключается в модальном или блокирующем контексте вызова. Если вызов функции ПолучитьФорму() или ОткрытьФорму() происходит из модального окна либо из формы, открытой в режиме БлокироватьВесьИнтерфейс или БлокироватьОкноВладельца (что характерно для окон регистрации продаж, меню и диалогов РМК), платформа изолирует область поиска. Поиск выполняется исключительно внутри текущего модального слоя. Формы, открытые в основном сеансе или в независимых окнах, становятся недоступными для стандартного сопоставления, и 1С принудительно инициализирует новый экземпляр.

Способ 1. Архитектурная изоляция через механизм оповещений (Рекомендуемый)

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

Разберем реализацию по шагам:

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

Такой подход полностью устраняет зависимость от модального стека и не создает дублирующих окон при любых контекстах вызова.

Способ 2. Поиск открытой формы через коллекцию окон клиентского приложения

Если в коде критически важно получить прямую ссылку на клиентский объект формы, обойти ограничение поиска по ключу уникальности можно с помощью перебора глобальной коллекции открытых окон через ПолучитьОкна().

Рассмотрим пример функции поиска и получения формы:


&НаКлиенте
Функция ПолучитьЭкземплярФормыДисплея() Экспорт
    
    ФормаДисплея = Неопределено;
    
    Для Каждого Окно Из ПолучитьОкна() Цикл
        Для Каждого ФормаОкна Из Окно.Содержимое Цикл
            Если ФормаОкна.ИмяФормы = "ОбщаяФорма.ФормаДисплеяПокупателя" Тогда
                ФормаДисплея = ФормаОкна;
                Прервать;
            КонецЕсли;
        КонецЦикла;
        
        Если ФормаДисплея <> Неопределено Тогда
            Прервать;
        КонецЕсли;
    КонецЦикла;
    
    Если ФормаДисплея = Неопределено Тогда
        ФормаДисплея = ОткрытьФорму("ОбщаяФорма.ФормаДисплеяПокупателя", , , "ДисплейПокупателя", ВариантОткрытияОкна.ОтдельноеОкно);
    КонецЕсли;
    
    Возврат ФормаДисплея;
    
КонецФункции

Метод ПолучитьОкна() возвращает все окна приложения независимо от того, в каком модальном слое выполняется текущий программный код, что гарантирует нахождение ранее созданного экземпляра.

Способ 3. Отвязка вызова от модального контекста через обработчик ожидания

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

Посмотрим на пример реализации:


&НаКлиенте
Процедура ИнициализироватьДисплейИзМодальногоОкна()
    // Подключаем однократный обработчик с минимальной задержкой
    ПодключитьОбработчикОжидания("ВыполнитьОткрытиеДисплея", 0.1, Истина);
КонецПроцедуры

&НаКлиенте
Процедура ВыполнитьОткрытиеДисплея() Экспорт
    ОткрытьФорму("ОбщаяФорма.ФормаДисплеяПокупателя", , , "ДисплейПокупателя", ВариантОткрытияОкна.ОтдельноеОкно);
КонецПроцедуры

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

← На главную