首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >从VirtualTreeView拖放到外壳(Ole拖放)

从VirtualTreeView拖放到外壳(Ole拖放)
EN

Stack Overflow用户
提问于 2020-03-05 13:51:33
回答 1查看 401关注 0票数 1

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

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

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-04-05 19:54:10

在Windows中执行任何拖放操作都需要创建一个IDataObject,并将该对象交给Windows.

Virtual为您处理大量此类工作,为您创建一个实现IDataObject的对象。然后,当需要帮助填充树时,树会引发事件。

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

  • CF_FILEDESCRIPTOR,和
  • CF_FILECONTENTS

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

OnGetUserClipboardFormats事件

在这个事件中,您有机会向树将要创建的IDataObject添加额外的剪贴板格式:

代码语言:javascript
运行
复制
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 (“Format")
  • CFSTR_FILEDESCRIPTOR ("FileGroupDescriptorW")
  • CFSTR_FILECONTENTS ("FileContents")
  • CF_ENHMETAFILE

”)

它将看到FileDescriptorFileContents.包含IDataObject

然后,接收应用程序将要求IDataObject实际提取数据。(这种“延迟呈现”是一件好事,它意味着您的源应用程序实际上不必读取任何内容,除非它确实被请求)。

OnRenderOleData事件

这是虚拟树实现其IDataObject被要求呈现某些内容的事件,它需要您最终呈现该实际内容。

这两种剪贴板格式的一般思想是:

  • CF_FILEDESCRIPTOR允许您返回描述类似文件的内容的记录(例如文件名、文件大小、创建日期、上次修改日期、上次访问日期),date)
  • CF_FILECONTENTS允许您返回一个包含实际文件内容

IStream

代码语言:javascript
运行
复制
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分配的内存中:

代码语言:javascript
运行
复制
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

代码语言:javascript
运行
复制
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

  • how以将内容返回为IStream
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/60546907

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档