从VirtualTreeView拖放到外壳(Ole拖放) [英] Drag and drop from VirtualTreeView to shell (Ole drag and drop)

查看:67
本文介绍了从VirtualTreeView拖放到外壳(Ole拖放)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试从 VirtualTreeView 拖放以在shell中创建文件(从 VirtualTreeView 拖放到File Explorer或桌面文件夹中的文件夹)./p>

我只找到了执行相反操作的示例(VirtualTreeView的外壳),但是我找不到执行此操作的任何示例.帮助吗?

解决方案

在Windows中进行任何拖放操作都涉及创建 IDataObject ,并将该对象提供给Windows.

虚拟树视图为您处理了很多繁琐的工作,创建了一个为您实现 IDataObject 的对象.然后,当您需要帮助填充树时,该树就会引发事件.

在通过复制粘贴或拖放操作传递类似文件" 的内容时,您需要向 IDataObject 添加两种剪贴板格式:

  • CF_FILEDESCRIPTOR
  • CF_FILECONTENTS

除了支持虚拟树本身将添加的格式外,您还可以选择表示支持更多剪贴板格式.

OnGetUserClipboardFormats事件

在此事件中,您有机会向树将要创建的 IDataObject 添加其他剪贴板格式:

 过程TForm1.lvAttachmentsGetUserClipboardFormats(发送者:TBaseVirtualTree;var格式:TFormatEtcArray);变种i:整数;开始//添加CF_FILEDESCRIPTOR和CF_FILECONTENTS的格式i:=长度(格式);SetLength(Formats,i + 1);Formats [i] .cfFormat:= CF_FILEDESCRIPTOR;Formats [i] .ptd:= nil;格式[i] .dwAspect:= DVASPECT_CONTENT;Formats [i] .lindex:= -1;格式[i] .tymed:= TYMED_HGLOBAL;i:=长度(格式);SetLength(Formats,i + 1);Formats [i] .cfFormat:= CF_FILECONTENTS;Formats [i] .ptd:= nil;格式[i] .dwAspect:= DVASPECT_CONTENT;Formats [i] .lindex:= 0;格式[i] .tymed:= TYMED_ISTREAM;结尾; 

然后,作为拖放操作的一部分,树将向外壳赋予 IDataObject .

稍后,用户放置项目的应用程序将枚举 IDataObject 中的所有格式,例如:

  • CF_HTML ("HTML格式")
  • CFSTR_FILEDESCRIPTOR ("FileGroupDescriptorW")
  • CFSTR_FILECONTENTS ("FileContents")
  • CF_ENHMETAFILE

它将看到 IDataObject 包含 FileDescriptor FileContents .

