Наведи порядок с помощью дерева

04.06.17

Разработка - Универсальные функции

Быстрая правка данных из динамического списка - их перетаскивание между группировками в режиме иерархического просмотра

Есть динамический список с группировками, и очень хочется навести порядок с помощью обычного драг-эн-дропа, перетаскивая нужные объекты или целые группировки туда-сюда. Идея, думаю, ясна, полезность её мне тоже видится немалой. Теории на тему поведения "демонических списков" в случае перетаскивания уже изрядно (например, //infostart.ru/public/419306/), поэтому предлагаю работающее решение, точнее, оснастку для него.

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

1. Позволяет перетаскивать один или несколько выделенных объектов.
2. При перетаскивании показывает, куда оно "упадёт".
3. Позволяет "бросить" и над элементом (тогда перенесётся в его непосредственного "родителя" по иерархии), и над группировкой любого уровня.
4. При перетаскивании "элемент взяли - над элементом бросили" не дёргает сервер низачем, кроме самого изменения данных объекта.


#Область ПравкаДинамическогоСпискаПеретаскиванием

#Область Описание

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

#КонецОбласти

#Область СлужебныеМеханизмы

&НаСервере
Функция ПолучитьДанныеБлокаНаСервере(рИмяЭлемента,соотЗначенияПолей)
Попытка
	рСКД=Элементы[рИмяЭлемента].ПолучитьИсполняемуюСхемуКомпоновкиДанных();
	рНастройки=Элементы[рИмяЭлемента].ПолучитьИсполняемыеНастройкиКомпоновкиДанных();
	//
	// нам нужны только Ссылка, но проще отфильтровать их по заполненности, чем перенастраивать Структуру
	//
	Для каждого киз Из соотЗначенияПолей Цикл
		// по-хорошему ещё бы проверку, а нет ли других отборов на это поле; черновик!
		отб=рНастройки.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
		отб.ЛевоеЗначение=Новый ПолеКомпоновкиДанных(киз.Ключ); // уже содержит путь к данным через точку
		отб.ВидСравнения=ВидСравненияКомпоновкиДанных.Равно;
		отб.ПравоеЗначение=киз.Значение;
		отб.Использование=Истина;
	КонецЦикла;
	//		
	КомпоновщикМакета=Новый КомпоновщикМакетаКомпоновкиДанных;
	МакетКомпоновки=КомпоновщикМакета.Выполнить(рСКД,рНастройки,,,Тип("ГенераторМакетаКомпоновкиДанныхДляКоллекцииЗначений"));
	ПроцессорКД=Новый ПроцессорКомпоновкиДанных;
	ПроцессорКД.Инициализировать(МакетКомпоновки,,,Истина);
	ПроцессорВывода=Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений;
	трез=Новый ТаблицаЗначений;
	ПроцессорВывода.УстановитьОбъект(трез);
	трез=ПроцессорВывода.Вывести(ПроцессорКД,Истина);
	//
	Возврат трез.ВыгрузитьКолонку("Ссылка");
Исключение
	Сообщить("ПолучитьДанныеБлокаНаСервере, ошибка: "+ОписаниеОшибки());
	Возврат Неопределено;
КонецПопытки;
КонецФункции

&НаСервереБезКонтекста
Функция ИзменитьЗначенияГруппировочныхПолейДляОбъектаНаСервере(рСсылка,соотЗначенияПолей)
	рОбъект=рСсылка.ПолучитьОбъект();
	// использовать ЗаполнитьЗначенияСвойств(рОбъект,соотЗначенияПолей) не рекомендуется
	
	//<ВНИМАНИЕ! ЗДЕСЬ ВСТАВИТЬ КОНКРЕТИКУ ИЗМЕНЕНИЯ ПОЛЕЙ СОГЛАСНО ЛОГИКЕ ПРИКЛАДНОГО РЕШЕНИЯ!>
	//
	// В простейшем случае:
	инфо="";
	Для каждого киз Из соотЗначенияПолей Цикл
		инфо=инфо+Символы.ПС+"Изменяется "+киз.Ключ+" = "+Строка(киз.Значение);		
		//рОбъект[киз.Ключ]=киз.Значение; // например, так
	КонецЦикла;
	Сообщить(инфо);
	
	рОбъект.ОбменДанными.Загрузка=Истина;
	Попытка
		рОбъект.Записать();
		Возврат Истина;
	Исключение
		Сообщить("Ошибка: "+ОписаниеОшибки());
		Возврат Ложь;
	КонецПопытки;	
КонецФункции

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

#КонецОбласти

#Область ОбработчикиСобытийПеретаскивания

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

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

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

#КонецОбласти

#КонецОбласти

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

Некоторая специфика в том, что источник и получатель события - один и тот же элемент формы. Тестировалось на 8.3.6 в толстом и тонком клиенте.
Если это кому пригодится, будет неплохо)

