Дерево значений в 1С 8.3

Объект «ДеревоЗначений» в 1С 8.3 представляет собой динамический набор значений любого типа. Для такого набора доступны свойства «колонки» и «строки» где каждая строка может иметь набор подчинённых строк, а каждая подчинённая строка набор своих подчинённых строк и т.д.  Программная инициализация дерева значений в 1С осуществляется с помощью оператора НОВЫЙ.

Пример:

ДеревоЗначений = Новый ДеревоЗначений;

Визуально, объект «ДеревоЗначений» похож на дерево групп справочника. К примеру, можно открыть справочник «Номенклатура» любой типовой конфигурации выставив форму списка в «Режим просмотра» — «Дерево».

 

Полезно понимать, что объект «ДеревоЗначений» схож с объектом «ТаблицаЗначений». Основной отличительный признак наличие дополнительного реквизита «Родитель» у объекта «ДеревоЗначений», с помощью которого и организовывается иерархия и подчинённость.

Дерево значений на управляемой форме

Если появляется необходимость показать данные в виде дерева пользователю, следует на форме создать реквизит с типом «ДеревоЗначений» и определится с составом колонок.

Для отображения на форме добавим реквизит в раздел элементов формы простым перетаскиванием.

Заполнение дерева значений

Заполнить данными дерево значений в 1С можно программно (вручную) или получить из результата запроса. Рассмотрим на примерах оба способа.

Ручное заполнение реквизита формы с типом «ДеревоЗначений»

Простой пример:

&НаКлиенте
Процедура ДЗ_НаФорме(Команда)
ДЗ_НаФормеНаСервере();
КонецПроцедуры

&НаСервере
Процедура ДЗ_НаФормеНаСервере()

// Преобразуем реквизит формы ДЗ в объект прикладного типа
ДеревоЗначений = РеквизитФормыВЗначение("ДЗ");

Корень = ДеревоЗначений.Строки.Добавить();
Корень.Наименование = "Корневая группа";

Группа1 = Корень.Строки.Добавить();
Группа1.Наименование = "Первая группа";

Элемент1 = Группа1.Строки.Добавить();
Элемент1.Наименование = "Элемент №1";                               

Группа2 = Корень.Строки.Добавить();
Группа2.Наименование = "Втровая группа";

Элемент1 = Группа2.Строки.Добавить();
Элемент1.Наименование = "Элемент №1";

Элемент2 = Группа2.Строки.Добавить();
Элемент2.Наименование = "Элемент №2";
        
// Преобразуем объект ДеревоЗначений в реквизит формы         
ЗначениеВРеквизитФормы(ДеревоЗначений,"ДЗ");                                                                     

КонецПроцедуры

Результат запроса на форме:

Заполнение дерева значений из результата запроса

Сделаем простой запрос к справочнику «Номенклатура». Результат запроса преобразуем в дерево значений с помощью метода «Выгрузить», установив для его параметра «ТипОбхода» в значение «ПоГруппировкамСИерархией».  Для передачи сформированного дерева значений на форму, важно чтобы имена колонок совпадали с именами колонок реквизита на форме.

&НаКлиенте
Процедура ДЗ_НаФорме(Команда)
ДЗ_НаФормеНаСервере();
КонецПроцедуры

&НаСервере
Процедура ДЗ_НаФормеНаСервере()

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

ЗначениеВРеквизитФормы(ДеревоЗначений, "ДЗ");

КонецПроцедуры

Результат запроса на форме может выглядеть так:

Поиск строк в дереве значений

Для поиска строк в дереве значений существует два способа, точнее говоря, метода.

Метод «Найти()»

  • Найти(<Значение>, <Колонки>, <ВключатьПодчиненные>)

Возвращает строку дерева значений, если строка не найдена, вернёт «Неопределено». Поиск можно ограничивать, указав в параметре <Колонки> нужные колонки для поиска. Также расширять, указав для параметра <ВключатьПодчиненные> значение «Истина», тогда в поиске будут участвовать строки подчинённых коллекций.

Для примера найдём строку со значением «Элемент №1» в дереве значений вида:

Пример кода:

&НаКлиенте
Процедура НайтиСтрокуДЗ(Команда)
            НайтиСтрокуДЗ_НаСервере();
КонецПроцедуры

&НаСервере
Процедура НайтиСтрокуДЗ_НаСервере()

// Преобразуем реквизит формы ДЗ в объект прикладного типа
ДеревоЗначений = РеквизитФормыВЗначение("ДЗ");

// Ищем строку
НайденнаяСтрока = ДеревоЗначений.Строки.Найти("Элемент №1","Наименование", Истина);

// Анализ результата поиска
Если НайденнаяСтрока = Неопределено Тогда
Сообщить("Строка не найдена");
Иначе
Сообщить(НайденнаяСтрока.Наименование + " - " + НайденнаяСтрока.Родитель.Наименование);
КонецЕсли;
          
