Перетаскивание объектов в управляемых формах 1С: способы и рабочий код
Содержание
Перетаскивание (Drag & Drop) в 1С 8.3 делают не кодом «с нуля», а через штатные события управляемой формы: НачалоПеретаскивания, ПроверкаПеретаскивания и Перетаскивание. Чтобы сделать перетаскивание, достаточно разрешить его у элемента-таблицы в свойствах и описать два-три клиентских обработчика — строки начнут переноситься мышью.
Важная оговорка сразу: динамический список мышью не переупорядочить — его порядок задаёт запрос. Менять порядок перетаскиванием можно у таблицы значений на форме и у табличной части документа, а переносить элементы между списками — почти у любых таблиц.
Рабочих способов три: штатный механизм управляемой формы для списков и табличных частей, HTML-поле для канбана и свободного размещения карточек, внешняя компонента для нестандартных сценариев. Ниже — какой когда брать, с проверяемым кодом для первого (он закрывает большинство задач).
Как платформа обрабатывает перетаскивание
Перетаскивание в управляемой форме — это цепочка из четырёх событий у элемента формы. Два из них срабатывают у источника (откуда тащим), два — у приёмника (куда бросаем):
- 🎯
НачалоПеретаскивания— у источника, когда пользователь «взял» строку. Здесь вы кладёте в перенос нужные данные. - 🔎
ПроверкаПеретаскивания— у приёмника, пока курсор движется над целью. Здесь вы решаете, разрешить ли бросок и каким будет действие (перемещение или копирование). - 📥
Перетаскивание— у приёмника, в момент броска. Здесь выполняется основная работа: добавить строку, сдвинуть, переназначить группу. - 🔚
ОкончаниеПеретаскивания— у источника, после завершения. Нужно редко — например, чтобы удалить строку из источника при перемещении.
Данные между событиями ходят через объект ПараметрыПеретаскивания. У него два ключевых свойства: Значение — то, что переносим (вы задаёте его сами в НачалоПеретаскивания), и Действие — тип операции из набора ТипДействияПеретаскивания (Перемещение, Копирование, Отмена, НеОбрабатывать).
И общий приём на все обработчики: если вы делаете перенос своим кодом, ставьте СтандартнаяОбработка = Ложь. Иначе платформа попытается выполнить перенос по-своему поверх вашего — и результат будет непредсказуемым.
Включаем перетаскивание и переставляем строки мышью
Самая частая задача — дать пользователю менять порядок строк в таблице на форме (список задач, приоритеты, этапы). Разберём на таблице значений Задачи, выведенной на форму элементом с тем же именем.
Шаг 1. Разрешите перетаскивание у элемента. В конфигураторе откройте форму, выделите элемент-таблицу Задачи и в палитре свойств (группа «Использование») включите начало перетаскивания у источника и приём у приёмника — для одной таблицы это один и тот же элемент. Названия свойств в разных версиях платформы могут отличаться; ориентируйтесь на пункты, отвечающие за перетаскивание.
Шаг 2. Запомните, какую строку тащим. В НачалоПеретаскивания положите в перенос идентификатор текущей строки:
&НаКлиенте
Процедура ЗадачиНачалоПеретаскивания(Элемент, ПараметрыПеретаскивания, СтандартнаяОбработка)
ПараметрыПеретаскивания.Значение = Элементы.Задачи.ТекущаяСтрока;
КонецПроцедуры
Элементы.Задачи.ТекущаяСтрока возвращает идентификатор строки — по нему позже найдём её в коллекции.
Шаг 3. Разрешите бросок. В ПроверкаПеретаскивания задайте действие, иначе курсор покажет «запрещено»:
&НаКлиенте
Процедура ЗадачиПроверкаПеретаскивания(Элемент, ПараметрыПеретаскивания, СтандартнаяОбработка, Строка, Поле)
ПараметрыПеретаскивания.Действие = ТипДействияПеретаскивания.Перемещение;
КонецПроцедуры
Шаг 4. Переставьте строку в момент броска. Параметр Строка — идентификатор строки-приёмника (или Неопределено, если бросили не на строку):
&НаКлиенте
Процедура ЗадачиПеретаскивание(Элемент, ПараметрыПеретаскивания, СтандартнаяОбработка, Строка, Поле)
СтандартнаяОбработка = Ложь;
ИсходнаяСтрока = Задачи.НайтиПоИдентификатору(ПараметрыПеретаскивания.Значение);
Если ИсходнаяСтрока = Неопределено Тогда
Возврат;
КонецЕсли;
Если Строка = Неопределено Тогда
// бросили на пустое место — ставим в конец
Смещение = Задачи.Количество() - 1 - Задачи.Индекс(ИсходнаяСтрока);
Иначе
СтрокаПриёмник = Задачи.НайтиПоИдентификатору(Строка);
Смещение = Задачи.Индекс(СтрокаПриёмник) - Задачи.Индекс(ИсходнаяСтрока);
КонецЕсли;
Задачи.Сдвинуть(ИсходнаяСтрока, Смещение);
КонецПроцедуры
Весь код — на клиенте: события перетаскивания клиентские, к базе здесь не обращаемся. Если порядок нужно сохранить в реквизите (например, Приоритет), пересчитайте его одним проходом после сдвига и запишите на сервере один раз — не на каждое движение мыши.
Табличная часть документа работает так же: выводите её на форму таблицей и вешаете те же обработчики на элемент ТЧ. Ссылочная коллекция в памяти ведёт себя как таблица значений.
Перенос элементов между двумя списками и в дерево
Второй по частоте сценарий — две таблицы рядом: слева «Доступные», справа «Выбранные», элементы перетаскиваются из одной в другую. Здесь важно передать между коллекциями не идентификатор (он действителен только внутри своей таблицы), а сами значения полей.
В источнике соберите данные строки в структуру:
&НаКлиенте
Процедура ДоступныеНачалоПеретаскивания(Элемент, ПараметрыПеретаскивания, СтандартнаяОбработка)
Текущая = Элементы.Доступные.ТекущиеДанные;
Если Текущая = Неопределено Тогда
СтандартнаяОбработка = Ложь;
Возврат;
КонецЕсли;
ПараметрыПеретаскивания.Значение = Новый Структура("Код, Наименование", Текущая.Код, Текущая.Наименование);
КонецПроцедуры
В приёмнике добавьте строку и заполните её из структуры — с проверкой типа, чтобы случайный перенос файла или элемента из другого списка не уронил обработчик:
&НаКлиенте
Процедура ВыбранныеПеретаскивание(Элемент, ПараметрыПеретаскивания, СтандартнаяОбработка, Строка, Поле)
СтандартнаяОбработка = Ложь;
Данные = ПараметрыПеретаскивания.Значение;
Если ТипЗнч(Данные) <> Тип("Структура") Тогда
Возврат;
КонецЕсли;
НоваяСтрока = Выбранные.Добавить();
ЗаполнитьЗначенияСвойств(НоваяСтрока, Данные);
КонецПроцедуры
Если перенос односторонний (элемент должен исчезнуть из «Доступных»), удалите исходную строку в ОкончаниеПеретаскивания источника — но только когда Действие равно Перемещение, а не Отмена.
Дерево и иерархия. Когда нужно перетаскиванием подчинять элементы группам (иерархический справочник, дерево значений), цель — строка-группа. У иерархического динамического списка штатная обработка часто уже меняет родителя сама: оставьте СтандартнаяОбработка = Истина и проверьте поведение. Если нужна своя логика (запрет вложенности, пересчёт итогов) — перехватывайте бросок и в Строка получите узел-приёмник, которому назначаете нового родителя.
HTML-поле для канбана и свободного перетаскивания
Когда штатных таблиц мало — нужен канбан с колонками, перетаскивание плиток по сетке, своя анимация — берут поле вида «Поле HTML документа». Внутри него работает обычный веб: верстаете карточки, вешаете на них стандартный HTML5 drag & drop.
Минимальный каркас разметки внутри поля:
<div class="card" draggable="true" ondragstart="onStart(event)">Заявка №1</div>
<div class="col" ondragover="event.preventDefault()" ondrop="onDrop(event)">В работе</div>
Логика переноса (dragstart, dragover, drop) целиком живёт в JS внутри HTML-поля — платформу она не касается, пока вы перекладываете карточки визуально. Сложность только в обратной связи: когда карточка переехала в другую колонку, это изменение нужно вернуть в 1С, чтобы записать новый статус.
Способ обмена зависит от версии платформы и вида поля. Рабочий общий приём — отдавать результат из HTML в 1С через переход по служебной ссылке с закодированными данными, а на стороне формы ловить это событие поля и разбирать параметры. Конкретную привязку сверяйте со справкой по вашему релизу: детали обмена HTML-поля с формой между версиями менялись.
HTML-поле — мощный, но хрупкий инструмент: верстка и скрипты не проверяются конфигуратором, а поведение в тонком и веб-клиенте различается. Берите его, только когда штатных таблиц действительно не хватает.
Внешние компоненты и перетаскивание файлов из проводника
Если задача выходит за рамки форм — нужен нестандартный виджет, интеграция с рабочим столом ОС, особая обработка жестов — пишут внешнюю компоненту на технологии Native API. Она рисует свой интерфейс и обрабатывает перетаскивание собственным кодом, а в 1С отдаёт готовый результат. Это самый трудоёмкий путь: оправдан, когда первые два способа не закрывают сценарий.
Отдельный случай — перетаскивание файлов из проводника Windows прямо в форму. Платформа это поддерживает: при броске файла срабатывает тот же Перетаскивание, а в ПараметрыПеретаскивания.Значение приходят данные о файле (или массив, если тянули несколько). Тип значения здесь не такой, как при переносе строк, поэтому обязательно проверяйте его через ТипЗнч() перед обработкой и ветвите код: строки — одно, файлы — другое.
В веб-клиенте перетаскивание файлов из ОС ограничено тем, что разрешает браузер и настройки безопасности, — проверяйте сценарий в той среде, где работают пользователи, а не только в тонком клиенте.
Перетаскивание в больших списках: производительность
Само перетаскивание нагрузки не создаёт — проблемы начинаются, когда на каждое движение мыши дёргают сервер или базу. Несколько правил, чтобы форма с тысячами строк не «залипала»:
- ⚡ Держите обработчики перетаскивания клиентскими. Все расчёты позиции делайте в памяти, без обращения к базе.
- 💾 Пишите результат один раз — в момент броска, а не на каждом событии. Если переставляете много строк, сохраняйте новый порядок пакетом через один серверный вызов.
- 🚫 Не пытайтесь переупорядочивать мышью динамический список: он читается из базы порциями, и «перетащенный» порядок всё равно собьётся при обновлении. Для ручного порядка используйте таблицу значений на форме или реквизит-порядок у объекта.
- 📉 Не перечитывайте всю таблицу после броска. Меняйте в коллекции только затронутые строки — сдвиг и перенос этого не требуют.
Если пользователю нужно расставить приоритеты среди сотен позиций, не грузите их все в одну форму: дайте фильтр или постраничный вывод, а перетаскивание оставьте в пределах видимой порции.
Что собирают на практике
Перетаскивание редко делают ради самого эффекта — за ним почти всегда стоит понятная задача. Типовые сценарии и способ под каждый:
- 📋 Ручной приоритет списка. Менеджер перетаскиванием расставляет задачи, заявки, этапы по важности. Таблица значений на форме, сдвиг строки, запись реквизита-порядка при закрытии. Способ 1.
- ↔️ Выбор из доступного. Две таблицы: слева весь справочник номенклатуры или сотрудников, справа отобранные позиции. Элементы перетягивают слева направо. Способ 1, перенос через структуру.
- 🗂️ Раскладка по группам. Перетаскиванием подчиняют элементы папкам в иерархическом справочнике или узлам дерева. Способ 1 с деревом.
- 🧩 Канбан по статусам. Карточки заявок или задач переезжают между колонками «Новая → В работе → Закрыта», смена колонки меняет статус в базе. HTML-поле, способ 2.
- 📎 Прикрепление файлов . Документы тянут из проводника прямо в форму объекта и сохраняют как присоединённые файлы. Способ 3, обработка файлового переноса.
Для первых трёх задач внешние компоненты и HTML не нужны — хватает штатных событий управляемой формы. Усложняйте инструмент только когда сценарий этого требует, а не заранее.
Почему перетаскивание не работает: разбор по симптомам
Чаще всего «не тащится» по одной из пяти причин. Найдите свой симптом:
Ещё одна тонкая ловушка — обращение к серверным данным прямо в клиентском обработчике перетаскивания. Если в момент броска нужно что-то из базы (проверить права, подтянуть реквизиты), вынесите это в процедуру &НаСервереБезКонтекста и вызовите её из обработчика, а не пытайтесь читать базу с клиента.
Частые вопросы
Работает ли перетаскивание в веб-клиенте и на мобильном?
Можно ли перетаскивать строки между двумя разными открытыми формами?
Как добавить перетаскивание в типовую конфигурацию, не снимая её с поддержки?
Можно ли перетащить сразу несколько выделенных строк?
НачалоПеретаскивания соберите в Значение не одну строку, а массив данных по всем выделенным строкам и в приёмнике обойдите его циклом.