Как узнать и программно установить активное окно в списке номенклатуры 1С

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

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

Анализ структуры окон клиентского приложения

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

Когда мы открываем форму списка номенклатуры, в массиве Содержимое находится ровно один элемент — сама форма (закладка «Основное»). Как только мы нажимаем на пункт панели навигации (например, «Цены»), в этот массив добавляется второй элемент. Порядок элементов в массиве соответствует порядку их открытия. Однако главная сложность заключается в том, что у объекта УправляемаяФорма в этом массиве нет прямого свойства Активна или Текущая.

Выясним причину странного поведения при закрытии: по умолчанию нажатие Esc инициирует закрытие всей цепочки, если фокус находится в основном окне, или закрывает только активное подчиненное окно. Но в некоторых случаях (в зависимости от настроек интерфейса) платформа закрывает все сразу. Наша задача — перехватить этот момент.

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

Рассмотрим вариант, при котором мы будем анализировать количество открытых подчиненных форм в обработчике ПередЗакрытием. Если в массиве Содержимое больше одного элемента, мы отменяем стандартное закрытие и принудительно закрываем только «лишние» формы, возвращая фокус на основную.

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


&НаКлиенте
Процедура ПередЗакрытием(Отказ, ЗавершениеРаботы, ТекстПредупреждения, СтандартнаяОбработка)
    
    // Получаем текущее окно, в котором находится форма
    ТекущееОкно = ЭтаФорма.ПолучитьОкно();
    МассивОкон = ТекущееОкно.Содержимое;
    
    // Если в окне открыто что-то кроме основной формы (индекс 0)
    Если МассивОкон.Количество() > 1 Тогда
        
        // Устанавливаем Отказ, чтобы основная форма не закрылась
        Отказ = Истина;
        
        // Начинаем закрывать все окна, кроме первого (основного)
        // Важно: закрываем с конца или по индексу 1 в цикле Пока
        Пока МассивОкон.Количество() > 1 Цикл
            Попытка
                МассивОкон[1].Закрыть();
            Исключение
                Прервать; // На случай, если форма не может быть закрыта
            КонецПопытки;
        КонецЦикла;
        
        // Активизируем основную форму номенклатуры
        МассивОкон[0].Активизировать();
        
    КонецЕсли;
    
КонецПроцедуры

Проанализируем этот код. Мы используем цикл Пока, потому что при вызове метода Закрыть() состав коллекции Содержимое динамически меняется. Мы всегда пытаемся закрыть элемент с индексом 1, пока в коллекции не останется только элемент с индексом 0 (наша основная форма).

Проблема «анабиоза» интерфейса и методы её решения

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

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

Способ 2. Использование временной задержки (Double Esc)

Если мы хотим добиться максимально естественного поведения, можно внедрить логику «двойного нажатия». Рассмотрим ситуацию: первое нажатие Esc переводит пользователя в основную закладку, а второе (если оно сделано быстро) — закрывает форму. Это решение выглядит очень надежным в промышленной эксплуатации.

Реализуем это через хранение времени последнего нажатия в реквизите формы МоментНажатияEsc (тип Дата):


&НаКлиенте
Процедура ПередЗакрытием(Отказ, ЗавершениеРаботы, ТекстПредупреждения, СтандартнаяОбработка)
    
    ТекущееОкно = ЭтаФорма.ПолучитьОкно();
    
    Если ТекущееОкно.Содержимое.Количество() > 1 Тогда
        
        // Проверяем, как давно пользователь нажимал Esc в прошлый раз
        Если ЗначениеЗаполнено(МоментНажатияEsc) И (ТекущаяДата() - МоментНажатияEsc < 2) Тогда
            // Если прошло меньше 2 секунд, значит пользователь хочет действительно выйти
            Возврат; 
        Иначе
            // Иначе — просто переключаем его на главную закладку
            Отказ = Истина;
            ТекущееОкно.Содержимое[0].Активизировать();
            МоментНажатияEsc = ТекущаяДата();
            
            // Опционально: можно программно закрыть подчиненные окна, как в первом способе
            Пока ТекущееОкно.Содержимое.Количество() > 1 Цикл
                ТекущееОкно.Содержимое[1].Закрыть();
            КонецЦикла;
        КонецЕсли;
        
    КонецЕсли;
    
КонецПроцедуры

Как гарантированно узнать, какая закладка активна сейчас?

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

  1. Создадим в основной форме (Номенклатура) экспортную переменную или метод УстановитьАктивнуюЗакладку(ИмяФормы).
  2. Для каждой формы, вызываемой из навигации (Цены, Спецификации), в расширении перехватим событие ПриАктивизации.
  3. В этом событии будем вызывать ОповеститьОВыборе или напрямую обращаться к владельцу формы, передавая имя текущей открытой закладки.

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

Подведем итог: для реализации комфортного перехода между окнами панели навигации по Esc наиболее эффективным является сочетание проверки Окно.Содержимое.Количество() и механизма предотвращения случайного закрытия всей формы. Это избавляет пользователя от необходимости заново открывать список номенклатуры и искать нужную позицию после случайного нажатия клавиши выхода из режима просмотра цен или спецификаций.

← На главную