【问题标题】:Drag and drop from VirtualTreeView to shell (Ole drag and drop)从 VirtualTreeView 拖放到 shell(Ole 拖放)
【发布时间】:2020-03-05 13:51:33
【问题描述】:

我正在尝试从 VirtualTreeView 拖放以在 shell 中创建文件(从 VirtualTreeView 拖放到文件资源管理器或桌面文件夹中的文件夹)。

我只找到了相反的例子(VirtualTreeView 的shell),但我找不到任何这样做的例子。帮忙?

【问题讨论】:

    标签: delphi drag-and-drop c++builder ole virtualtreeview


    【解决方案1】:

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

    Virtual Treeview 为您处理了很多繁重的工作,为您创建了一个实现IDataObject 的对象。然后,当您需要帮助填充树时,树会引发事件。

    当通过复制粘贴或拖放传递“类文件” 内容时,您需要将两种剪贴板格式添加到IDataObject

    • CF_FILEDESCRIPTOR
    • CF_FILECONTENTS

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

    OnGetUserClipboardFormats 事件

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

    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;
    

    然后,树会将 IDataObject 作为拖放操作的一部分提供给 shell。

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

    • CF_HTML ("HTML 格式")
    • CFSTR_FILEDESCRIPTOR ("FileGroupDescriptorW")
    • CFSTR_FILECONTENTS ("文件内容")
    • CF_ENHMETAFILE

    它会看到 IDataObject 包含 FileDescriptorFileContents

    然后接收应用程序将要求IDataObject 实际咳出数据。 (这个“延迟渲染”是一件好事,它意味着你的源应用程序实际上不需要读取任何内容,除非它真的被请求)。

    OnRenderOleData 事件

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

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

    • CF_FILEDESCRIPTOR 允许您返回描述类似文件的内容的记录(例如文件名、文件大小、创建日期、上次修改日期、上次访问日期)
    • CF_FILECONTENTS 让您返回一个包含实际文件内容的IStream
    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;
    

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

    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;
    

    第二个助手将你的类文件的二进制内容复制到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;
    

    您的附件对象,无论它是什么都必须知道:

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

    【讨论】:

      猜你喜欢
      • 2012-11-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-03
      相关资源
      最近更新 更多