Почему при переходе с 1С 8.2 на 8.3 в обычных формах некорректно срабатывает событие ПриСменеСтраницы на панели и как это исправить?

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

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

В чем заключается различие поведения платформы 1С 8.2 и 8.3

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

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

В платформе 1С 8.3 механизм отрисовки неактивных страниц был оптимизирован. В результате поведение изменилось:

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

Если код внутри события ПриВыводеСтроки или ПриПолученииДанных рассчитывает на то, что структуры данных уже подготовлены в процедуре ПриСменеСтраницы, либо напрямую проверяет свойство ЭлементыФормы.Панель.ТекущаяСтраница, форма либо аварийно завершает работу с ошибкой обращения к пустой переменной, либо выводит некорректные и пустые строки.

Способ 1. Защита от неопределенных данных и ленивая инициализация в табличном поле

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

Посмотрим на пример кода обработки события ПриВыводеСтроки:


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

При таком подходе не имеет значения, в каком порядке платформа вызвала события: данные будут корректно сформированы в момент первого фактического обращения к ним.

Способ 2. Отказ от проверки активной страницы панели внутри обработчиков строк

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


// Ошибочный подход для 1С 8.3:
Если ЭлементыФормы.ОсновнаяПанель.ТекущаяСтраница = ЭлементыФормы.ОсновнаяПанель.Страницы.СтраницаЗаказы Тогда
    // Обработка данных
КонецЕсли;

В момент первичной отрисовки в 1С 8.3 свойство ТекущаяСтраница еще указывает на старую закладку, с которой только что ушел пользователь. В результате условие не выполняется, и таблица отрисовывается без нужного оформления. Избавимся от этой зависимости и свяжем логику непосредственно с источником данных переданной строки или разделим обработчики для каждого табличного поля отдельно.

Способ 3. Предварительная инициализация всех страниц при открытии формы

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

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


Процедура ПриОткрытии()
    
    // Инициализируем общие кэши и таблицы для всех страниц панели
    ИнициализироватьКэшКлиентов();
    ИнициализироватьКэшЗадач();
    ИнициализироватьКэшЗаказов();
    
    // Настраиваем отборы и видимость по умолчанию
    ОбновитьСостояниеПанели(ЭлементыФормы.ОсновнаяПанель.ТекущаяСтраница);
    
КонецПроцедуры

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

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

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

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

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

Посмотрим, как это выглядит в коде модуля формы:


Перем мСтраницаДляОбновления;

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

Процедура ВыполнитьОтложенноеОбновлениеСтраницы()
    
    Если мСтраницаДляОбновления = ЭлементыФормы.ОсновнаяПанель.Страницы.СтраницаCRM Тогда
        ЗаполнитьДанныеРабочегоСтолаCRM();
        ЭлементыФормы.ТабличноеПолеCRM.ОбновитьСтроки();
    КонецЕсли;
    
КонецПроцедуры

Способ 5. Переход с события ПриВыводеСтроки на событие ПриПолученииДанных

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

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


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

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

← На главную