Как вывести дерево значений в табличное поле на обычной форме в 1С

Программист 1С v8.3 (Обычные формы) IT и автоматизация бизнеса
← На главную

При работе с обычными (неуправляемыми) формами в 1С:Предприятии разработчики часто сталкиваются с задачей вывода иерархических данных. Наиболее подходящим инструментом для этого является объект ДеревоЗначений, отображаемый через элемент управления ТабличноеПоле. Однако при попытке вывести результат запроса «напрямую» новички нередко совершают ошибки, приводящие к тому, что табличное поле остается пустым или система выдает ошибки вроде «Метод объекта не обнаружен».

В этой статье мы подробно разберем, как правильно связать ДеревоЗначений с элементом формы, рассмотрим основные подводные камни, научимся программно настраивать колонки и управлять раскрытием ветвей дерева.

Выясняем причину проблемы: почему дерево не выводится?

Для начала проанализируем ситуацию, описанную на форуме. Разработчик сформировал запрос с иерархическими итогами и выгрузил его в дерево значений:


Результат = Запрос.Выполнить().Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкамСИерархией);

Однако при попытке вывести этот результат на форму возникли сложности. В сообщении 5 упоминается ошибка:


{ВнешнийОтчет.ОтчетПоНоменклатуре.Форма.ФормаОтчета.Форма(19)}: Метод объекта не обнаружен (Загрузить)
ПолеТабличногоДокументаРезультат.Загрузить(...)

Здесь допущены сразу две серьезные ошибки:

  1. Использован не тот элемент управления. Вместо ТабличногоПоля разработчик обратился к ПолюТабличногоДокумента (предназначенному для вывода печатных форм и отчетов, аналогу макета). У объекта ПолеТабличногоДокумента действительно нет метода Загрузить().
  2. Попытка вызвать метод Загрузить() у элемента формы напрямую. Даже если бы это было правильное ТабличноеПоле, его данные заполняются иначе.

Выяснив причины неудач, рассмотрим правильные способы решения этой задачи на обычных формах.

Способ 1. Прямое связывание через свойство «Значение» и метод «СоздатьКолонки()»

Если на вашей форме размещено ТабличноеПоле (например, с именем ТабличноеПоле1), которое в конфигураторе не привязано ни к какому конкретному реквизиту формы (свойство Данные у него пустое), вы можете связать его с деревом программно.

Выполним эту операцию по шагам:

  1. Получим дерево значений из запроса с помощью обхода по группировкам с иерархией.
  2. Присвоим полученное дерево свойству Значение нашего табличного поля.
  3. Вызовем метод СоздатьКолонки() для автоматического построения колонок на форме.

Посмотрим на пример готового кода:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	Номенклатура.Ссылка КАК Ссылка
    |ИЗ
    |	Справочник.Номенклатура КАК Номенклатура
    |ГДЕ
    |	НЕ Номенклатура.ЭтоГруппа
    |УПОРЯДОЧИТЬ ПО
    |	Ссылка
    |ИТОГИ ПО
    |	Ссылка ИЕРАРХИЯ";

// Выгружаем результат запроса в дерево значений
ДеревоДанных = Запрос.Выполнить().Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкамСИерархией);

// Связываем данные с элементом формы
ЭлементыФормы.ТабличноеПоле1.Значение = ДеревоДанных;

// Формируем визуальные колонки на форме
ЭлементыФормы.ТабличноеПоле1.СоздатьКолонки();

Метод СоздатьКолонки() анализирует структуру колонок в переданном ДеревеЗначений и автоматически воссоздает их в элементе управления. Без вызова этого метода табличное поле визуально останется пустым, хотя данные в нем уже будут присутствовать.

Способ 2. Рекомендуемый метод связывания через свойство «Данные»

Использование метода СоздатьКолонки() имеет один существенный недостаток: он полностью сбрасывает все визуальные настройки колонок (ширину, заголовки, видимость, форматы вывода чисел/дат), которые вы могли ранее вручную настроить в Конфигураторе.

Чтобы сохранить уникальный дизайн вашей таблицы, разберем более надежный профессиональный подход:

  1. В модуле формы (или в ее реквизитах) определите переменную (реквизит формы) с типом ДеревоЗначений. Назовем её, например, ДеревоНоменклатуры.
  2. В свойствах элемента управления ТабличноеПоле1 на форме найдите свойство Данные и укажите в нем имя нашего реквизита: ДеревоНоменклатуры.
  3. В Конфигураторе зайдите в свойства колонок табличного поля и добавьте нужные колонки вручную. Для каждой колонки обязательно заполните свойство Данные — укажите туда точное имя поля, которое будет возвращать ваш запрос (в нашем случае — Ссылка).

При таком подходе программный код значительно упрощается, а метод СоздатьКолонки() вызывать больше не нужно:


// Заполняем непосредственно реквизит формы, связанный с табличным полем
ДеревоНоменклатуры = Запрос.Выполнить().Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкамСИерархией);

