从VirtualTreeView拖放到shell(Ole拖放)



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

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

在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;

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

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

  • CF_HTML("HTML格式"(
  • CFSTR_FILEDESCRIPTOR("文件组描述符W"(
  • 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返回

相关内容

  • 没有找到相关文章

最新更新