Как при перетаскивании в динамический список узнать, в какую строку сбросили элемент?

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

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

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

Теоретические основы: как работает перетаскивание в таблицах формы

В управляемых формах 1С за прием перетаскиваемых данных отвечают два ключевых события таблицы формы (которая может отображать как динамический список, так и таблицу значений или дерево значений):

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

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


&НаКлиенте
Процедура СписокПолучательПеретаскивание(Элемент, ПараметрыПеретаскивания, СтандартнаяОбработка, Строка, Поле)
    // Здесь пишется код обработки
КонецПроцедуры

Давайте подробно разберем назначение параметров, играющих ключевую роль в решении нашей задачи:

Особенности параметра «Строка» для разных типов данных

Тип значения, возвращаемый в параметре Строка, напрямую зависит от источника данных, отображаемого в таблице формы:

Пошаговый алгоритм реализации перетаскивания

Давайте рассмотрим комплексный практический пример. Предположим, у нас есть список проектов (Динамический список 1 - Получатель) и список задач (Динамический список 2 - Источник). Наша цель — перетащить задачу из второго списка на конкретный проект в первом списке, чтобы автоматически изменить реквизит Проект у этой задачи в базе данных.

Шаг 1. Настройка свойств элементов формы

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

  1. Для таблицы-источника (список задач) установите свойство РазрешитьНачалоПеретаскивания в значение Истина.
  2. Для таблицы-получателя (список проектов) установите свойство РазрешитьПеретаскивание в значение Истина.

Шаг 2. Визуальный контроль в событии «ПроверкаПеретаскивания»

Для того чтобы пользователь сразу видел, на какие строки списка-получателя допускается сбрасывать элементы, настроим обработку события ПроверкаПеретаскивания. Например, мы хотим запретить сброс задач в «пустую область» списка или на проекты, которые имеют признак закрытых (архивных).

Рассмотрим пример реализации проверки на клиенте:


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

Метод ДанныеСтроки() элемента формы позволяет прочитать реквизиты строки динамического списка непосредственно на клиенте без обращения к серверу. Это обеспечивает мгновенный отклик интерфейса при перемещении курсора.

Шаг 3. Обработка события «Перетаскивание»

Теперь реализуем непосредственно сам процесс переноса данных и изменения реквизитов объектов в базе данных. Когда пользователь отпускает левую кнопку мыши над целевой строкой, срабатывает событие Перетаскивание.

В параметре ПараметрыПеретаскивания.Значение по умолчанию передается массив ссылок на выделенные в источнике строки (перетаскивать можно сразу несколько объектов). Нам нужно обойти этот массив, передать данные на сервер и обновить динамические списки.

Проанализируем пример кода:


&НаКлиенте
Процедура СписокПолучательПеретаскивание(Элемент, ПараметрыПеретаскивания, СтандартнаяОбработка, Строка, Поле)
    
    // Отменяем стандартную обработку платформы
    СтандартнаяОбработка = Ложь;
    
    // Если сбросили мимо строк, ничего не делаем
    Если Строка = Неопределено Тогда
        Возврат;
    КонецЕсли;
    
    // Получаем данные целевого проекта из строки-получателя
    ДанныеПроекта = Элемент.ДанныеСтроки(Строка);
    Если ДанныеПроекта = Неопределено Тогда
        Возврат;
    КонецЕсли;
    
    СсылкаНаПроект = ДанныеПроекта.Ссылка;
    
    // Проверяем, что переносимые данные корректны и содержат массив
    МассивПереносимыхЗадач = Неопределено;
    Если ТипЗнч(ПараметрыПеретаскивания.Значение) = Тип("Массив") Тогда
        МассивПереносимыхЗадач = ПараметрыПеретаскивания.Значение;
    КонецЕсли;
    
    Если МассивПереносимыхЗадач <> Неопределено И МассивПереносимыхЗадач.Количество() > 0 Тогда
        
        // Вызываем серверную процедуру для изменения данных в БД
        ИзменитьПроектУЗадачНаСервере(МассивПереносимыхЗадач, СсылкаНаПроект);
        
        // Обновляем динамические списки на форме, чтобы пользователь сразу увидел изменения
        Элементы.СписокПолучатель.Обновить();
        Элементы.СписокИсточник.Обновить();
        
        ПоказатьОповещениеПользователя("Успешно", , "Задачи перепривязаны к новому проекту!");
        
    КонецЕсли;
    
КонецПроцедуры

Шаг 4. Серверная обработка изменения данных

Для изменения реквизитов перенесенных объектов в базе данных необходимо выполнить транзакционную запись на сервере. Чтобы не перегружать контекст формы, рекомендуется использовать внеконтекстную процедуру &НаСервереБезКонтекста:


&НаСервереБезКонтекста
Процедура ИзменитьПроектУЗадачНаСервере(МассивЗадач, СсылкаНаНовыйПроект)
    
    Для Каждого СсылкаНаЗадачу Из МассивЗадач Цикл
        
        // Проверяем тип, чтобы исключить ошибки
        Если ТипЗнч(СсылкаНаЗадачу) = Тип("СправочникСсылка.Задачи") Тогда
            
            // Блокируем объект перед изменением
            ОбъектЗадача = СсылкаНаЗадачу.ПолучитьОбъект();
            Если ОбъектЗадача <> Неопределено Тогда
                
                // Устанавливаем новый связанный проект
                ОбъектЗадача.Проект = СсылкаНаНовыйПроект;
                
                Попытка
                    ОбъектЗадача.Записать();
                Исключение
                    ЗаписьЖурналаРегистрации("Перетаскивание.ОшибкаЗаписи",
                        УровеньЖурналаРегистрации.Ошибка, , ,
                        ОписаниеОшибки());
                    ВызватьИсключение "Не удалось записать изменения по задаче: " + СсылкаНаЗадачу;
                КонецПопытки;
                
            КонецЕсли;
            
        КонецЕсли;
        
    КонецЦикла;
    
КонецПроцедуры

Продвинутый прием: оптимизация через событие «НачалоПеретаскивания»

Иногда при перетаскивании требуется передать не просто ссылки на переносимые элементы, а сразу структуру дополнительных данных, чтобы избежать повторных обращений к базе данных на этапе проверки или завершения переноса. Для этого мы можем переопределить поведение источника в событии НачалоПеретаскивания.

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


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

Использование такого подхода позволяет в обработчике ПроверкаПеретаскивания на стороне получателя моментально анализировать свойства перетаскиваемого объекта (например, запретить перенос просроченных задач или задач с высокой важностью на определенные типы проектов) абсолютно без серверных вызовов!

Подводя итог, можно утверждать, что использование параметра Строка в сочетании с методом ДанныеСтроки() дает разработчику полный контроль над поведением интерфейса управляемых форм при реализации сложных сценариев Drag-and-Drop в 1С.

← На главную