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