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