При работе с обычными (неуправляемыми) формами в 1С:Предприятии разработчики часто сталкиваются с задачей вывода иерархических данных. Наиболее подходящим инструментом для этого является объект ДеревоЗначений, отображаемый через элемент управления ТабличноеПоле. Однако при попытке вывести результат запроса «напрямую» новички нередко совершают ошибки, приводящие к тому, что табличное поле остается пустым или система выдает ошибки вроде «Метод объекта не обнаружен».
В этой статье мы подробно разберем, как правильно связать ДеревоЗначений с элементом формы, рассмотрим основные подводные камни, научимся программно настраивать колонки и управлять раскрытием ветвей дерева.
Для начала проанализируем ситуацию, описанную на форуме. Разработчик сформировал запрос с иерархическими итогами и выгрузил его в дерево значений:
Результат = Запрос.Выполнить().Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкамСИерархией);
Однако при попытке вывести этот результат на форму возникли сложности. В сообщении 5 упоминается ошибка:
{ВнешнийОтчет.ОтчетПоНоменклатуре.Форма.ФормаОтчета.Форма(19)}: Метод объекта не обнаружен (Загрузить)
ПолеТабличногоДокументаРезультат.Загрузить(...)
Здесь допущены сразу две серьезные ошибки:
ТабличногоПоля разработчик обратился к ПолюТабличногоДокумента (предназначенному для вывода печатных форм и отчетов, аналогу макета). У объекта ПолеТабличногоДокумента действительно нет метода Загрузить().Загрузить() у элемента формы напрямую. Даже если бы это было правильное ТабличноеПоле, его данные заполняются иначе.Выяснив причины неудач, рассмотрим правильные способы решения этой задачи на обычных формах.
Если на вашей форме размещено ТабличноеПоле (например, с именем ТабличноеПоле1), которое в конфигураторе не привязано ни к какому конкретному реквизиту формы (свойство Данные у него пустое), вы можете связать его с деревом программно.
Выполним эту операцию по шагам:
Значение нашего табличного поля.СоздатьКолонки() для автоматического построения колонок на форме.Посмотрим на пример готового кода:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Ссылка КАК Ссылка
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| НЕ Номенклатура.ЭтоГруппа
|УПОРЯДОЧИТЬ ПО
| Ссылка
|ИТОГИ ПО
| Ссылка ИЕРАРХИЯ";
// Выгружаем результат запроса в дерево значений
ДеревоДанных = Запрос.Выполнить().Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкамСИерархией);
// Связываем данные с элементом формы
ЭлементыФормы.ТабличноеПоле1.Значение = ДеревоДанных;
// Формируем визуальные колонки на форме
ЭлементыФормы.ТабличноеПоле1.СоздатьКолонки();
Метод СоздатьКолонки() анализирует структуру колонок в переданном ДеревеЗначений и автоматически воссоздает их в элементе управления. Без вызова этого метода табличное поле визуально останется пустым, хотя данные в нем уже будут присутствовать.
Использование метода СоздатьКолонки() имеет один существенный недостаток: он полностью сбрасывает все визуальные настройки колонок (ширину, заголовки, видимость, форматы вывода чисел/дат), которые вы могли ранее вручную настроить в Конфигураторе.
Чтобы сохранить уникальный дизайн вашей таблицы, разберем более надежный профессиональный подход:
ДеревоЗначений. Назовем её, например, ДеревоНоменклатуры.ТабличноеПоле1 на форме найдите свойство Данные и укажите в нем имя нашего реквизита: ДеревоНоменклатуры.Ссылка).При таком подходе программный код значительно упрощается, а метод СоздатьКолонки() вызывать больше не нужно:
// Заполняем непосредственно реквизит формы, связанный с табличным полем
ДеревоНоменклатуры = Запрос.Выполнить().Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкамСИерархией);
Платформа 1С автоматически сопоставит имена колонок из структуры полученного дерева с именами колонок, настроенных в интерфейсе табличного поля, сохранив все ваши визуальные настройки (шрифты, цвета, ширину и доступность).
Проанализируем конструкцию запроса, которую использовал автор темы:
ГДЕ НЕ Номенклатура.ЭтоГруппа
...
ИТОГИ ПО Ссылка ИЕРАРХИЯ
Здесь кроется важная особенность работы платформы 1С. Условие ГДЕ НЕ Номенклатура.ЭтоГруппа полностью исключает папки справочника из детальных записей выборки. Однако использование конструкции ИТОГИ ПО Ссылка ИЕРАРХИЯ заставляет платформу принудительно воссоздавать родительские группы в дереве значений, чтобы сохранить корректную древовидную структуру.
Что важно помнить в этом случае? Для строк-родителей (которые являются группами) все остальные выбранные в запросе реквизиты (например, код, артикул, цена, если бы они были в выборке) будут содержать значение NULL или пустые значения. Заполнено будет только ключевое поле иерархии — Ссылка. Учитывайте это при выводе дополнительных данных в строки дерева.
По умолчанию при заполнении табличного поля все узлы дерева выводятся в свернутом виде. Пользователю приходится кликать на каждый «плюсик», чтобы увидеть подчиненные элементы. Рассмотрим, как сделать интерфейс более дружелюбным.
Вариант А. Настройка свойства в Конфигураторе
У элемента управления ТабличноеПоле есть замечательное свойство Начальное отображение дерева (в коде доступно как НачальноеОтображениеДерева). Вы можете установить его значение в:
РаскрыватьВерхнийУровень — раскроет только самые первые ветки;РаскрыватьВсеУровни — полностью развернет все узлы дерева при его выводе.Программно это можно сделать одной строкой:
ЭлементыФормы.ТабличноеПоле1.НачальноеОтображениеДерева = НачальноеОтображениеДерева.РаскрыватьВсеУровни;
Вариант Б. Программный обход и выборочное раскрытие
Если вам нужно развернуть только определенные строки дерева по условию, воспользуйтесь методом табличного поля Развернуть(). Рассмотрим пример функции, которая рекурсивно обходит строки дерева и разворачивает их:
Процедура РазвернутьСтрокиДереваРекурсивно(ЭлементТабличноеПоле, СтрокиДерева)
Для Каждого Строка Из СтрокиДерева Цикл
// Разворачиваем текущую строку
ЭлементТабличноеПоле.Развернуть(Строка, Истина);
// Переходим к обработке подчиненных строк
РазвернутьСтрокиДереваРекурсивно(ЭлементТабличноеПоле, Строка.Строки);
КонецЦикла;
КонецПроцедуры
// Пример вызова процедуры для всей коллекции верхнего уровня:
РазвернутьСтрокиДереваРекурсивно(ЭлементыФормы.ТабличноеПоле1, ДеревоДанных.Строки);
В сообщениях 5 и 6 на форуме возникла путаница из-за предложения использовать методы управляемых форм на обычных формах. Давайте проясним эту разницу, чтобы вы легко ориентировались в обеих технологиях.
На управляемых формах напрямую присвоить дерево значений элементу формы (как мы делали в Способе 1) нельзя, так как на клиенте объект ДеревоЗначений недоступен. Процесс выглядит совершенно иначе:
ДеревоЗначений (например, с именем ДеревоНаФорме).ЗначениеВРеквизитФормы:
&НаСервере
Процедура ЗаполнитьДеревоНаСервере()
Запрос = Новый Запрос("ВЫБРАТЬ Номенклатура.Ссылка КАК Ссылка ИЗ Справочник.Номенклатура");
ДеревоИзЗапроса = Запрос.Выполнить().Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкамСИерархией);
// Переносим данные в реквизит управляемой формы
ЗначениеВРеквизитФормы(ДеревоИзЗапроса, "ДеревоНаФорме");
КонецПроцедуры
Как видим, в управляемом интерфейсе разработчик полностью изолирован от прямого обращения к элементам формы для передачи данных, в то время как на обычных формах мы напрямую взаимодействуем с коллекцией ЭлементыФормы. Для реализации динамической структуры данных в тонком клиенте можно изучить пример программного вывода произвольной таблицы значений на управляемую форму.
Используйте предложенные нами способы программного вывода деревьев значений, адаптируйте их под свои задачи автоматизации и создавайте удобные интерфейсы для пользователей вашей системы!