Книга знаний

1С:Предприятие / v8 / Приемы программирования / Формы

v8: Глобальное перетаскивание <редактируется>

Механизм перетаскивания в типовых конфигурациях используется далеко не на максимум. А ведь он значительно ускоряет работу пользователя.Автор статьи: TormozIT | Редакторы:
Последняя редакция №12 от 12.08.06 | История
URL: http://kb.mista.ru/article.php?id=326

Ключевые слова: Перетаскивание, глобальное, Drag, Drop, ПараметрыПеретаскивания, ТабличноеПоле, ПолеТабличногоДокумента, ПроверкаПеретаскивания, НачалоПеретаскивания, ОкончаниеПеретаскивания


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

Для начала немного общих моментов о самом механизме.


Самое понятие "перетаскивание" подразумевает интерактивную передачу значения от источника приемнику.
Событий перетаскивания всего 4:
- НачалоПеретаскивания
- ОкончаниеПеретаскивания
- ПроверкаПеретаскивания
- Перетаскивание
Для первой пары событий необходимо установить флажок в настройках элемента управления "Разрешить начало перетаскивания". Для пары - "Разрешить перетаскивание".
Перетаскивание реализовано для двух видов элементов управления: ТабличноеПоле и ПолеТабличногоДокумента. Для поля табличного документа есть некоторые ограничения. Во-первых, в режиме ТолькоПросмотр обрабатываются только первая пара событий, т.е. в этом случае оно не может выступать в качестве приемника. Во-вторых, начало перетаскивания может быть вызвано только для одной прямоугольной области.

У каждого типа обработчика события перетаскивания есть параметр ПараметрыПеретаскивания.
Этот параметр представляет собой структуру из 3-х элементов:
- Действие
- ДопустимыеДействия
- Значение

Начнем с последнего параметра - Значение. Он при стандартном начале перетаскивания в разных случаях имеет различный тип. Если источником является элемент управления типа ТабличноеПоле, то  значение перетаскивания будет иметь тип строки значения табличного поля в одиночном режиме выделения и тип Массив в случае множественного режима выделения, где элементами массива будут строки значения табличного поля. В случае поля табличного документа тип значения будет ТабличныйДокумент и будет содержать одну выделенную прямоугольную область.

Параметр Действие отвечает за внешний вид курсора и используется для управления типом перетаскивания. Он может принимать 4 значения:
- Выбор
- Копирование
- Отмена
- Перемещение
При перетаскивании левой кнопкой мыши по умолчанию Действие содержит Перемещение. Если пользователя дополнительно удерживает нажатой клавишу <CTRL>, то Действие содержит Копирование. Если претаскивание производится правой кнопкой мыши, то по умолчанию Действие содержит Выбор независимо от состояния клавиши <CTRL>.

Параметр ДопустимыеДействия предназначен для передачи приемнику допустимых типов перетаскивания и может принимать также 4, но уже других значения:
- Копирование
- КопированиеИПеремещение
- НеОбрабатывать
- Перемещение

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

Основные идеи глобального перетаскивания.


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

Пример реализации


Я реалиазовал свою идею в конфигурации "Управление торговлей 10.2". Так что в коде пару раз встретятся незначительные привязки к особенностям этой конфигурации. В целом же реализация очень универсальна.
[1C]
////////////////////////////////////////////////////////////////////////////////
// ПРОЦЕДУРЫ И ФУНКЦИИ ОБЩЕГО НАЗНАЧЕНИЯ

// Функция разбивает строку разделителем.
//
// Параметры:
//  пСтрока      - Строка - которую разбиваем;
//  *пРазделитель - Строка - символ-разделитель.
//
// Возвращаемое значение:
//               - Массив - содержащий фрагменты, на которые разбивает строку разделитель.
//
Функция ЛксПолучитьМассивИзСтрокиСРазделителем(пСтрока, пРазделитель = ".") Экспорт
   
   Массив = Новый Массив;
   лСтрока = СтрЗаменить(пСтрока, пРазделитель, Символы.ПС);
   Для Счетчик = 1 По СтрЧислоСтрок(лСтрока) Цикл
       Массив.Добавить(СтрПолучитьСтроку(лСтрока, Счетчик));
   КонецЦикла;
   Возврат Массив;
   
