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

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

Реализация механизма Drag-and-Drop (перетаскивания) в дереве значений на обычных формах 1С:Предприятие 8 — это задача, требующая четкого понимания жизненного цикла событий элемента управления и особенностей работы с объектом ДеревоЗначений. В отличие от простых списков, дерево подразумевает иерархическую структуру, что накладывает дополнительные ограничения при перемещении элементов.

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

Последовательность событий при перетаскивании

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

  1. НачалоПеретаскивания — срабатывает в источнике (тот элемент, который мы начали тянуть). Здесь мы определяем, что именно передается.
  2. ПроверкаПеретаскивания — срабатывает в приемнике (тот элемент, над которым находится курсор). Здесь мы разрешаем или запрещаем «бросок» в конкретную точку.
  3. Перетаскивание — основное событие в приемнике, где происходит фактическое изменение данных.
  4. ОкончаниеПеретаскивания — финальное событие в источнике, где можно выполнить действия после завершения процесса (например, очистить временные переменные).

Шаг 1. Настройка элемента управления

Для начала разберем настройки самого табличного поля, отображающего дерево значений. Чтобы механизм заработал, в свойствах элемента управления (на форме) необходимо установить флаги РазрешитьНачалоПеретаскивания и РазрешитьПеретаскивание. Без этих галочек платформа не будет инициализировать соответствующие события. При настройке свойств элементов управления и изучении доступных событий можно использовать шпаргалку по разработке обычных и управляемых форм.

Шаг 2. Обработка начала перетаскивания

Рассмотрим подробнее событие НачалоПеретаскивания. В этом обработчике нам нужно поместить перетаскиваемую строку в параметры. Посмотрим на пример кода:


Процедура ДеревоЗначенийНачалоПеретаскивания(Элемент, ПараметрыПеретаскивания, СтандартнаяОбработка)
    
    // Передаем текущую строку в параметры перетаскивания
    ПараметрыПеретаскивания.Значение = Элемент.ТекущаяСтрока;
    
    // Определяем разрешенные действия (копирование или перемещение)
    ПараметрыПеретаскивания.ДопустимыеДействия = ДопустимыеДействияПеретаскивания.Перемещение;
    
КонецПроцедуры

Важный момент: если у вас включено множественное выделение, в ПараметрыПеретаскивания.Значение следует передавать массив строк.

Шаг 3. Проверка возможности сброса

Теперь проанализируем событие ПроверкаПеретаскивания. Это «фильтр», который определяет визуальный отклик (вид курсора). Здесь мы должны реализовать бизнес-логику: можно ли переносить этот элемент в выбранную папку?

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


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

Шаг 4. Реализация перемещения строки

Перейдем к самому сложному этапу — событию Перетаскивание. Нужно понимать технический нюанс: в 1С:Предприятии 8 свойство Родитель у строки дерева значений доступно только для чтения. Мы не можем просто написать Строка.Родитель = НоваяГруппа.

Вместо этого мы применим следующий алгоритм:

  1. Создадим новую строку в коллекции строк целевого родителя.
  2. Скопируем данные из старой строки в новую.
  3. Рекурсивно перенесем всех «детей» (подчиненные строки).
  4. Удалим исходную строку.

Разберем, как это реализовать с помощью вспомогательной процедуры. Сначала основной обработчик:


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

Шаг 5. Рекурсивное копирование данных

Чтобы перенос работал для веток любой вложенности, нам понадобится рекурсивная функция. Она будет создавать структуру в новом месте, проходя сверху вниз по всем уровням иерархии.


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

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

Дополнительные советы и оптимизация

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

  1. Обновление интерфейса: После массового удаления и добавления строк дерево может «схлопнуться». Если вы хотите сохранить состояние развернутости узлов, вам придется программно обходить дерево и использовать метод Развернуть() для новых строк.
  2. Смещение при вставке: В событии Перетаскивание можно анализировать, куда именно упал элемент — «на строку» или «между строк». Для этого используются параметры координат, что позволяет не только менять родителя, но и изменять порядок строк внутри одной группы (используя метод Вставить() вместо Добавить()).
  3. Транзакции: Если дерево значений связано с данными в базе (например, это дерево подразделений), все действия по перемещению обязательно должны быть обернуты в транзакцию на сервере.

Таким образом, мы выяснили, что перетаскивание в «обычных формах» — это не просто смена ссылки на родителя, а полноценная операция трансформации структуры данных. Следуя описанной последовательности событий и используя рекурсивный метод копирования, вы сможете реализовать интуитивно понятный и стабильный интерфейс для пользователей вашей конфигурации.

← На главную