Вступайте в нашу телеграмм-группу Инфостарт

Динамический список перетаскивание drag'n'drop

См. также

Загрузка и выгрузка в Excel Универсальные функции Программист 1С:Предприятие 8 Россия Бесплатно (free)

Описанный ниже подход позволяет в три шага заполнять формулы в Excel файлы, вне зависимости от ОС сервера (MS Windows Server или Linux). Подход подразумевает отказ от работы с COM-объектом в пользу работы через "объектную модель документа" (DOM).

30.10.2025    3614    Abysswalker    9    

45

Универсальные функции Работа с интерфейсом Программист 1С:Предприятие 8 Бесплатно (free)

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

14.05.2025    6711    DeerCven    15    

59

Универсальные функции Программист 1С:Предприятие 8 1C:Бухгалтерия Бесплатно (free)

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

21.05.2024    49679    dimanich70    83    

170

Универсальные функции Программист 1С:Предприятие 8 1C:Бухгалтерия Абонемент ($m)

Задача: вставить картинку из буфера обмена на форму средствами платформы 1С.

1 стартмани

18.03.2024    7388    6    John_d    13    

59

Универсальные функции Программист Стажер 1С:Предприятие 8 1C:Бухгалтерия Бесплатно (free)

Пришлось помучиться с GUID-ами немного, решил поделиться опытом, мало ли кому пригодится.

12.02.2024    62386    atdonya    31    

70

Универсальные функции Программист 1С:Предприятие 8 Бесплатно (free)

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

30.11.2023    9221    ke.92@mail.ru    17    

68
Комментарии
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
1. VmvLer 05.06.17 13:14 Сейчас в теме
есть статистика об эффективности метода для справочника Номенклатура с количеством элементов от 100к?
2. VmvLer 05.06.17 13:16 Сейчас в теме
+(1) или документов с аналогичным количеством записей в БД
3. Yashazz 4891 05.06.17 17:06 Сейчас в теме
(1) Для больших объёмов надо допиливать в первую очередь место, где у меня помечено "черновик". Там тогда нужно сделать запрос вместо вызова СКД. Ну и, естественно, обработка должна вызываться не в цикле каждый раз (как это в примере, для каждого объекта), а единожды, с передачей массива ссылок на сервер.
Статистики, к сожалению, нету. Делал как приблуду по просьбе жены, на днях)
8. nickperel 5 07.06.17 17:13 Сейчас в теме
(3)
Очень хорошая идея, спасибо. Попробую у себя.
7. nickperel 5 07.06.17 17:10 Сейчас в теме
(1)

(1)
есть статистика об эффективности метода для справочника Номенклатура с количеством элементов от 100к?


Все должно красиво работать. Дин.список тонким клиентом запрашивается в области видимости на форме.
Если сильно вверх\низ, можно группе выделения сменить групп. реквизит.
4. kolya_tlt 92 07.06.17 09:47 Сейчас в теме
рСКД, соотДанных, киз.