КонецФункции // ЛксПолучитьМассивИзСтрокиСРазделителем()

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

// Определяет тип ссылки.
//
// Параметры:
//  пЗначение  – Произвольный – которое проверяем.
//
// Возвращаемое значение:
//  Строка       – имя типа объектов метаданных;
//  Неопределено – значение не является ссылкой.
//
Функция ЛксПолучитьТипСсылки(пЗначение) Экспорт

   ОбъектМетаданных = Метаданные.НайтиПоТипу(ТипЗнч(пЗначение));
   Если ОбъектМетаданных <> Неопределено Тогда
       МассивФрагментов = ЛксПолучитьМассивИзСтрокиСРазделителем(ОбъектМетаданных.ПолноеИмя());
       Если МассивФрагментов.Количество() = 2 Тогда
           Возврат МассивФрагментов[0];
       Иначе
           // Ссылка на строку табличной части
       КонецЕсли;
   КонецЕсли;
   Возврат Неопределено;
   
КонецФункции // ЛксПолучитьТипСсылки()

// Определяет тип строки табличной части.
//
// Параметры:
//  пЗначение  – Произвольный – которое проверяем.
//
// Возвращаемое значение:
//  Строка       – имя типа объектов метаданных;
//  Неопределено – значение не является строкой табличной части.
//
Функция ЛксПолучитьТипСтрокиТабличнойЧасти(пЗначение) Экспорт

   МассивФрагментов = ЛксПолучитьМассивИзСтрокиСРазделителем(Строка(пЗначение));
   СтрокаТипЗначения = МассивФрагментов[0];
   МаркерСписка      = "ТабличнаяЧастьСтрока";
   ДлинаСтрокиТипа = СтрДлина(СтрокаТипЗначения);
   ДлинаМаркера    = СтрДлина(МаркерСписка);
   Если ДлинаСтрокиТипа > ДлинаМаркера И Прав(СтрокаТипЗначения, ДлинаМаркера) = МаркерСписка Тогда
       Возврат Лев(СтрокаТипЗначения, ДлинаСтрокиТипа - ДлинаМаркера);
   Иначе
       Возврат Неопределено;
   КонецЕсли;

КонецФункции // ЛксПолучитьТипСсылки()

// Определяет тип списка табличного поля.
//
// Параметры:
//  пТабличноеПоле  – ТабличноеПоле – которое проверяем.
//
// Возвращаемое значение:
//  Строка       – имя типа объектов метаданных;
//  Неопределено – табличное поле не является списком.
//
Функция ЛксПолучитьТипОбъектовСпискаТабличногоПоля(пТабличноеПоле)

   Попытка
       ЗначениеТабличногоПоля = пТабличноеПоле.Значение;
   Исключение
       // Сюда попадаем из ввода на основании. Баг платформы.
       Если Строка(пТабличноеПоле.ТекущиеДанные) = "ТекущиеДанныеСписка" Тогда
           Возврат ЛксПолучитьТипСсылки(пТабличноеПоле.ТекущаяСтрока);
       КонецЕсли;
   КонецПопытки;
   МассивФрагментов = ЛксПолучитьМассивИзСтрокиСРазделителем(Строка(ЗначениеТабличногоПоля));
   СтрокаТипЗначения = МассивФрагментов[0];
   МаркерСписка      = "Список";
   ДлинаСтрокиТипа = СтрДлина(СтрокаТипЗначения);
   ДлинаМаркера    = СтрДлина(МаркерСписка);
   Если ДлинаСтрокиТипа > ДлинаМаркера И Прав(СтрокаТипЗначения, ДлинаМаркера) = МаркерСписка Тогда
       Возврат Лев(СтрокаТипЗначения, ДлинаСтрокиТипа - ДлинаМаркера);
   Иначе
       Возврат Неопределено;
   КонецЕсли;

КонецФункции // ЛксПолучитьТипОбъектовСпискаТабличногоПоля()

