Как вывести окно вопроса на передний план при работе с заблокированной формой-владельцем

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

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

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

Анализируем причину возникновения проблемы

Для начала разберем механику взаимодействия окон. В режиме «Формы в закладках» (MDI) платформа управляет иерархией окон иначе, чем в современном интерфейсе «Такси». Когда мы открываем форму подбора с параметром РежимОткрытияОкнаФормы.БлокироватьОкноВладельца, создается жесткая связь. Основное окно (реализация) становится недоступным для взаимодействия, а форма подбора становится активным слоем.

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

Методы ЭтаФорма.Активизировать() или ЭтаФорма.Окно.Активизировать() в данном случае бесполезны, так как платформа игнорирует попытки программно передать фокус заблокированному окну.

Решение №1: Изменение режима блокировки интерфейса

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

Рассмотрим, как это реализовать в коде вызова формы подбора:


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

Преимущество: Это гарантированно решает проблему «проваливания» окон в старых интерфейсах 1С.

Недостаток: Пользователь не сможет переключиться на другие открытые закладки (например, посмотреть отчет), пока открыта форма подбора.

Решение №2: Перенос вызова вопроса в форму подбора

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

Рассмотрим схему взаимодействия:

  1. В форме подбора пользователь выбирает товар.
  2. Вместо немедленного закрытия или отправки данных, форма подбора делает «запрос проверки» владельцу (через Оповестить или вызов экспортного метода).
  3. Владелец сообщает: «Товар уже есть в списке, нужно подтверждение».
  4. Форма подбора, получив этот сигнал, сама вызывает ПоказатьВопрос.

Посмотрим на пример реализации в форме подбора:


&НаКлиенте
Процедура СписокВыбор(Элемент, ВыбраннаяСтрока, Поле, СтандартнаяОбработка)
    СтандартнаяОбработка = Ложь;
    ДанныеТовара = Элементы.Список.ТекущиеДанные;
    
    // Проверяем наличие товара в документе-владельце
    Если ВладелецФормы.ТоварУжеЕстьВДокументе(ДанныеТовара.Ссылка) Тогда
        // Окно вопроса вызывается из АКТИВНОЙ формы подбора
        ОписаниеОповещения = Новый ОписаниеОповещения("ПослеОтветаНаВопрос", ЭтаФорма, ДанныеТовара);
        ПоказатьВопрос(ОписаниеОповещения, "Такой товар уже есть. Увеличить количество?", РежимДиалогаВопрос.ДаНет);
    Иначе
        ОповеститьОВыборе(ДанныеТовара.Ссылка);
    КонецЕсли;
КонецПроцедуры

&НаКлиенте
Процедура ПослеОтветаНаВопрос(Результат, ДополнительныеПараметры) Экспорт
    Если Результат = КодВозвратаДиалога.Да Тогда
        ОповеститьОВыборе(ДополнительныеПараметры.Ссылка);
    КонецЕсли;
КонецПроцедуры

В этом случае владельцем окна вопроса становится форма подбора, и оно гарантированно отобразится поверх неё.

Решение №3: Использование обработчика ожидания

Если логику невозможно перенести в дочернюю форму, можно воспользоваться техническим «хаком» — вызовом вопроса через небольшую паузу. Это позволяет платформе завершить текущий стек отрисовки интерфейса и корректно пересчитать Z-порядок окон.

Разберем, как реализовать это в форме документа (владельце):


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

&НаКлиенте
Процедура ВыполнитьПоказВопроса() Экспорт
    ОписаниеОповещения = Новый ОписаниеОповещения("ПослеВопроса", ЭтаФорма);
    ПоказатьВопрос(ОписаниеОповещения, "Увеличить количество?", РежимДиалогаВопрос.ДаНет);
КонецПроцедуры

Этот метод часто помогает «вытолкнуть» окно на передний план, так как вызов происходит уже после того, как основные процедуры обработки выбора завершены.

Резюме

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

Выбор конкретного метода зависит от требований к юзабилити вашего решения и сложности логики взаимодействия между формами.

← На главную