При проектировании интерфейсов в 1С:Предприятии часто возникает необходимость представить информацию в виде структурированного дерева. Для этих целей используется объект ДеревоЗначений. Однако при его заполнении и выводе на форму начинающие разработчики регулярно сталкиваются с трудностями: подчиненные элементы не отображаются, данные выводятся сплошным плоским списком, или дерево вовсе кажется пустым.
В этой статье мы подробно разберем, как правильно заполнять дерево значений программно (вручную по выборке), как выгружать его напрямую из запроса с итогами, а также как настроить свойства элемента формы для корректного отображения иерархии и сетки границ.
Рассмотрим ситуацию, когда на форме создано дерево значений с одной колонкой составного типа (например, ГруппировкаНоменклатуры, в которой могут храниться ссылки на справочники НоменклатурнаяГруппа и Номенклатура). Наша задача — сделать так, чтобы номенклатурная группа располагалась на верхнем уровне, а подчиненная ей номенклатура — на втором уровне.
Часто разработчики пишут код следующим образом:
// Ошибочный вариант заполнения
Пока Выборка.Следующий() Цикл
НС1 = Дерево1.Строки.Добавить();
НС1.НоменклатурнаяГруппа = Выборка.НоменклатурнаяГруппа;
НС2 = НС1.Строки.Добавить();
НС2.Номенклатура = Выборка.Номенклатура;
КонецЦикла;
Проанализируем этот код. На первый взгляд, логика соблюдена: мы добавляем строку первого уровня НС1, а затем в ее коллекцию строк добавляем подчиненную строку НС2. Однако при таком подходе в интерфейсе мы увидим только номенклатурные группы, а подчиненные номенклатурные позиции отображаться не будут.
Выясним причину проблемы. Она кроется в именах колонок. В коде значения присваиваются разным свойствам (колонкам): НоменклатурнаяГруппа для родителя и Номенклатура для потомка. Но если на форме у дерева значений создана всего одна колонка (например, с именем Номенклатура), то свойство НоменклатурнаяГруппа у строки дерева просто отсутствует или не связано с элементом формы. Данные записываются "в пустоту", а подчиненная строка отображается пустой.
Чтобы исправить эту ошибку, мы должны заполнять одну и ту же колонку как для родительской, так и для подчиненной строки. Давайте посмотрим на правильный вариант кода:
// Правильный вариант ручного заполнения
Пока Выборка.Следующий() Цикл
// Создаем строку первого уровня (родительский узел)
НС1 = Дерево1.Строки.Добавить();
НС1.Номенклатура = Выборка.НоменклатурнаяГруппа; // Записываем группу в общую колонку
// Создаем строку второго уровня (подчиненный узел)
НС2 = НС1.Строки.Добавить();
НС2.Номенклатура = Выборка.Номенклатура; // Записываем номенклатуру в ту же колонку
КонецЦикла;
В данном случае колонка Номенклатура имеет составной тип, что позволяет ей хранить как ссылки на номенклатурные группы, так и ссылки на саму номенклатуру. Заполняя одно и то же поле на разных уровнях иерархии, мы добиваемся корректного распределения данных.
Даже после исправления кода в модуле разработчики часто замечают, что дерево на форме все равно отображается плоским списком, либо подчиненные строки видны, но без возможности их свернуть или развернуть. Это связано с настройками элемента формы.
По умолчанию элемент формы типа "Таблица", связанный с деревом значений, может не иметь включенной настройки отображения иерархических связей. Чтобы включить древовидную структуру, выполните следующие шаги в конфигураторе:
ОтображатьИерархию в коде) и установите его в значение Истина.После включения этого свойства платформа автоматически добавит кнопки свертывания и развертывания узлов (плюсы и минусы) к первой колонке таблицы, и данные примут правильный древовидный вид.
Ручной обход выборки в цикле — не самый производительный метод, особенно при больших объемах данных. Гораздо эффективнее использовать возможности механизма запросов платформы 1С. Мы можем построить запрос с итогами и выгрузить его результаты напрямую в дерево значений.
Рассмотрим пример запроса и его выгрузки:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| спрНоменклатура.Ссылка КАК Номенклатура,
| спрНоменклатура.НоменклатурнаяГруппа КАК НоменклатурнаяГруппа
|ИЗ
| Справочник.Номенклатура КАК спрНоменклатура
|ГДЕ
| спрНоменклатура.Ссылка В ИЕРАРХИИ(&ГруппыТоваров)
| И спрНоменклатура.ЭтоГруппа = ЛОЖЬ
|ИТОГИ ПО
| НоменклатурнаяГруппа";
РезультатЗапроса = Запрос.Выполнить();
// Выгружаем результат запроса в дерево значений по группировкам
ДеревоЗначений = РезультатЗапроса.Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкам);
При использовании метода Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкам) крайне важно учитывать два нюанса:
ВЫБРАТЬ и секции ИТОГИ ПО вашего запроса. Если на форме колонка называется Номенклатура, а в итогах запроса мы группируем по полю НоменклатурнаяГруппа, то дерево создаст две разные колонки. На уровне группировки (первый уровень) будет заполнено поле НоменклатурнаяГруппа, а поле Номенклатура будет пустым. На детальном уровне (второй уровень) будет заполнено только поле Номенклатура. Это приводит к эффекту "пустых строк" в интерфейсе.Еще один частый вопрос касается визуального оформления дерева на форме: как сделать границы строк и колонок видимыми, чтобы интерфейс выглядел более структурированным? По умолчанию сетка в таблицах управляемых форм может быть отключена.
Решается это очень просто через палитру свойств элемента формы "Таблица":
Да (или включите флажок).Да.Эти простые настройки сделают таблицу более читаемой, четко очертив границы каждой ячейки.
В платформе 1С:Предприятие 8.3 при работе с управляемым интерфейсом важно помнить ключевое архитектурное правило: объект ДеревоЗначений существует исключительно на стороне сервера. Мы не можем напрямую передать его на клиент или привязать к элементу формы без специальной трансформации.
Давайте разберем по шагам правильную схему работы в управляемом приложении:
ДеревоДанных) и типом значения ДеревоЗначений.ДеревоДанных.&НаСервере).ДеревоЗначений отобразился в интерфейсе, необходимо использовать метод взаимного преобразования: ЗначениеВРеквизитФормы().Посмотрим на пример реализации этой схемы в коде модуля управляемой формы:
&НаСервере
Процедура ЗаполнитьДеревоНаСервере()
// 1. Преобразуем реквизит формы в прикладной объект "ДеревоЗначений" для работы с ним на сервере
ДеревоОбъект = РеквизитФормыВЗначение("ДеревоДанных");
// Очищаем старые данные
ДеревоОбъект.Строки.Очистить();
// 2. Выполняем запрос для получения данных
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Ссылка КАК Номенклатура,
| Номенклатура.НоменклатурнаяГруппа КАК НоменклатурнаяГруппа
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| НЕ Номенклатура.ЭтоГруппа";
Выборка = Запрос.Выполнить().Выбрать();
// 3. Заполняем дерево значений вручную
Пока Выборка.Следующий() Цикл
// Ищем, создавали ли мы уже строку для этой номенклатурной группы
РодительскаяСтрока = Неопределено;
Для Каждого СтрокаГруппы Из ДеревоОбъект.Строки Цикл
Если СтрокаГруппы.ГруппировкаНоменклатуры = Выборка.НоменклатурнаяГруппа Тогда
РодительскаяСтрока = СтрокаГруппы;
Прервать;
КонецЕсли;
КонецЦикла;
// Если группы еще нет в дереве, создаем ее
Если РодительскаяСтрока = Неопределено Тогда
РодительскаяСтрока = ДеревоОбъект.Строки.Добавить();
РодительскаяСтрока.ГруппировкаНоменклатуры = Выборка.НоменклатурнаяГруппа;
КонецЕсли;
// Добавляем подчиненную номенклатуру в найденную/созданную группу
НоваяСтрока = РодительскаяСтрока.Строки.Добавить();
НоваяСтрока.ГруппировкаНоменклатуры = Выборка.Номенклатура;
КонецЦикла;
// 4. Возвращаем измененный объект обратно в реквизит формы для отображения на клиенте
ЗначениеВРеквизитФормы(ДеревоОбъект, "ДеревоДанных");
КонецПроцедуры
В этом примере мы использовали колонку ГруппировкаНоменклатуры, которая имеет составной тип. Обратите внимание на алгоритм поиска дублей родительских строк: перед добавлением новой группы мы проверяем, не была ли она добавлена ранее, чтобы избежать дублирования веток дерева.
Если дерево значений должно содержать тысячи или десятки тысяч строк, полное его заполнение на сервере и последующая передача на клиент через ЗначениеВРеквизитФормы могут вызвать серьезные задержки в работе интерфейса. Пользователь будет наблюдать "зависание" программы.
Для решения этой проблемы опытные разработчики применяют паттерн отложенной загрузки (или Lazy Loading). Идея заключается в следующем:
ПриРазворачивании.Такой подход позволяет дереву открываться мгновенно, независимо от общего объема данных в базе, так как детальные записи считываются только по требованию пользователя.
Подводя итог, можно сказать, что успешная работа с деревом значений в 1С складывается из аккуратного сопоставления имен колонок при ручном заполнении или выгрузке запроса, правильного распределения типов данных в составных колонках и точечной настройки свойств отображения интерфейса в конфигураторе.