// Проверяет, является ли ссылка указателем на объект БД.
//
// Параметры:
//  пСсылка      – Ссылка – которую проверяем.
//
// Возвращаемое значение:
//  Истина       – ссылка является указателем на объект БД;
//  Ложь         – ссылка не является указателем на объект БД.
//
Функция ЛксЛиСсылкаНаОбъектБД(пСсылка)

   ТипОбъекта = ЛксПолучитьТипСсылки(пСсылка);
   Если ТипОбъекта <> Неопределено Тогда
       Если Ложь
         ИЛИ ТипОбъекта = "ПланОбмена"
         ИЛИ ТипОбъекта = "Справочник"
       ИЛИ ТипОбъекта = "Документ"
         ИЛИ ТипОбъекта = "ПланВидовХарактеристик"
         ИЛИ ТипОбъекта = "БизнесПроцесс"
         ИЛИ ТипОбъекта = "Задача"
       Тогда
           Возврат Истина;
       КонецЕсли;
   КонецЕсли;
   Возврат Ложь;

КонецФункции // ЛксЛиСсылкаНаОбъектБД()

// Проверяет, является ли табличное поле списком ссылок.
//
// Параметры:
//  пТабличноеПоле  – ТабличноеПоле – которое проверяем.
//
// Возвращаемое значение:
//  Истина       – табличное поле является списком ссылок;
//  Ложь         – табличное поле не является списком ссылок.
//
Функция ЛксЛиСписокСсылок(пТабличноеПоле)

   ТипОбъектов = ЛксПолучитьТипОбъектовСпискаТабличногоПоля(пТабличноеПоле);
   Если ТипОбъектов <> Неопределено Тогда
       Если ТипОбъектов = "ЖурналДокументов"
        ИЛИ ТипОбъектов = "ПланОбмена"
        ИЛИ ТипОбъектов = "Справочник"
        ИЛИ ТипОбъектов = "Документ"
        ИЛИ ТипОбъектов = "ПланВидовХарактеристик"
        ИЛИ ТипОбъектов = "Задача" Тогда
           Возврат Истина;
       КонецЕсли;
   КонецЕсли;
   Возврат Ложь;

КонецФункции // ЛксЛиСписокСсылок()

////////////////////////////////////////////////////////////////////////////////
// ФУНКЦИОНАЛ ПЕРЕТАСКИВАНИЯ И БУФЕРА ОБМЕНА

// Получаем массив основных ключей (имен колонок) для перетаскивания.
// Т.е. одна из этих колонок будет ключевой при перетаскивании.
// Расположены в порядке убывания приоритета.
//
// Возвращаемое значение:
//  Массив       – Массив – имен колонок.
//
Функция ЛксПолучитьМассивОсновныхКлючейПеретаскивания()

   Массив = Новый Массив;
   Массив.Добавить("Ссылка");
   Массив.Добавить("Объект");
   Массив.Добавить("Характеристика");
   Массив.Добавить("ХарактеристикаНоменклатуры");
   Массив.Добавить("Договор");
   Массив.Добавить("Номенклатура");
   Массив.Добавить("Контрагент");
   Массив.Добавить("Контакт");
   Массив.Добавить("Пользователь");
   Возврат Массив;

КонецФункции // ЛксПолучитьМассивОсновныхКлючейПеретаскивания()

// Получает ссылку - значение ключа значения перетаскивания.
//
// Параметры:
//  пЗначение    – Произвольный – значение перетаскивания;
//                 <продолжение описания параметра>.
//
// Возвращаемое значение:
//               - Ссылка - найденное значение ключа;
//  Неопределено - получить ключ не удалось.
//
Функция ЛксПолучитьЗначениеКлючаПеретаскивания(пЗначение)

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

