Drag&Drop в версиях 3.4.x

В этом блоге хочу рассказать о развитии Drag&Drop в версии 3.4.x+
Никому не секрет, что DataGrid «умеет» принимать файлы с помощью Drag&Drop. Такая функциональность реализована на детали «Файлы». Но, для того, чтобы файлы извлечь с детали «Файлы», к примеру, прикрепить к письму, требуется совершать операции по сохранению его на жесткий диск, а уж потом перемещать куда нужно. Данный способ манипулирования файлами занимает много времени. Поэтому, в DataGrid был добавлен функционал полноценного Drag&Drop. Теперь есть возможность сохранять файлы только с помощью мыши. Также, весьма немаловажным бонусом, появилась возможность копировать и вставлять файлы с помощью комбинаций Ctrl+C/Ctrl+V. Именно с помощью этих операций не обойтись при переносе файлов внутри системы (файлы из одной записи в другую, из одного раздела – в другой). Плюс к выше сказанному появилась «автогенерация файлов по формату». Т.е., при нажатии Ctrl+V система анализирует содержимое буфера и генерирует (+ сохраняет) файл нужного формата.
Поддерживаемые форматы:

  1. Все изображения. Любое изображение будет сохранено в PNG формате (даже если Вы нажали Print Screen, то система сгенерирует Вам готовый файл)
  2. RTF. Любой форматированный текст буде сохранен в формате *.rtf
  3. HTML. Любой текст с HTML разметкой будет сохранен в *.html файл
  4. TEXT. Любой текст, который не является RTF – будет сохранен в *.txt файл

Дальше речь пойдет о реализации этих возможностей. Для начала пройдемся по имеющимся, а также новым свойствам:
Pic 1

  • CanCopySystemObjects – свойство, которое разрешает или запрещает копирование с помощью Ctrl+C
  • CanDragDropSystemObjects – знакомое свойство с предыдущих версий, которое разрешает или запрещает Drag&Drop в систему. Однако, в 3.4.x оно еще отвечает и за Drag&Drop из системы
  • CanPasteSystemObjects – свойство, которое разрешает или запрещает вставку с помощью Ctrl-V

Также, помимо свойств, появились новые события:
Pic 2

  1. OnGetSystemObjectNames – в обработчике события нужно заполнить имена файлов, которые будут копироваться (файлы будут сохранены под этими именами и расширениями). По количеству имен в списке будут генерироваться события OnGetSystemObjectData
  2. OnGetSystemObjectData - это событие придет столько раз, сколько было задано имен файлов в обработчике события OnGetSystemObjectNames. И при каждом вызове будет прислан Index файла, данные которого нужны. В обработчике необходимо вернуть поток данных согласно индексу файла
  3. OnPasteSystemObject – данное событие будет генерироваться при нажатии Ctrl+V. В него будет передан список файлов, которые нужно сохранить
  4. OnGetSystemObjectText – данное событие генерируется в том случае, когда «приемник» файла не может принять файл, но может принять текст. Поэтому в обработчике можно изменить текст, который будет передан

Маленьким, но приятным бонусом, появилась возможность отображать иконки, зарегистрированные в Windows согласно расширению файла:
Pic 3
Для того, нужно в обработчике OnGetRowDrawInfo, параметру ImageName нужно установить значение имени файла с расширением. Причем имя значения не имеет, так как поиск будет происходить только по расширению файла. Пример:

function OnGetRowDrawInfo(DataGrid, Color, TextColor, ImageName, Font) {
ImageName.Value = 'File.docx'; //Все элементы будут отображать иконку Word’а
ImageName.Value = 'File.xls' //Все будут отображать иконку Excel
}

Перед тем, как подвести итог, я предоставлю код реализации этих возможностей:

//Эта функция идентична для сохранения файлов как на
//событие OnDragDropSystemObjects, так и на OnPasteSystemObjects
function InsertSystemObjects(SystemObjectsList) {
        var FilesArray = SystemObjectsList.CommaText.split(',');
        var FileNames = System.CreateObject('TSObjectLibrary.StringsList');
        for (var i = 0; i FilesArray.length; i++) {
                FileNames.Clear();
                var Path = FilesArray[i];
                while (Path.indexOf('"', 0) != -1) {
                        Path = Path.replace('"', '')
                }
                FileNames.Add(Path);
                if (!CheckFileExists(Path)) {
                        AddObject(ft_FolderLink, Path, 1, null);
                } else {
                        AddObject(ft_File, FileNames, 1, null);
                }      
        }
}
 
//Реакция на Drag&Drop
function grdDataOnDragDropSystemObjects(DataGrid, DataGridColumn, SystemObjectsList, DestinationRowID, DropMode, WithCtrl) {
        InsertSystemObjects(SystemObjectsList);
}

//Реакция на Ctrl+V
function grdDataOnPasteSystemObjects(DataGrid, DataGridColumn, SystemObjectsList, SelectedIDs) {
        InsertSystemObjects(SystemObjectsList);
}