接收方的应用程序然后将要求 IDataObject 实际咳嗽数据.(此延迟渲染" 是一件好事,这意味着您的源应用程序实际上不需要读取任何内容,除非实际收到了请求).

OnRenderOleData事件

这是虚拟树意识到要求其渲染 IDataObject 来渲染某些东西的事件,它最终需要您渲染该实际内容.

使用这两种剪贴板格式的总体思路是:

  • CF_FILEDESCRIPTOR 可让您返回描述类似文件的记录(例如,文件名,文件大小,创建日期,最后修改日期,最后访问日期)
  • CF_FILECONTENTS 允许您返回包含实际文件内容的 IStream

 过程TForm1.lvAttachmentsRenderOLEData(发件人:TBaseVirtualTree; const FormatEtcIn:tagFORMATETC;中等:tagSTGMEDIUM;ForClipboard:布尔值;var结果:HRESULT);变种全球:HGLOBAL;stm:IStream;开始如果FormatEtcIn.cfFormat = CF_FILEDESCRIPTOR,则开始全局:= GetAttachmentFileDescriptorsFromListView(lvAttachments,ForClipboard);如果global = 0,那么出口;ZeroMemory(@Medium,SizeOf(Medium));Medium.tymed:= TYMED_HGLOBAL;Medium.hGlobal:=全局;结果:= S_OK;结尾否则,如果FormatEtcIn.cfFormat = CF_FILECONTENTS,则开始ZeroMemory(@Medium,SizeOf(Medium));Medium.tymed:= TYMED_ISTREAM;结果:= GetAttachmentStreamFromListView(lvAttachments,ForClipboard,FormatEtcIn.lindex,stm);如果失败(结果),则出口;Medium.stm:= Pointer(stm);IUnknown(Medium.stm)._ AddRef;结果:= S_OK;结尾;结尾; 

第一个帮助函数创建一个 FILE_DESCRIPTOR 对象的数组,并将它们复制到 HGLOBAL 分配的内存中:

  function GetAttachmentFileDescriptorsFromListView(来源:TVirtualStringTree; ForClipboard:布尔值):HGLOBAL;变种i:整数;nCount:整数;节点:TNodeArray;描述符:TFileDescriptorDynArray;数据:附件;开始结果:= 0;如果是ForClipboard,则节点:= Source.GetSortedCutCopySet(False)别的节点:= Source.GetSortedSelection(False);如果Length(nodes)= 0则出口;nCount:= 0;对于i:= 0到Length(nodes)-1做开始//从该节点获取文件数据:= GetNodeDataFromNode(nodes [i]);如果未分配(数据),则继续;//将描述符数组的大小增加一Inc(nCount);SetLength(Descriptors,nCount);//填写下一个描述符描述符[nCount-1]:= data.ToWindowsFileDescriptor;结尾;结果:= FileDescriptorsToHGLOBAL(descriptors);结尾; 

第二个帮助程序将类文件的二进制内容复制到 IStream :

  function GetAttachmentStreamFromListView(来源:TVirtualStringTree; ForClipboard:布尔; lindex:整数; var stm:IStream):HResult;变种节点:TNodeArray;数据:附件;开始结果:= E_FAIL;如果是ForClipboard,则节点:= Source.GetSortedCutCopySet(False)别的节点:= Source.GetSortedSelection(False);如果Length(nodes)= 0则出口;如果(lIndex< Low(Nodes))或(lIndex> High(Nodes))则开始结果:= DV_E_LINDEX;出口;结尾;//从该节点获取文件数据:= GetNodeDataFromNode(nodes [i]);如果未分配(数据),则继续;//将内容提取到IStream包装的内存流中stm:= data.GetStream(nil);结果:= S_OK;结尾; 

您的附件对象,无论需要知道什么:

  • 如何将自己表示为 TFileDescriptor
  • 如何将内容作为 IStream
  • 返回

I am trying to drag and drop from VirtualTreeView to create a file in shell (drag and drop from VirtualTreeView to a folder in File Explorer or desktop folder).

I only found example of doing the opposite (shell to VirtualTreeView), but I cannot find any example for doing that. Help?

解决方案

Doing any drag-drop operations in Windows involves creating an IDataObject, and giving that object to Windows.

The Virtual Treeview handles a lot of that grunt-work for you, creating an object that implements IDataObject for you. The tree then raises events when you need to help populate it.

When passing "file-like" things through a copy-paste or a drag-drop, you are require to add two clipboard formats to the IDataObject:

  • CF_FILEDESCRIPTOR, and
  • CF_FILECONTENTS

In addition to support for formats that the virtualtree itself will add, you can choose to indicate support for more clipboard format.

OnGetUserClipboardFormats Event

This is the event where you are given a chance to add additional clipboard formats to the IDataObject that the tree will be creating:

procedure TForm1.lvAttachmentsGetUserClipboardFormats(Sender: TBaseVirtualTree;
  var Formats: TFormatEtcArray);
var
    i: Integer;
begin
    //Add formats for CF_FILEDESCRIPTOR and CF_FILECONTENTS
    i := Length(Formats);
    SetLength(Formats, i + 1);
    Formats[i].cfFormat := CF_FILEDESCRIPTOR;
    Formats[i].ptd := nil;
    Formats[i].dwAspect := DVASPECT_CONTENT;
    Formats[i].lindex := -1;
    Formats[i].tymed := TYMED_HGLOBAL;

    i := Length(Formats);
    SetLength(Formats, i + 1);
    Formats[i].cfFormat := CF_FILECONTENTS;
    Formats[i].ptd := nil;
    Formats[i].dwAspect := DVASPECT_CONTENT;
    Formats[i].lindex := 0;
    Formats[i].tymed := TYMED_ISTREAM;
end;

The tree will then given the IDataObject to the shell as part of the drag-drop operation.

Later, an application that the user dropped items onto will enumerate all formats in the IDataObject, e.g.:

  • CF_HTML ("HTML Format")
  • CFSTR_FILEDESCRIPTOR ("FileGroupDescriptorW")
  • CFSTR_FILECONTENTS ("FileContents")
  • CF_ENHMETAFILE

And it will see that the IDataObject contains FileDescriptor and FileContents.

The receiving application will then ask the IDataObject to actually cough up data. (This "delayed-rendering" is a good thing, it means your source application doesn't actually have to read any content unless it actually gets requested).

OnRenderOleData Event

This is the event where the virtual tree realizes its IDataObject has been asked to render something, and it needs you to finally render that actual content.

The general idea with these two clipboard formats is:

  • CF_FILEDESCRIPTOR lets you return a record that describes the file-like thing (e.g. filename, file size, created date, last modified date, last accessed date)
  • CF_FILECONTENTS lets you return an IStream that contains the actual file contents

procedure TForm1.lvAttachmentsRenderOLEData(Sender: TBaseVirtualTree; const FormatEtcIn: tagFORMATETC;
  out Medium: tagSTGMEDIUM; ForClipboard: Boolean; var Result: HRESULT);
var
    global: HGLOBAL;
    stm: IStream;
begin
    if FormatEtcIn.cfFormat = CF_FILEDESCRIPTOR then
    begin
        global := GetAttachmentFileDescriptorsFromListView(lvAttachments, ForClipboard);
        if global = 0 then
            Exit;
        ZeroMemory(@Medium, SizeOf(Medium));
        Medium.tymed := TYMED_HGLOBAL;
        Medium.hGlobal := global;
        Result := S_OK;
    end
    else if FormatEtcIn.cfFormat = CF_FILECONTENTS then
    begin
        ZeroMemory(@Medium, SizeOf(Medium));
        Medium.tymed := TYMED_ISTREAM;
        Result := GetAttachmentStreamFromListView(lvAttachments, ForClipboard, FormatEtcIn.lindex, stm);
        if Failed(Result) then
            Exit;
        Medium.stm := Pointer(stm);
        IUnknown(Medium.stm)._AddRef;
        Result := S_OK;
    end;
end;

The first helper function creates an array of FILE_DESCRIPTOR objects, and copies them to a HGLOBAL allocated memory:

function GetAttachmentFileDescriptorsFromListView(Source: TVirtualStringTree; ForClipboard: Boolean): HGLOBAL;
var
    i: Integer;
    nCount: Integer;
    nodes: TNodeArray;
    descriptors: TFileDescriptorDynArray; 
    data: TAttachment;
begin
    Result := 0;

   if ForClipboard then
      nodes := Source.GetSortedCutCopySet(False)
   else
      nodes := Source.GetSortedSelection(False);

   if Length(nodes) = 0 then
      Exit;

   nCount := 0;
   for i := 0 to Length(nodes) - 1 do
   begin
        //Get the file thing from this node
        data := GetNodeDataFromNode(nodes[i]);
        if not Assigned(data) then
            Continue;

        //Increase the size of our descriptors array by one
        Inc(nCount);
        SetLength(Descriptors, nCount);

        //Fill in the next descriptor
        descriptors[nCount-1] := data.ToWindowsFileDescriptor;
   end;

   Result := FileDescriptorsToHGLOBAL(descriptors);
end;

The second helper copies your file-like thing's binary contents to an IStream:

function GetAttachmentStreamFromListView(Source: TVirtualStringTree; ForClipboard: Boolean; lindex: Integer; var stm: IStream): HResult;
var
    nodes: TNodeArray;
    data: TAttachment;
begin
   Result := E_FAIL;

   if ForClipboard then
      nodes := Source.GetSortedCutCopySet(False)
   else
      nodes := Source.GetSortedSelection(False);

   if Length(nodes) = 0 then
      Exit;

   if (lIndex < Low(Nodes)) or (lIndex > High(Nodes)) then
    begin
      Result := DV_E_LINDEX;
      Exit;
   end;

   //Get the file thing from this node
   data := GetNodeDataFromNode(nodes[i]);
   if not Assigned(data) then
      Continue;

    //Fetch the content into a IStream wrapped memory stream
    stm := data.GetStream(nil);
    Result := S_OK;
end;

Your attachment object, whatever it is has to know:

  • how to represent itself as a TFileDescriptor
  • how to return the contents as an IStream

这篇关于从VirtualTreeView拖放到外壳(Ole拖放)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