Работа с иерархическими структурами данных, такими как Дерево значений, является одной из наиболее частых задач при разработке интерфейсов для сложных документов, например, коммерческих предложений. В таких задачах требуется не просто вывести список товаров, а сгруппировать их по категориям (например, «Класс люкс» или «Эконом»), обеспечив удобную навигацию и редактирование. В этой статье мы подробно разберем, как правильно реализовать программное добавление строк на разные уровни дерева, обеспечив корректное поведение интерфейса.
Рассмотрим типичную ошибку начинающих разработчиков, представленную в первом сообщении. Использование метода РеквизитФормыВЗначение на сервере для добавления строк — это «тяжелая» операция. При каждом добавлении строки происходит полная конвертация данных формы в объектную модель и обратно (ЗначениеВРеквизитФормы). Это не только замедляет работу интерфейса, но и может приводить к потере фокуса текущей строки.
Важное правило: Для простых манипуляций со строками (добавление, удаление, изменение значений) лучше работать напрямую с типом ДанныеФормыДерево на стороне клиента или сервера без лишних конвертаций. Это повышает производительность и делает код чище.
Для создания новой категории или группы в корне дерева нам не нужно знать текущее положение курсора. Мы просто обращаемся к корневой коллекции элементов дерева. Разберем код процедуры, которая добавляет новую ветку в самый верхний уровень иерархии:
&НаКлиенте
Процедура ДобавитьУровень1(Команда)
// Получаем коллекцию элементов верхнего уровня
НоваяСтрока = Дерево.ПолучитьЭлементы().Добавить();
// Заполняем ключевые поля, если это необходимо
// НоваяСтрока.Наименование = "Новая категория";
// Устанавливаем фокус на новую строку для немедленного редактирования
Элементы.Дерево.ТекущаяСтрока = НоваяСтрока.ПолучитьИдентификатор();
Элементы.Дерево.ИзменитьСтроку();
КонецПроцедуры
В данном примере метод ПолучитьЭлементы(), вызванный от самого реквизита Дерево, возвращает коллекцию строк именно первого уровня. Использование метода ПолучитьИдентификатор() позволяет нам программно переключить внимание пользователя на созданный объект.
Наибольшую сложность вызывает добавление строки «внутрь» выбранной группы. Проанализируем ситуацию: пользователь может выделить как саму группу, так и уже существующий в ней товар. Программа должна «понимать», куда именно добавить новую позицию.
Рассмотрим алгоритм, который определяет текущего родителя и добавляет в него новую подчиненную строку:
&НаКлиенте
Процедура ДобавитьУровень2(Команда)
// Получаем данные текущей выделенной строки
ТекущаяСтрока = Элементы.Дерево.ТекущиеДанные;
Если ТекущаяСтрока = Неопределено Тогда
Сообщить("Сначала выберите категорию!");
Возврат;
КонецЕсли;
// Определяем, кто будет родителем для новой строки
// Если мы стоим на строке 1 уровня, то родителем будет сама эта строка
// Если мы стоим на строке 2 уровня, то родителем будет ее родитель
ТекущийРодитель = ТекущаяСтрока.ПолучитьРодителя();
Если ТекущийРодитель = Неопределено Тогда
// Мы на первом уровне, значит текущая строка и есть будущий родитель
РодительДляДобавления = ТекущаяСтрока;
Иначе
// Мы уже на втором уровне (или глубже), берем родителя текущей строки
РодительДляДобавления = ТекущийРодитель;
КонецЕсли;
// Добавляем подчиненный элемент в коллекцию родителя
НоваяПодчиненнаяСтрока = РодительДляДобавления.ПолучитьЭлементы().Добавить();
// Переводим фокус на новую строку
Элементы.Дерево.ТекущаяСтрока = НоваяПодчиненнаяСтрока.ПолучитьИдентификатор();
Элементы.Дерево.ИзменитьСтроку();
КонецПроцедуры
Этот подход универсален: он позволяет добавлять товары в группу, даже если пользователь в данный момент редактирует другой товар в этой же группе. Программа автоматически поднимается на уровень выше с помощью метода ПолучитьРодителя().
Чтобы дерево было читаемым, важно визуально отделять группы от товаров. Обычно для строк первого уровня устанавливают жирный шрифт или другой фоновый цвет. Выясним, как это сделать правильно.
Для этой задачи лучше всего подходит механизм Условного оформления формы. Вы можете настроить его программно в процедуре ПриСозданииНаСервере или интерактивно в конфигураторе:
Дерево.РодительскоеПоле Заполнено = Ложь (если у вас есть колонка со ссылкой на родителя) или через анализ специфических признаков группы.Чтобы запретить редактирование строк первого уровня, можно использовать обработчик события ПриАктивизацииСтроки или проверять условие в событии ПередНачаломИзменения элемента формы "Таблица". Если текущая строка является родителем, установите параметр Отказ = Истина.
Рассмотрим еще несколько важных аспектов, которые часто возникают при реализации коммерческих предложений:
1. Иерархический расчет итогов. В отличие от плоских таблиц, дерево не умеет автоматически суммировать итоги по веткам. Чтобы при изменении цены товара обновлялась сумма всей категории, необходимо написать рекурсивную функцию. Она должна обходить все подчиненные строки (ПолучитьЭлементы()), суммировать их значения и записывать результат в родительскую строку.
2. Развертывание веток. По умолчанию дерево открывается свернутым. Если вы хотите, чтобы пользователь сразу видел содержимое, используйте метод Элементы.Дерево.Развернуть(). Например, после добавления новой группы можно автоматически развернуть её, чтобы пользователь видел, куда добавляются товары.
3. Уникальные ключи. Если ваше дерево связано с данными в базе (например, со справочником "Номенклатура"), обязательно храните в строке дерева ссылку на объект или уникальный идентификатор. Это позволит вам корректно сохранить структуру дерева в документ или регистр при закрытии формы.
4. Drag-and-Drop. Для повышения удобства работы в свойствах элемента формы "Дерево" можно включить флаги РазрешитьНачалоПеретаскивания и РазрешитьПеретаскивание. Это позволит пользователю простым движением мыши перемещать товары из одной категории в другую, не используя кнопки на панели инструментов.
Проанализировав эти методы, мы видим, что работа с деревом значений требует внимательного отношения к уровням вложенности и коллекциям элементов. Правильное использование клиентских методов ПолучитьЭлементы() и ПолучитьРодителя() позволяет создавать интуитивно понятные и быстрые интерфейсы для бизнес-задач любой сложности.