从 VirtualTreeView 拖放到 shell(Ole 拖放)
Drag and drop from VirtualTreeView to shell (Ole drag and drop)
我正在尝试从 VirtualTreeView
拖放以在 shell 中创建文件(从 VirtualTreeView
拖放至文件资源管理器或桌面文件夹中的文件夹)。
我只找到了相反的例子(shell 到 VirtualTreeView),但我找不到任何这样做的例子。帮忙?
在 Windows 中执行任何拖放操作都涉及创建一个 IDataObject
,并将该对象提供给 Windows。
Virtual Treeview 为您处理大量繁重的工作,创建一个为您实现 IDataObject
的对象。当您需要帮助填充它时,树就会引发事件。
当通过复制粘贴或拖放传递 "file-like" 内容时,您需要向 IDataObject
添加两种剪贴板格式:
CF_FILEDESCRIPTOR
和
CF_FILECONTENTS
除了支持虚拟树本身会添加的格式外,您还可以选择表示支持更多的剪贴板格式。
OnGetUserClipboardFormats 事件
在这个事件中,您有机会向 IDataObject
树将要创建的 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 Format")
CFSTR_FILEDESCRIPTOR
("FileGroupDescriptorW")
CFSTR_FILECONTENTS
("FileContents")
CF_ENHMETAFILE
它会看到 IDataObject
包含 FileDescriptor 和 FileContents.
然后,接收应用程序将要求 IDataObject
实际发出数据。 (这 "delayed-rendering" 是一件好事,这意味着您的源应用程序实际上不必读取任何内容,除非它确实被请求)。
OnRenderOleData 事件
这是虚拟树意识到其 IDataObject
已被要求呈现某些内容的事件,它需要您最终呈现该实际内容。
这两种剪贴板格式的总体思路是:
CF_FILEDESCRIPTOR
可让您 return 描述类似文件的记录(例如文件名、文件大小、创建日期、最后修改日期、最后访问日期)
CF_FILECONTENTS
让你 return 一个包含实际文件内容的 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
- 如何 return 将内容作为
IStream
我正在尝试从 VirtualTreeView
拖放以在 shell 中创建文件(从 VirtualTreeView
拖放至文件资源管理器或桌面文件夹中的文件夹)。
我只找到了相反的例子(shell 到 VirtualTreeView),但我找不到任何这样做的例子。帮忙?
在 Windows 中执行任何拖放操作都涉及创建一个 IDataObject
,并将该对象提供给 Windows。
Virtual Treeview 为您处理大量繁重的工作,创建一个为您实现 IDataObject
的对象。当您需要帮助填充它时,树就会引发事件。
当通过复制粘贴或拖放传递 "file-like" 内容时,您需要向 IDataObject
添加两种剪贴板格式:
CF_FILEDESCRIPTOR
和CF_FILECONTENTS
除了支持虚拟树本身会添加的格式外,您还可以选择表示支持更多的剪贴板格式。
OnGetUserClipboardFormats 事件
在这个事件中,您有机会向 IDataObject
树将要创建的 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 Format")CFSTR_FILEDESCRIPTOR
("FileGroupDescriptorW")CFSTR_FILECONTENTS
("FileContents")CF_ENHMETAFILE
它会看到 IDataObject
包含 FileDescriptor 和 FileContents.
然后,接收应用程序将要求 IDataObject
实际发出数据。 (这 "delayed-rendering" 是一件好事,这意味着您的源应用程序实际上不必读取任何内容,除非它确实被请求)。
OnRenderOleData 事件
这是虚拟树意识到其 IDataObject
已被要求呈现某些内容的事件,它需要您最终呈现该实际内容。
这两种剪贴板格式的总体思路是:
CF_FILEDESCRIPTOR
可让您 return 描述类似文件的记录(例如文件名、文件大小、创建日期、最后修改日期、最后访问日期)CF_FILECONTENTS
让你 return 一个包含实际文件内容的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
- 如何 return 将内容作为
IStream