При переводе сильно кастомизированных конфигураций с обычными формами (толстый клиент) с платформы 1С:Предприятие 8.2 на 1С:Предприятие 8.3 разработчики нередко сталкиваются с неочевидными программными ошибками. Особенно часто сбои происходят на сложных рабочих столах, таких как рабочее место менеджера CRM или многостраничные формы документов и справочников в конфигурациях «1С:Управление торговлей 10.3» и совместных отраслевых решениях. Выясним причину этой проблемы и разберем проверенные способы ее устранения.
Проанализируем механику работы графического движка и очередность инициализации элементов управления. В платформе 1С 8.2 при интерактивном клике пользователя по закладке панели происходил следующий порядок вызовов:
ПриСменеСтраницы для элемента управления Панель. В этом обработчике программисты традиционно подготавливали выборки, устанавливали отборы в динамических списках, инициализировали структуры или меняли значения реквизитов формы, отвечающих за контекст текущей вкладки.ПриВыводеСтроки или ПриПолученииДанных.В платформе 1С 8.3 механизм отрисовки неактивных страниц был оптимизирован. В результате поведение изменилось:
ПриВыводеСтроки, и только после завершения первой отрисовки управление передается в обработчик ПриСменеСтраницы.ПриСменеСтраницы, затем обновляются строки таблицы.Если код внутри события ПриВыводеСтроки или ПриПолученииДанных рассчитывает на то, что структуры данных уже подготовлены в процедуре ПриСменеСтраницы, либо напрямую проверяет свойство ЭлементыФормы.Панель.ТекущаяСтраница, форма либо аварийно завершает работу с ошибкой обращения к пустой переменной, либо выводит некорректные и пустые строки.
Рассмотрим первый и самый надежный метод решения проблемы. Нам необходимо убрать жесткую зависимость обработчика отрисовки строк от факта предварительного вызова события смены страницы. Если при первом вызове необходимые структуры еще не проинициализированы, мы должны выполнить их подготовку прямо по месту или безопасно выйти из процедуры.
Посмотрим на пример кода обработки события ПриВыводеСтроки:
Процедура ТабличноеПолеЗаказыПриВыводеСтроки(Элемент, ОформлениеСтроки, ДанныеСтроки)
// Проверяем, инициализирована ли структура контекста
Если мКэшСостояний = Неопределено Тогда
// Выполняем первичную инициализацию по требованию
ИнициализироватьКонтекстСтраницыЗаказов();
КонецЕсли;
// Безопасное получение данных оформления
СтрокаДанных = мКэшСостояний.Получить(ДанныеСтроки.Ссылка);
Если СтрокаДанных <> Неопределено Тогда
ОформлениеСтроки.ЦветТекста = СтрокаДанных.Цвет;
КонецЕсли;
КонецПроцедуры
При таком подходе не имеет значения, в каком порядке платформа вызвала события: данные будут корректно сформированы в момент первого фактического обращения к ним.
Разберем типичную архитектурную ошибку разработчиков, перешедших с платформы 8.2. Довольно часто внутри одного общего обработчика табличных полей или списка документов использовалось условие проверки текущей страницы панели:
// Ошибочный подход для 1С 8.3:
Если ЭлементыФормы.ОсновнаяПанель.ТекущаяСтраница = ЭлементыФормы.ОсновнаяПанель.Страницы.СтраницаЗаказы Тогда
// Обработка данных
КонецЕсли;
В момент первичной отрисовки в 1С 8.3 свойство ТекущаяСтраница еще указывает на старую закладку, с которой только что ушел пользователь. В результате условие не выполняется, и таблица отрисовывается без нужного оформления. Избавимся от этой зависимости и свяжем логику непосредственно с источником данных переданной строки или разделим обработчики для каждого табличного поля отдельно.
Если рабочий стол содержит сложную взаимосвязанную логику (как в типовых решениях CRM), проанализируем вариант сквозной инициализации в обработчике ПриОткрытии. Подготовим структуры для всех страниц сразу, не дожидаясь интерактивных кликов пользователя.
Посмотрим на пример организации процедуры открытия:
Процедура ПриОткрытии()
// Инициализируем общие кэши и таблицы для всех страниц панели
ИнициализироватьКэшКлиентов();
ИнициализироватьКэшЗадач();
ИнициализироватьКэшЗаказов();
// Настраиваем отборы и видимость по умолчанию
ОбновитьСостояниеПанели(ЭлементыФормы.ОсновнаяПанель.ТекущаяСтраница);
КонецПроцедуры
Благодаря разовому заполнению общих коллекций к моменту первого клика по любой вкладке все необходимые структуры уже будут находиться в оперативной памяти.
В ситуациях, когда подготовка данных для конкретной страницы требует тяжелых запросов к базе данных и не может выполняться заранее в процедуре ПриОткрытии, применим отложенный вызов через ПодключитьОбработчикОжидания.
Разберем по шагам реализацию этого механизма:
ПриСменеСтраницы сохраняем целевую страницу и запускаем однократный фоновый таймер с минимальной задержкой.Посмотрим, как это выглядит в коде модуля формы:
Перем мСтраницаДляОбновления;
Процедура ОсновнаяПанельПриСменеСтраницы(Элемент, ТекущаяСтраница)
мСтраницаДляОбновления = ТекущаяСтраница;
// Подключаем однократный вызов через 0.1 секунды
ПодключитьОбработчикОжидания("ВыполнитьОтложенноеОбновлениеСтраницы", 0.1, Истина);
КонецПроцедуры
Процедура ВыполнитьОтложенноеОбновлениеСтраницы()
Если мСтраницаДляОбновления = ЭлементыФормы.ОсновнаяПанель.Страницы.СтраницаCRM Тогда
ЗаполнитьДанныеРабочегоСтолаCRM();
ЭлементыФормы.ТабличноеПолеCRM.ОбновитьСтроки();
КонецЕсли;
КонецПроцедуры
Рассмотрим наиболее производительную альтернативу устаревшему событию ПриВыводеСтроки. Использование пакетного события ПриПолученииДанных позволяет не только решить проблему с рассинхронизацией порядка отрисовки страниц, но и значительно ускоряет работу интерфейса за счет групповой обработки строк.
Посмотрим на пример реализации пакетной обработки:
Процедура ТабличноеПолеПриПолученииДанных(Элемент, ОформленияСтрок)
// Формируем массив ссылок из переданного пакета строк
МассивСсылок = Новый Массив;
Для Каждого Оформление Из ОформленияСтрок Цикл
МассивСсылок.Добавить(Оформление.ДанныеСтроки.Ссылка);
КонецЦикла;
// Получаем необходимые расчетные данные одним запросом
ТаблицаРезультатов = ПолучитьОформлениеДляСписка(МассивСсылок);
// Применяем оформления к строкам
Для Каждого Оформление Из ОформленияСтрок Цикл
СтрокаНастроек = ТаблицаРезультатов.Найти(Оформление.ДанныеСтроки.Ссылка, "Ссылка");
Если СтрокаНастроек <> Неопределено Тогда
Оформление.ЦветФона = СтрокаНастроек.ЦветФона;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Резюмируем: при переносе обычных форм на платформу 1С 8.3 нельзя полагаться на то, что событие ПриСменеСтраницы всегда опережает первую визуализацию табличных полей. Изоляция контекста отрисовки таблиц, предварительная инициализация структур или использование отложенных обработчиков гарантируют стабильную работу интерфейса без сбоев.