Как реализовать последовательное открытие нескольких форм выбора в управляемом приложении 1С 8.3?

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

При переходе на управляемые формы (УФ) и отказе от использования модальных окон программисты 1С часто сталкиваются с проблемой: как заставить программу «ждать» выбора пользователя, если нужно открыть несколько окон выбора подряд? Типичная ситуация — загрузка данных из внешнего файла (например, XML), где для каждой строки файла требуется вручную подобрать номенклатуру из справочника.

В этой статье мы подробно разберем, почему привычные циклы Для...Цикл больше не работают для открытия форм и как правильно реализовать алгоритм последовательного вызова окон с использованием асинхронных методов платформы 1С v8.3.

Проблема асинхронного открытия форм

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

В управляемом приложении, особенно в конфигурациях типа УТ 11.4, ERP 2.4 или КА 2, использование модальных окон запрещено. Метод ОткрытьФорму работает асинхронно: он отправляет команду на открытие окна и тут же переходит к выполнению следующей строки кода, не дожидаясь действий пользователя. Если мы напишем цикл, система мгновенно откроет столько окон, сколько итераций в цикле (например, 10 или 20), что приведет к «каше» на экране и невозможности нормальной работы.

Решение 1: Рекурсивный вызов через ОписаниеОповещения

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

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

Шаг 2. Создание рекурсивной процедуры. Напишем код, который будет открывать форму и передавать «самому себе» управление после закрытия этой формы.


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

&НаКлиенте
Процедура ОткрытьСледующуюФорму(РезультатЗакрытия, ДопПараметры) Экспорт
    
    // Если это не первый запуск, обрабатываем результат предыдущего выбора
    Если РезультатЗакрытия <> Неопределено Тогда
        ДопПараметры.Результаты.Добавить(РезультатЗакрытия);
        Сообщить("Выбран элемент №" + ДопПараметры.Текущий + ": " + РезультатЗакрытия);
        
        // Увеличиваем счетчик шагов только после успешного выбора
        ДопПараметры.Текущий = ДопПараметры.Текущий + 1;
    КонецЕсли;

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

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

Решение 2: Использование «Контейнера состояния» и обработка отказа

Часто пользователь может нажать «Отмена» или закрыть окно выбора крестиком. В этом случае РезультатЗакрытия будет равен Неопределено. Мы должны решить: прерывать ли весь процесс или просто пропустить этот шаг.

Рассмотрим более продвинутый вариант с обработкой пустых значений и передачей данных через структуру:


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

Использование структуры ПараметрыЦепочки позволяет избежать использования глобальных переменных модуля. Это делает код чище и позволяет запускать несколько таких процессов параллельно (если это необходимо).

Решение 3: Альтернативный UX-подход (Таблица на форме)

Проанализируем ситуацию с точки зрения удобства пользователя. Если в XML-файле 50 строк, открывать форму выбора 50 раз подряд — это плохая практика (UX). Пользователь быстро устанет и начнет ошибаться.

Рассмотрим более эффективный алгоритм:

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

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

Технические детали и советы

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

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

2. Парсинг XML. Чтение файла лучше производить один раз. Если данных много, прочитайте их на сервере, поместите в Массив или ТаблицуЗначений и верните на клиент. Не стоит перечитывать файл на каждом шаге итерации.

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


ПараметрыОтбора = Новый Структура("Отбор", Новый Структура("ВидНоменклатуры", НужныйВид));
ОткрытьФорму("Справочник.Номенклатура.ФормаВыбора", ПараметрыОтбора, ЭтаФорма, , , , ОписаниеОповещения);

Выясним причину ошибки «Использование модальных окон запрещено». Эта ошибка возникает, если в свойствах конфигурации установлен режим использования модальности «Не использовать», а вы вызываете методы типа Выбрать() или ОткрытьМодально(). Использование ОписаниеОповещения — это единственный штатный способ обойти это ограничение, сохраняя логику последовательных действий.

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

← На главную