Книга знаний

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

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

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

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


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

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


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

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

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

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

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

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

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


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

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


Я реалиазовал свою идею в конфигурации "Управление торговлей 10.2". Так что в коде пару раз встретятся незначительные привязки к особенностям этой конфигурации. В целом же реализация довольно универсальна.

Особенности моей реализации.

1) Естественно я использую свою передовую методику Книга знаний: v8: Методика переопределения и вызова обработчиков событий формы; =). Так что имеются обращения к функциям и процедурам из этой статьи.

2) При открытии каждой формы, элементы которой должны выступать в роли источника перетаскивания, нужно вызывать процедуру ЛксПриОткрытииФормы. Но это относится только к случаям, когда флажок "Разрешить начало перетаскивания" не установлен хотя бы у одного из нужных элементов управления.

3) В модуль каждой формы, элементы которой должны выступать в роли приемника перетаскивания, следует поместить унифицированные обработчики событий
// Процедура - обработчик события "Перетаскивание" всех элементов формы типа ТабличноеПоле
//
Процедура ЛксТабличноеПолеПеретаскивание(Элемент, ПараметрыПеретаскивания, СтандартнаяОбработка, Строка, Колонка)

    ЛксПеретаскивание(Элемент, ПараметрыПеретаскивания, СтандартнаяОбработка, Строка, Колонка);
    [Выполнить(ЛксПолучитьСтароеДействиеФормы(ЭтаФорма, "Перетаскивание", Элемент.Имя));]    

КонецПроцедуры // ЛксТабличноеПолеПеретаскивание()

// Процедура - обработчик события "ПроверкаПеретаскивания" всех элементов формы типа ТабличноеПоле
//
Процедура ЛксТабличноеПолеПроверкаПеретаскивания(Элемент, ПараметрыПеретаскивания, СтандартнаяОбработка, Строка, Колонка)

    ЛксПроверкаПеретаскивания(Элемент, ПараметрыПеретаскивания, СтандартнаяОбработка, Строка, Колонка);
    [Выполнить(ЛксПолучитьСтароеДействиеФормы(ЭтаФорма, "ПроверкаПеретаскивания", Элемент.Имя));]    

КонецПроцедуры // ЛксТабличноеПолеПроверкаПеретаскивания()

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

3)...


Текст общего модуля Перетаскивание.
////////////////////////////////////////////////////////////////////////////////
// ПРОЦЕДУРЫ И ФУНКЦИИ ОБЩЕГО НАЗНАЧЕНИЯ

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

////////////////////////////////////////////////////////////////////////////////
// ГЛОБАЛЬНАЯ НАСТРОЙКА ФОРМЫ ПРИ ЕЕ ОТКРЫТИИ

// Переопределяет обработчики перетаскивания и проверки перетаскивания табличных полей формы.
//
// Параметры:
//  пФорма       – Форма – форма.
//
Процедура ЛксУстановитьОбработчикиПеретаскиванияТабличныхПолейФормы(пФорма) Экспорт

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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


Продолжение следует...

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

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