КонецПроцедуры

Результат:

Из результата видно, что метод вернул первую попавшуюся строку, и поиск был прекращён.

Метод «НайтиСтроки()»

Если необходимо найти все строки со значением «Элемент №1» тогда следует использовать метод НайтиСтроки()

  • НайтиСтроки(<ПараметрыОтбора>, <ВключатьПодчиненные>)

Где <ПараметрыОтбора> — это условия для поиска в виде структуры, а параметр <ВключатьПодчиненные> с помощью значений Ложь/Истина контролирует возможность поиска в подчинённых коллекциях.

Пример кода:

&НаКлиенте
Процедура НайтиСтрокиДЗ(Команда)
НайтиСтрокиДЗНаСервере();
КонецПроцедуры

&НаСервере
Процедура НайтиСтрокиДЗНаСервере()

// Преобразуем реквизит формы ДЗ в объект прикладного типа
ДеревоЗначений = РеквизитФормыВЗначение("ДЗ");

// Условие для поиска
ПараметрыОтбора = Новый Структура;
ПараметрыОтбора.Вставить("Наименование", "Элемент №1");

// Поиск строк
МассивСтрок = ДеревоЗначений.Строки.НайтиСтроки(ПараметрыОтбора, Истина);

// Проверяем найдены ли строки
Если МассивСтрок.Количество() = 0 Тогда
Сообщить("Не найдено ни одной строки, удовлетворяющей условия поиска.");
КонецЕсли;

// Перебор массива строк
Для Каждого Строка Из МассивСтрок Цикл

Если Строка.Родитель = Неопределено Тогда
Сообщить(Строка.Наименование + " - Корень дерева значений.");
Иначе
Сообщить(Строка.Наименование + " - " + Строка.Родитель.Наименование);
КонецЕсли

КонецЦикла;

КонецПроцедуры

Результат:

Очистка дерева значений или строк

Для очистки дерева значений пригодятся методы  Очистить(), Удалить(). Пример кода:

&НаСервере
Процедура УдалитьСтрокуНаСервере()

// Преобразуем реквизит формы ДЗ в объект прикладного типа
ДеревоЗначений = РеквизитФормыВЗначение("ДЗ");

ДеревоЗначений.Строки.Очистить();

// Или по индексу
ДеревоЗначений.Строки.Удалить(0);

// Преобразуем объект ДеревоЗначений в реквизит формы
ЗначениеВРеквизитФормы(ДеревоЗначений, "ДЗ");

КонецПроцедуры

С помощью данных методов возможно удаление конкретных строк дерева значений.

!!!При удалении либо очистки строки все её подчинённые строки удалятся!!!

Обход дерева значений в 1С

Обойти все строки дерева значений удобнее всего с помощью рекурсии.  Таким способом мы сможем обойти дерево значений любой вложенности.

Пример:

&НаСервере
Процедура ОбойтиДЗ_НаСервере()
ДеревоЗначений = РеквизитФормыВЗначение("ДЗ");
ОбходДереваЗначений(ДеревоЗначений);
КонецПроцедуры

&НаСервере
Процедура ОбходДереваЗначений(Данные)

Для Каждого Строка Из Данные.Строки Цикл

Сообщить(Строка.Наименование);

Если Строка.Строки.Количество()>0 Тогда
ОбходДереваЗначений(Строка);
КонецЕсли;

КонецЦикла;

КонецПроцедуры

Как преобразовать дерево значений в таблицу значений

Состав элементов и реквизитов формы:

&НаКлиенте
Процедура ВТЗ(Команда)
ВТЗНаСервере();
КонецПроцедуры

&НаСервере
Процедура ВТЗНаСервере()


ДеревоЗначений = РеквизитФормыВЗначение("ДЗ");
ТаблицаЗначений = РеквизитФормыВЗначение("ТЗ");

ПреобразоватьВ_ТЗ(ДеревоЗначений, ТаблицаЗначений, Новый УникальныйИдентификатор("00000000-0000-0000-0000-000000000000"));
ДеревоЗначений.Строки.Очистить();

ЗначениеВРеквизитФормы(ТаблицаЗначений, "ТЗ");
ЗначениеВРеквизитФормы(ДеревоЗначений, "ДЗ");
КонецПроцедуры

&НаСервере
Процедура ПреобразоватьВ_ТЗ(Данные, ТаблицаЗначений, ГУИД)

Для Каждого Строка Из Данные.Строки Цикл

НовСтрока = ТаблицаЗначений.Добавить();
НовСтрока.Наименование = Строка.Наименование;
НовСтрока.Родитель = ГУИД;
НовСтрока.ГУИД = Новый УникальныйИдентификатор();

Если Строка.Строки.Количество()>0 Тогда
ПреобразоватьВ_ТЗ(Строка, ТаблицаЗначений, НовСтрока.ГУИД);
КонецЕсли;

КонецЦикла;

КонецПроцедуры

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