Платформа 1С автоматически сопоставит имена колонок из структуры полученного дерева с именами колонок, настроенных в интерфейсе табличного поля, сохранив все ваши визуальные настройки (шрифты, цвета, ширину и доступность).

Важный нюанс запроса: обход иерархии номенклатуры

Проанализируем конструкцию запроса, которую использовал автор темы:


ГДЕ НЕ Номенклатура.ЭтоГруппа
...
ИТОГИ ПО Ссылка ИЕРАРХИЯ

Здесь кроется важная особенность работы платформы 1С. Условие ГДЕ НЕ Номенклатура.ЭтоГруппа полностью исключает папки справочника из детальных записей выборки. Однако использование конструкции ИТОГИ ПО Ссылка ИЕРАРХИЯ заставляет платформу принудительно воссоздавать родительские группы в дереве значений, чтобы сохранить корректную древовидную структуру.

Что важно помнить в этом случае? Для строк-родителей (которые являются группами) все остальные выбранные в запросе реквизиты (например, код, артикул, цена, если бы они были в выборке) будут содержать значение NULL или пустые значения. Заполнено будет только ключевое поле иерархии — Ссылка. Учитывайте это при выводе дополнительных данных в строки дерева.

Как автоматически раскрыть все ветви дерева значений на форме?

По умолчанию при заполнении табличного поля все узлы дерева выводятся в свернутом виде. Пользователю приходится кликать на каждый «плюсик», чтобы увидеть подчиненные элементы. Рассмотрим, как сделать интерфейс более дружелюбным.

Вариант А. Настройка свойства в Конфигураторе
У элемента управления ТабличноеПоле есть замечательное свойство Начальное отображение дерева (в коде доступно как НачальноеОтображениеДерева). Вы можете установить его значение в:

Программно это можно сделать одной строкой:


ЭлементыФормы.ТабличноеПоле1.НачальноеОтображениеДерева = НачальноеОтображениеДерева.РаскрыватьВсеУровни;

Вариант Б. Программный обход и выборочное раскрытие
Если вам нужно развернуть только определенные строки дерева по условию, воспользуйтесь методом табличного поля Развернуть(). Рассмотрим пример функции, которая рекурсивно обходит строки дерева и разворачивает их:


Процедура РазвернутьСтрокиДереваРекурсивно(ЭлементТабличноеПоле, СтрокиДерева)
    Для Каждого Строка Из СтрокиДерева Цикл
        // Разворачиваем текущую строку
        ЭлементТабличноеПоле.Развернуть(Строка, Истина);
        
        // Переходим к обработке подчиненных строк
        РазвернутьСтрокиДереваРекурсивно(ЭлементТабличноеПоле, Строка.Строки);
    КонецЦикла;
КонецПроцедуры

// Пример вызова процедуры для всей коллекции верхнего уровня:
РазвернутьСтрокиДереваРекурсивно(ЭлементыФормы.ТабличноеПоле1, ДеревоДанных.Строки);

Для сравнения: как эта задача решается на управляемых формах

В сообщениях 5 и 6 на форуме возникла путаница из-за предложения использовать методы управляемых форм на обычных формах. Давайте проясним эту разницу, чтобы вы легко ориентировались в обеих технологиях.

На управляемых формах напрямую присвоить дерево значений элементу формы (как мы делали в Способе 1) нельзя, так как на клиенте объект ДеревоЗначений недоступен. Процесс выглядит совершенно иначе:

  1. На форме создается реквизит с типом ДеревоЗначений (например, с именем ДеревоНаФорме).
  2. Данный реквизит перетаскивается на форму, превращаясь в таблицу формы. Колонки жестко описываются в списке реквизитов формы.
  3. На сервере выполняется запрос, результат выгружается в дерево, а затем данные переносятся в реквизит формы специальным методом ЗначениеВРеквизитФормы:

&НаСервере
Процедура ЗаполнитьДеревоНаСервере()
    
    Запрос = Новый Запрос("ВЫБРАТЬ Номенклатура.Ссылка КАК Ссылка ИЗ Справочник.Номенклатура");
    ДеревоИзЗапроса = Запрос.Выполнить().Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкамСИерархией);
    
    // Переносим данные в реквизит управляемой формы
    ЗначениеВРеквизитФормы(ДеревоИзЗапроса, "ДеревоНаФорме");
    
КонецПроцедуры

Как видим, в управляемом интерфейсе разработчик полностью изолирован от прямого обращения к элементам формы для передачи данных, в то время как на обычных формах мы напрямую взаимодействуем с коллекцией ЭлементыФормы. Для реализации динамической структуры данных в тонком клиенте можно изучить пример программного вывода произвольной таблицы значений на управляемую форму.

Используйте предложенные нами способы программного вывода деревьев значений, адаптируйте их под свои задачи автоматизации и создавайте удобные интерфейсы для пользователей вашей системы!

← На главную