Книга знаний

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

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

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

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


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

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


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

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

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

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

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

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

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


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

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


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

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

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