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

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

Работа с ДеревомЗначений в Обычных формах 1С 8.2 (и 8.3) часто вызывает трудности у начинающих разработчиков. В отличие от простых таблиц, дерево обладает иерархической структурой, что делает стандартное перемещение строк "вверх" и "вниз" нетривиальной задачей. Рассмотрим подробно, как реализовать этот механизм, разберем типичные ошибки и предложим готовые алгоритмы для различных ситуаций.

Метод Сдвинуть(): перемещение внутри одного уровня

Для начала разберем самый простой сценарий: нам нужно поменять местами две строки, которые находятся "внутри" одного и того же родителя. Для этого в платформе предусмотрен встроенный метод коллекции строк — Сдвинуть(). Проанализируем его синтаксис: он принимает индекс строки и числовой параметр смещения.

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

Код для перемещения строки вниз:


Процедура КнопкаВнизНажатие(Элемент)
    // Получаем текущую строку из элемента формы
    ТекущаяСтрока = ЭлементыФормы.ГруппировкиНаПечать.ТекущаяСтрока;
    Если ТекущаяСтрока = Неопределено Тогда
        Возврат;
    КонецЕсли;

    // Определяем родителя. Если родителя нет, работаем с корнем дерева
    РодительТекущейСтроки = ТекущаяСтрока.Родитель;
    КоллекцияСтрок = ?(РодительТекущейСтроки = Неопределено, ГруппировкиНаПечать.Строки, РодительТекущейСтроки.Строки);

    ИндексТекущейСтроки = КоллекцияСтрок.Индекс(ТекущаяСтрока);
    
    // Проверяем, не является ли строка последней в своем списке
    Если ИндексТекущейСтроки < (КоллекцияСтрок.Количество() - 1) Тогда
        КоллекцияСтрок.Сдвинуть(ТекущаяСтрока, 1);
    Иначе
        Сообщить("Строка уже находится в самом низу текущего уровня!");
    КонецЕсли;
КонецПроцедуры

Код для перемещения строки вверх:


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

    РодительТекущейСтроки = ТекущаяСтрока.Родитель;
    КоллекцияСтрок = ?(РодительТекущейСтроки = Неопределено, ГруппировкиНаПечать.Строки, РодительТекущейСтроки.Строки);

    ИндексТекущейСтроки = КоллекцияСтрок.Индекс(ТекущаяСтрока);

    // Проверяем, не является ли строка первой
    Если ИндексТекущейСтроки > 0 Тогда
        КоллекцияСтрок.Sдвинуть(ТекущаяСтрока, -1);
    Иначе
        Сообщить("Выше двигаться нельзя, это начало уровня.");
    КонецЕсли;
КонецПроцедуры

Смена иерархии: перемещение ветки на другой уровень

Ситуация усложняется, когда нам нужно "вытащить" строку из подчинения и сделать ее на уровень выше (или наоборот — подчинить соседней строке). Метод Сдвинуть() здесь бессилен, так как он работает только внутри одной коллекции. Проанализируем ситуацию: чтобы перенести строку в другой узел, ее нужно фактически создать заново в целевом узле и удалить из старого.

Главная опасность здесь — потеря подчиненных строк (потомков). Если мы просто удалим родителя, все вложенные данные исчезнут. Поэтому мы будем использовать рекурсию. Разберем по шагам алгоритм переноса всей ветки дерева.

Рекурсивный алгоритм переноса ветки

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


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

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


Процедура КнопкаПоднятьПоИерархии(Элемент)
    ТекущаяСтрока = ЭлементыФормы.ГруппировкиНаПечать.ТекущаяСтрока;
    
    Если ТекущаяСтрока = Неопределено Тогда
        Возврат;
    КонецЕсли;
    
    Если ТекущаяСтрока.Родитель = Неопределено Тогда
        Сообщить("Строка уже на верхнем уровне!");
        Возврат;
    КонецЕсли;

    // Определяем, куда переносим. 
    // Если родитель текущей строки сам является корневым, то "дедушка" - это все Дерево.
    СтарыйРодитель = ТекущаяСтрока.Родитель;
    НовыйРодитель = ?(СтарыйРодитель.Родитель = Неопределено, ГруппировкиНаПечать, СтарыйРодитель.Родитель);

    // Запускаем процесс копирования
    ПеренестиВеткуРекурсивно(ТекущаяСтрока, НовыйРодитель);

    // После успешного копирования удаляем старую ветку
    СтарыйРодитель.Строки.Удалить(ТекущаяСтрока);
КонецПроцедуры

Типичные ошибки и важные нюансы

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

  1. Бесконечная рекурсия: Никогда не пытайтесь добавить строку внутрь самой себя. Перед началом перемещения всегда проверяйте, не является ли целевой узел потомком перемещаемого узла. Когда программная реализация в интерфейсе слишком сложна, альтернативой может стать настройка робота на платформе One RPA для автоматизации действий пользователя.
  2. Потеря фокуса: После удаления строки и создания ее копии методом рекурсии, свойство ТекущаяСтрока табличного поля обнулится. Чтобы пользователю было комфортно, программно устанавливайте фокус на новую строку: ЭлементыФормы.Дерево.ТекущаяСтрока = СозданнаяСтрока;.
  3. Ошибка "Неправильное смещение": Всегда делайте проверку на границы индекса (Количество() - 1 для движения вниз и 0 для движения вверх). Платформа 1С не "прощает" выход за границы коллекции и выдает исключение.
  4. Динамические колонки: Если колонки дерева созданы программно (как в примере из темы), убедитесь, что при ЗаполнитьЗначенияСвойств() типы данных в новой строке совпадают с типами в старой.

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

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

← На главную