//Запрос имен объектов, который нужно копировать
//Если, в обработчике , вы запретите копировать какой-то
//файл (не передадите его имя), то в обработчике события
//OnGetSystemObjectData вам также нужно реализовывать
//дополнительные проверки
function grdDataOnGetSystemObjectNames(DataGrid, SourceDataGrid, SelectedIDs, SystemObjectNames) {
        var Dataset = GetSingleItemByCode('ds_Files', 'SystemObjectsFileDetail');
        var FileDataDataField = Dataset.DataFields.ItemsByName('FileData');
        EnableDatasetFilters(Dataset, false, 'ID');
        EnableDatasetField(Dataset, FileDataDataField, false);  
        Dataset.Open();
        for (var i = 0; i SelectedIDs.Count; i++) {
                if (!Dataset.Locate('ID', SelectedIDs(i))) {
                        continue;
                }
                SystemObjectNames.Add(Dataset('Link'));
        }
        Dataset.Close();
}

//Как было сказано выше - это событие генерируется для
//каждого файла, имя которого было указано в обработчике
//события OnGetSystemObjectNames. Порядок в списке
//файлов совпадает с порядком генерации события.
//Т.е., параметр SystemObjectIndex – это индекс в списке имен файлов
function grdDataOnGetSystemObjectData(DataGrid, SourceDataGrid, SelectedIDs, SystemObjectIndex, SystemObjectData) {
        if (!SelectedIDs) {
                return;
        }
       
        if (SystemObjectIndex >= SelectedIDs.Count) {
                return;
        }
        var RecordID = SelectedIDs(SystemObjectIndex);
        var Dataset = GetSingleItemByCode('ds_Files', 'SystemObjectsFileDetail');
        var FileDataDataField = Dataset.DataFields.ItemsByName('FileData');
        ApplyDatasetFilter(Dataset, 'ID', RecordID, true);
        EnableDatasetField(Dataset, FileDataDataField, true);
        Dataset.Open();
        //Из Blob поля мы просто сохраняем значение в поток SystemObjectData
        Dataset.DataFields.ItemsByName('FileData').GetValAsBlob(SystemObjectData);
        Dataset.Close();
}

Важно для разработчика:

  1. События OnGetSystemObjectNames, OnGetSystemObjectData и OnGetSystemObjectText начинают приходить только после того, как вы нажали Ctrl+V или отпустили кнопку мыши (при перетаскивании файлов) вне зависимости от того наше ли это приложение, или же стороннее
  2. События OnGetSystemObjectNames, OnGetSystemObjectData и OnGetSyestemObjectText в качестве параметра SelectedIDs принимают список IDs записей, которые действительно были скопированы (это важно). Этот список может не совпадать с DataGrid.SelectedIDs или SourceDataGrid.SelectedIDs. Связано это с тем, что вы можете переносить файлы, к примеру, с одного договора – в другой, и при этом DataGrid == SourceDataGrid (ведь это та же деталь), а при переходе на другую запись основного реестра применится фильтр для детали, и SourceDataGrid .SelectedIDs уже будет содержать совершенно другие значения
  3. В событие OnGetSystemObjectNames в качестве параметра SystemObjectNames приходит уже созданный IStringsList, Вам не нужно его создавать или же уничтожать – нужно только заполнить
  4. В событие OnGetSystemObjectData в качестве параметра SystemObjectData приходит уже созданный IStream. Вам достаточно просто записать в него значение. Создавать или уничтожать его также не нужно
  5. Событие OnGetSystemObjectText приходит только тогда, когда «приемник» не может принять файл(ы) (к примеру, при вставке в открытый документ Word). В нем можно указать текст, который необходимо вставить
  6. Вы можете пропустить копирование какого-либо файла. Для этого достаточно внести изменения в код обработчика OnGetSystemObjectData:
  7. //Чтобы отменить копирование файла не обязательно его
    //отсутствие в списке файлов, достаточно отменить его по индексу
    function grdDataOnGetSystemObjectData(DataGrid, SourceDataGrid, SelectedIDs, SystemObjectIndex,   SystemObjectData) {
            if (!SelectedIDs) {
                    return;
            }
           
            if (SystemObjectIndex >= SelectedIDs.Count) {
                    return;
            }
           //К примеру мне не хочется, чтобы пользователь
           //скопировал 2-й выбранный файл
           //Для этого достаточно выйти из функции, не
           //заполняя поток SystemObjectData
           If (SystemObjectIndex == 1) {
              return;
    }
  8. Перемещение между DataGrid внутри системы осуществляется разными способами, в зависимости от установленных свойств. Рассмотрим 2 случая:
    1. Перемещение по умолчанию
    2. Pic 4

    3. Копирование файла(ов)
    4. Pic 5

      При манипуляции комбинациями Ctrl+C/Ctrl+V все аналогично, только учитывается свойство CanPasteSystemObjects

    Собственно, это все, что я хотел рассказать о развитии Drag&Drop’а.

    Приятной работы!

    P.S. Вся функциональность доступна в версиях 3.4.0.99+ и 3.4.1.16+

Нравится

Поделиться

2 комментария

Подскажите, есть ли возможность обновиться с версии 3.3.2.310 до версии 3.4? Необходимо получить возможность перетягивания файлов из Террасофт в письмо MS Outlook.
С какими проблемами можно столкнуться при этом (совместимость конфигурации 3.3.2 с версией бинарных файлов 3.4, прочее)?

Андрей, обновление возможно. Как один из вариантов - развернуть 3.4 и перенести данные из 3.3.2 на уровне БД. Также, необходимо согласовать изменение лицензий с ответственным менеджером со стороны Terrasoft.

Показать все комментарии