При работе с большими деревьями значений в обычных формах 1С (например, в УТ 10.3) разработчики часто сталкиваются с критическим падением производительности при программном сворачивании или развертывании всех узлов. Основная проблема заключается в том, что при каждом вызове метода Свернуть() или Развернуть() платформа инициирует обновление графического интерфейса. Это приводит к многократному вызову тяжелых обработчиков событий, таких как ПриВыводеСтроки и ПриПолученииДанных. Если в этих процедурах выполняются расчеты или запросы, процесс может затянуться на минуты.
В этой статье мы подробно разберем, как оптимизировать этот процесс, используя различные подходы: от простых флагов до глубокой оптимизации через свойства контролов.
Проанализируем самую частую причину тормозов. Когда мы в цикле обходим строки дерева и сворачиваем их, 1С пытается перерисовать форму после каждой операции. Если у нас 1000 строк, обработчик ПриВыводеСтроки может быть вызван тысячи раз. Самый простой и эффективный способ — временно «отключить» логику внутри этих событий.
Для этого в модуле формы создадим переменную-флаг. Рассмотрим пример реализации:
Перем ЭтоМассовоеДействие;
Процедура КоманднаяПанельСвернутьВсе(Кнопка)
ЭтоМассовоеДействие = Истина; // Включаем блокировку
// Выполняем действия с деревом
Для Каждого СтрокаВерхнегоУровня Из ДеревоТоваров.Строки Цикл
ЭлементыФормы.ДеревоТоваров.Свернуть(СтрокаВерхнегоУровня, Истина);
КонецЦикла;
ЭтоМассовоеДействие = Ложь; // Выключаем блокировку
// Принудительно обновляем форму один раз в конце
ЭлементыФормы.ДеревоТоваров.ОбновитьСтроки();
КонецПроцедуры
Теперь нам необходимо изменить обработчики событий дерева. Выясним, как это повлияет на скорость:
Процедура ДеревоТоваровПриВыводеСтроки(Элемент, ОформлениеСтроки, ДанныеСтроки)
Если ЭтоМассовоеДействие = Истина Тогда
Возврат; // Ничего не считаем и не оформляем, пока идет цикл
КонецЕсли;
// Ваша обычная логика оформления (цвета, шрифты, вычисления)
ОформлениеСтроки.Ячейки.Остаток.УстановитьТекст(ДанныеСтроки.Остаток);
КонецПроцедуры
Важный нюанс: использование флага позволяет избежать тяжелых вычислений, но сама платформа все равно будет тратить время на вызов пустой процедуры. Для максимального ускорения перейдем к более радикальным методам.
Часто программисты пишут собственные рекурсивные функции для обхода всех уровней дерева. Однако платформа 1С на уровне встроенного языка уже имеет оптимизированный механизм. Разберем синтаксис методов Свернуть() и Развернуть():
ЭлементыФормы.ИмяЭлемента.Свернуть(Строка, Рекурсивно)
Если во второй параметр передать Истина, платформа выполнит операцию для всех дочерних узлов на уровне собственного кода (C++), что работает значительно быстрее, чем цикл на языке 1С. Посмотрим на оптимизированный код развертывания:
Для Каждого Строка Из ДеревоТоваров.Строки Цикл
ЭлементыФормы.ДеревоТоваров.Развернуть(Строка, Истина);
КонецЦикла;
Рассмотрим «хитрый» способ, который позволяет обмануть графическую подсистему обычных форм. Суть заключается в том, чтобы на время выполнения массовой операции превратить «Дерево» в «Список». В режиме списка иерархия не отрисовывается, и платформа не пытается отслеживать состояние узлов.
Выполним следующие шаги в коде:
Отображение в значение Список.Дерево.
ЭлементДерево = ЭлементыФормы.ДеревоТоваров;
ЭлементДерево.Видимость = Ложь;
// Устанавливаем режим списка, это отключает отрисовку веток дерева
ЭлементДерево.Отображение = ОтображениеТаблицы.Список;
Для Каждого ТекущаяСтрока Из ДеревоТоваров.Строки Цикл
ЭлементДерево.Свернуть(ТекущаяСтрока, Истина);
КонецЦикла;
// Возвращаем исходный вид
ЭлементДерево.Отображение = ОтображениеТаблицы.Дерево;
ЭлементДерево.Видимость = Истина;
Этот метод показывает лучшие результаты на деревьях, содержащих более 5-10 тысяч строк. Платформа перерисовывает дерево целиком только один раз в момент включения видимости.
В обычных формах 1С предусмотрены стандартные сочетания клавиш для управления деревьями, которые работают на уровне системных событий Windows. Проанализируем их:
Мы можем имитировать нажатие этих клавиш программно через объект WScript.Shell. Это заставляет платформу использовать свой внутренний высокопроизводительный механизм сворачивания.
Процедура РазвернутьВсеДеревоЧерезКлавиши()
ЭлементыФормы.ДеревоТоваров.УстановитьФокус();
Shell = Новый COMОбъект("WScript.Shell");
// Имитируем нажатие Ctrl+Shift+Num+
// Плюс в SendKeys экранируется фигурными скобками
Shell.SendKeys("^+{ADD}");
КонецПроцедуры
Внимание: данный метод имеет недостаток — он требует, чтобы фокус ввода находился именно на дереве, и может не сработать, если пользователь в этот момент переключит окно.
Помимо способов сворачивания, важно проанализировать, как написаны ваши основные обработчики. Вот несколько советов по улучшению производительности:
ПриПолученииДанных вместо ПриВыводеСтроки. Процедура ПриПолученииДанных вызывается один раз для всей пачки строк, отображаемых на экране. В ней можно одним запросом получить данные для всех строк сразу, что кардинально быстрее, чем делать вычисления для каждой строки по отдельности.ОбновитьОтображениеДанных() для всей формы. Если нужно обновить только дерево, используйте ЭлементыФормы.ДеревоТоваров.ОбновитьСтроки().Подводя итог, можно сказать: для небольших деревьев достаточно использовать флаг блокировки в ПриВыводеСтроки, а для сложных и объемных структур лучшим решением будет временная смена режима Отображение на Список в сочетании с рекурсивными методами платформы.