我正在尝试从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
包含FileDescriptor和FileContents。
然后,接收应用程序将要求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
返回