// Ищет ссылку в табличном поле списка ссылочного объекта.
//
// Параметры:
//  пСписок      – ТабличноеПоле – списка ссылочного объекта;
//  пЗначение    – Произвольный – значение перетаскивания.
//
// Возвращаемое значение:
//  Истина       – использовать удалось;
//  Ложь         – использовать не удалось.
//
Функция ЛксНайтиСсылкуВТабличномПолеСписка(пСписок, пЗначение)

   ЗначениеКлючаИсточника = ЛксПолучитьЗначениеКлючаПеретаскивания(пЗначение);
   Если ЗначениеКлючаИсточника = Неопределено Тогда
       Возврат Ложь;
   КонецЕсли;
   Попытка
       пСписок.ТекущаяСтрока = ЗначениеКлючаИсточника;
       Возврат Истина;
   Исключение
   КонецПопытки;
   Если ЛксПолучитьТипСсылки(ЗначениеКлючаИсточника) = "Справочник" Тогда
       ЗначениеРеквизита = ЗначениеКлючаИсточника.Владелец;
       Попытка
           пСписок.ТекущаяСтрока = ЗначениеРеквизита;
           Возврат Истина;
       Исключение
       КонецПопытки;
   КонецЕсли;
   Для Каждого Реквизит Из ЗначениеКлючаИсточника.Метаданные().Реквизиты Цикл
       ЗначениеРеквизита = ЗначениеКлючаИсточника[Реквизит.Имя];
       Попытка
           пСписок.ТекущаяСтрока = ЗначениеРеквизита;
           Возврат Истина;
       Исключение
       КонецПопытки;
   КонецЦикла;
   Возврат Ложь;

КонецФункции // ЛксНайтиСсылкуВТабличномПолеСписка()

// Ищет ссылку в табличном поле.
//
// Параметры:
//  пТабличноеПоле – ТабличноеПоле – где ищем;
//  пЗначение    – Произвольный – значение перетаскивания;
//  пМассивСсылочныхКолонок - Массив - имен колонок ссылочного типа табличного поля,
//                 которые претендуют на роль ключа в порядке убывания приоритета;
//
// Возвращаемое значение:
//  Истина       – использовать удалось;
//  Ложь         – использовать не удалось.
//
Функция ЛксНайтиСсылкуВТабличномПоле(пТабличноеПоле, пЗначение, пМассивСсылочныхКолонок)

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

КонецФункции // ЛксНайтиСсылкуВТабличномПоле()

// Ищет ссылку в табличном документе. Все области с расшифровкой
// равной ссылке будут добавлены в коллекцию выделенных областей табличного документа.
// Пока не используется. Не протестировано.
//
// Параметры:
//  пПолеТабличногоДокумента – ПолеТабличногоДокумента – где будем искать;
//  пЗначение    – Произвольный – значение перетаскивания.
//
// Возвращаемое значение:
//  Истина       – использовать удалось;
//  Ложь         – использовать не удалось.
//
Функция ЛксНайтиСсылкуВПолеТабличногоДокумента(пПолеТабличногоДокумента, пЗначение)

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

КонецФункции // ЛксНайтиСсылкуВПолеТабличногоДокумента()

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

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

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

   Если ТипЗнч(пЭлемент) = Тип("ПолеТабличногоДокумента") Тогда
       пПараметрыПеретаскивания.Действие           = ДействиеПеретаскивания.Перемещение;
       Возврат Истина;
   КонецЕсли;
   Значение = пПараметрыПеретаскивания.Значение;
   МассивФрагментовПриемника = ЛксПолучитьМассивИзСтрокиСРазделителем(Строка(пЭлемент.Значение));
   
   Если Истина
     И МассивФрагментовПриемника[0] = "ДокументТабличнаяЧастьСтрока"
     И ТипЗнч(Значение) = Тип("Структура")
     И Значение.Свойство("ИзПодбора")
   Тогда    
       // Не обрабатываем стандартное перетаскивание из подбора в ТЧ документа.
       Возврат Ложь;        
   КонецЕсли;
   
   пСтандартнаяОбработка = Ложь;
   Если Истина Тогда
       // Так быстрее будет работать. А от полной обработки будет ощутимая вычислительная нагрузка.
       пПараметрыПеретаскивания.Действие           = ДействиеПеретаскивания.Перемещение;
   Иначе
       пПараметрыПеретаскивания.Действие           = ДействиеПеретаскивания.Отмена;
   КонецЕсли;
   Возврат Истина;
   
КонецФункции // ЛксПроверкаПеретаскивания()

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

Специальная реализация перетаскивания на типовую форму подбора 1С-ных конфигураций.



Описание | Рубрикатор | Поиск | ТелепатБот | Захваченные статьи | Установки | Форум
© Станислав Митичкин (Волшебник), 2005-2025 | Mista.ru

Яндекс.Метрика