как это развидеть?
dj_serega; ixijixi; TreeDogNight; pavlov_dv; bonv; WizaXxX; +6 Ответить
5. Yashazz 4891 07.06.17 13:36 Сейчас в теме
(4) Если вам шашечки, а не ехать, то извольте тут не флудить. А желающие гундеть пусть задумаются, насколько сама БСП соответствует стандартам кода 1С.
6. kolya_tlt 92 07.06.17 15:34 Сейчас в теме
(5) стандарты не для того, чтобы им слепо следовать. это том рекомендаций как, например, толковый словарь. не все же люди говорят и пишут правильно и под одним словом понимают один смысл, но стремится к этому нужно. объяснять зачем был придумал словарь нужно?
9. nickperel 5 07.06.17 17:24 Сейчас в теме
(5)
Если вам шашечки, а не ехать, то извольте тут не флудить. А желающие гундеть пусть задумаются, насколько сама БСП соответствует стандартам кода 1С.


:-) Без "гундеть", тут не могут..

БСП, наверно, ничему, кроме соображений бизнес интересов собственно самой 1С не соответствует.. Та же структура с другого подьезда выдает сертификаты программистам.
Весело.
Каждый раз думаешь из БСП чего-нибудь "дернуть" по быстрому, а вылезает длиииииииинная борода... с граблями...
10. nickperel 5 07.06.17 17:27 Сейчас в теме
(6)
стандарты не для того, чтобы им слепо следовать


Хотелось бы видеть стандарты декларируемые непосредственно в коде конфигураций с которыми имеешь дело. Оно было и есть как-то не совсем и не всегда так..
11. kolya_tlt 92 08.06.17 15:57 Сейчас в теме
(10) почему-то я сейчас вспомнил мемы Кличко :))
dj_serega; +1 Ответить
12. пользователь 09.06.17 08:21
Сообщение было скрыто модератором.
...
13. artemusII 99 12.06.17 12:02 Сейчас в теме
Наверняка в аду есть отдельный котел для тех, кто после знаков препинания и арифметических знаков не ставит пробел...
14. Yashazz 4891 14.06.17 21:00 Сейчас в теме
(13) А ещё для тех, кто реквизиты в объектах и объекты в дереве метаданных не сортирует по алфавиту))) и кто пояснения пишет к процедуре "НекоеВыполнениеДействия" в духе "// эта процедура выполняет некое действие" и точка))) и особенно для тех, кто такие "эргономичные" интерфейсы клепает, что на малый монитор не лезут и крутить надо))) Продолжить?

А вообще интересно, по делу кто-нибудь выскажется, или только гундеть умеем?)
15. Yashazz 4891 15.06.17 15:09 Сейчас в теме
Вообще что характерно: возбухают по поводу стиля кода обычно те, кто сами на ИС мало или ничего не выкладывает. Кто пользуется плодами чужих трудов. Таких вообще год от года всё больше - халявщиков, жирующих на чужом труде. И именно они гневно возмущаются, мол как же так, их величествам глазки ломать неохота - мало что код на дармовщинку, так он ещё и должен быть белым и пушистым. Такое прямо капризное детское возмущение, типа погремушку не того сорта дали.

Собеседую я тут одного "товарисча" на предмет себе в помощники. Уверенно хочет шестизначный оклад. Ставлю задачу, мол, как будете решать? Он выдаёт "скачаю обработку". Причём не то чтоб сильно шаблонная задача-то, не тот случай, когда есть типовые удачные решения. Говорю, а где, а если не найдёте? Он, уверенно: "ну, на ИС есть наверняка, или попрошу кого-нибудь допилить". Я аж дар речи потерял от подобной наглости.

Так и здесь. Мало что код выложен, ещё изволь их величествам имена переменных, чтоб, значитца, ндравилось) Зажрались ребятки. Почитали б на досуге исходники сишного или паскалевского кода - резко бы просветлились, особливо насчёт имён переменных))
16. Yashazz 4891 23.08.17 19:51 Сейчас в теме
Копался в коде печати кассовой книги УТ 11.2. Если очень вежливо, то - такой код наверняка не получил бы 1С-Совместимо. Желающие да посмотрят сами)
Для отправки сообщения требуется регистрация/авторизация