Как получить подчиненные строки дерева на управляемой форме 1С и отметить их флажками?

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

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

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

Архитектурные ограничения: табличная часть и дерево значений

Проанализируем, почему не получается получить подчиненные строки напрямую из табличной части:

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

Основное решение: использование ДанныеФормыДерево и рекурсивный обход на клиенте

Рассмотрим правильный подход к организации дерева и установке флажков. Все манипуляции с отметками строк в интерфейсе лучше выполнять на клиенте без лишних серверных вызовов — это обеспечивает высокую скорость отклика формы.

Разберем по шагам, как это устроено:

  1. Создаем в реквизитах формы объект ДеревоДанных с типом ДеревоЗначений, добавляем колонку Пометка (тип Булево) и необходимые информационные колонки.
  2. Переносим реквизит в элементы формы как таблицу.
  3. В событии элемента формы ДеревоДанныхПометкаПриИзменении получаем текущую выбранную строку через Элементы.ДеревоДанных.ТекущиеДанные.
  4. Рекурсивно обходим подчиненные элементы, используя метод ПолучитьЭлементы().

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


&НаКлиенте
Процедура ДеревоДанныхПометкаПриИзменении(Элемент)
    
    ТекущаяСтрока = Элементы.ДеревоДанных.ТекущиеДанные;
    Если ТекущаяСтрока = Неопределено Тогда
        Возврат;
    КонецЕсли;
    
    // Устанавливаем такое же значение пометки для всех дочерних строк
    УстановитьПометкуДочернимЭлементам(ТекущаяСтрока, ТекущаяСтрока.Пометка);
    
КонецПроцедуры

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

Обратите внимание: проверка на количество элементов перед циклом не требуется. Если у строки нет дочерних элементов, коллекция ПодчиненныеСтроки будет пустой, и цикл Для Каждого просто не выполнится, что является естественным условием выхода из рекурсии.

Управление родительскими строками при изменении дочерних

Часто требуется не только отмечать подчиненные строки, но и снимать или ставить пометку у родительских элементов, если изменилось состояние дочернего. Для перехода к вышестоящему уровню у объекта ДанныеФормыЭлементДерева существует метод ПолучитьРодителя().

Рассмотрим пример обновления состояния родителей вверх по иерархии:


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

Альтернативный вариант: плоская табличная часть со связями по ключам

Если по условиям задачи данные обязательно должны оставаться в плоской табличной части объекта (например, Объект.Состав), иерархию можно смоделировать программно:

  1. Добавляем в табличную часть две колонки: ИдентификаторСтроки (тип Число или УникальныйИдентификатор) и ИдентификаторРодителя.
  2. При изменении флажка обходим коллекцию строк формы и ищем те, у которых ИдентификаторРодителя совпадает с ИдентификаторСтроки текущей строки.

Посмотрим на пример реализации такого поиска:


&НаКлиенте
Процедура ТабличнаяЧастьПометкаПриИзменении(Элемент)
    
    ТекущаяСтрока = Элементы.ТабличнаяЧасть.ТекущиеДанные;
    Если ТекущаяСтрока = Неопределено Тогда
        Возврат;
    КонецЕсли;
    
    ОтметитьПодчиненныеВПлоскойТаблице(ТекущаяСтрока.ИдентификаторСтроки, ТекущаяСтрока.Пометка);
    
КонецПроцедуры

&НаКлиенте
Процедура ОтметитьПодчиненныеВПлоскойТаблице(ИДРодителя, ЗначениеПометки)
    
    Для Каждого СтрокаТЧ Из Объект.ТабличнаяЧасть Цикл
        Если СтрокаТЧ.ИдентификаторРодителя = ИДРодителя Тогда
            СтрокаТЧ.Пометка = ЗначениеПометки;
            // Рекурсивный поиск для следующего уровня
            ОтметитьПодчиненныеВПлоскойТаблице(СтрокаТЧ.ИдентификаторСтроки, ЗначениеПометки);
        КонецЕсли;
    КонецЦикла;
    
КонецПроцедуры

Использование реквизита формы с типом ДеревоЗначений является предпочтительным стандартом для управляемых форм 1С, так как платформа берет на себя визуальное построение веток, а разработчик получает прямой доступ к методам ПолучитьЭлементы() и ПолучитьРодителя().

← На главную