Как правильно открыть модальную форму из другой модальной формы в 1С 7.7 и избежать проблем с FormEx?

Программист 1С v7.7 IT и автоматизация бизнеса
← На главную

Разработка интерфейсных решений в среде 1С:Предприятие 7.7 часто преподносит сюрпризы, особенно когда речь заходит о каскадном открытии модальных окон. Дополнительные сложности возникают при активном использовании внешних компонент класса FormEx или 1С++, которые переопределяют стандартное поведение отрисовки окон Windows.

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

В чем суть технической проблемы?

Рассмотрим классическую ситуацию. У нас есть модальная форма (назовем ее «Форма 1»), в которой пользователь заполняет данные. Из нее по кнопке или автоматически должно открываться второе модальное окно («Форма 2»), например, форма поиска или подбора. Если пользователь нажимает на кнопку открытия «Формы 2» вручную — все работает отлично. Но если мы пытаемся автоматизировать этот процесс и запустить «Форму 2» сразу при открытии «Формы 1» (в процедурах ПриОткрытии или ПослеОткрытия), интерфейс «Формы 1» превращается в «кашу», а передаваемый контекст в отладчике оказывается пустым.

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

  1. Ограничение процедуры ПриОткрытии(). На момент выполнения этого предопределенного события операционная система еще физически не создала окно формы (отсутствует дескриптор HWND). Окно еще не отрисовано. Если внутри ПриОткрытии() принудительно запустить новое модальное окно, выполнение кода родительской формы приостанавливается. «Форма 1» застывает в полусозданном состоянии, из-за чего компонента FormEx не успевает применить свои хуки (перехватчики отрисовки) к элементам диалога. В результате мы получаем визуальный сбой — слои накладываются друг на друга, шрифты сбрасываются на системные.
  2. Особенности события ПослеОткрытия() в FormEx. Данное событие срабатывает уже после физического создания окна. Однако в некоторых версиях компоненты FormEx (особенно в диапазоне релизов от 2.0.5.105 до 2.0.5.114) существовал внутренний баг, из-за которого в модальном режиме выполнение ПослеОткрытия() ломалось или приводило к потере стека вызовов. Как результат — переданный параметр Контекст внутри дочернего окна оказывался пустым.

---

Решение 1. Использование однократного таймера (Обработки ожидания)

Это наиболее стабильный и проверенный временем способ обойти ограничения жизненного цикла формы в 1С 7.7. Наша задача — дать родительскому окну полностью инициализироваться, отрисоваться на экране, применить все стили FormEx и только после этого (асинхронно) запустить открытие второго окна.

Выясним, как это реализовать на практике. В модуле родительской формы («Форма 1») мы не вызываем открытие дочерней формы напрямую. Вместо этого мы взводим таймер на минимальное время:


Процедура ПриОткрытии()
    // Базовые инициализации
    // ...
    // Запускаем однократный таймер на 1 секунду (или меньше)
    Форма.ОбработкаОжидания("АвтоОткрытиеВторогоОкна", 1);
КонецПроцедуры

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


Процедура АвтоОткрытиеВторогоОкна()
    // Важно! Сразу отключаем таймер
    Форма.ОбработкаОжидания("", 0);
    
    // Теперь вызываем нашу процедуру открытия второго модального окна
    ОткрытьМодальноеОкно2();
КонецПроцедуры

При таком подходе операционная система успевает завершить создание окна «Формы 1», FormEx успешно инициализирует все слои и шрифты, и только после этого фокус ввода корректно передается в новое модальное окно.

---

Решение 2. Обновление FormEx и использование события ПослеСоздания()

Если вы столкнулись с тем, что процедура ПослеОткрытия() игнорируется или приводит к падению контекста, велика вероятность, что в вашей системе установлена устаревшая версия компоненты FormEx.

Для исправления ситуации выполните следующие шаги:

---

Решение 3. Безопасный обмен данными между формами через Список Значений

Проанализируем пример некорректной передачи данных, который часто используют разработчики:


// ПЛОХОЙ ПРИМЕР: Прямая запись в контекст родителя
Докум = Форма.Параметр.Получить("Контекст");
Докум.Товар = ТоварИзМодальнойФормы;

Почему это опасно? Прямое изменение реквизитов и переменных родительской формы через переданный контекст крайне нежелательно. Если пользователь закроет родительскую форму раньше времени или в момент работы подчиненной формы произойдет системный сбой, ссылка на контекст станет недействительной (битой). Это гарантированно приведет к аварийному завершению работы программы (ошибка Access Violation / Память не может быть read).

Рассмотрим правильный, безопасный и инкапсулированный вариант обмена данными через объект СписокЗначений, передаваемый по ссылке:

Код в родительской форме («Форма 1»):


Процедура ОткрытьМодальноеОкно2()
    ПараметрыПередачи = СоздатьОбъект("СписокЗначений");
    ПараметрыПередачи.Установить("Команда", "ПоискТовара");
    // Передаем также текущее значение для инициализации (если требуется)
    ПараметрыПередачи.Установить("ТекущийТовар", ВыбранныйТовар);
    
    // Открываем форму модально, передавая наш список вторым параметром
    ОткрытьФормуМодально("Обработка.ПоискАналогов", ПараметрыПередачи);
    
    // После закрытия подчиненной формы анализируем возвращенный результат
    Если ПараметрыПередачи.Получить("Статус") = "ОК" Тогда
        ВыбранныйТовар = ПараметрыПередачи.Получить("Результат");
        // Обновляем форму или таблицу
        Форма.Обновить(0);
    КонецЕсли;
КонецПроцедуры

Код в подчиненной форме («Форма 2»):


Процедура ПриОткрытии()
    // Получаем переданный список значений
    Параметры = Форма.Параметр;
    Если ТипЗначенияСтр(Параметры) = "СписокЗначений" Тогда
        Команда = Параметры.Получить("Команда");
        НачальныйТовар = Параметры.Получить("ТекущийТовар");
    КонецЕсли;
КонецПроцедуры

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

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

---

Решение 4. Альтернативный подход без модальности — динамические слои

Опытные разработчики 1С 7.7 знают, что каскадная модальность (окно поверх окна, поверх еще одного окна) — это источник постоянных проблем с фокусом ввода в Windows. Бывают ситуации, когда дочернее окно случайно «проваливается» под родительское, полностью блокируя интерфейс программы без возможности кликнуть куда-либо.

Если вы используете FormEx, отличной альтернативой второму модальному окну будет использование динамических слоев на одной и той же форме:

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

← На главную