从VirtualTreeView拖放到外壳(Ole拖放) [英] Drag and drop from VirtualTreeView to shell (Ole drag and drop)
问题描述
我正在尝试从 VirtualTreeView
拖放以在shell中创建文件(从 VirtualTreeView
拖放到File Explorer或桌面文件夹中的文件夹)./p>
我只找到了执行相反操作的示例(VirtualTreeView的外壳),但是我找不到执行此操作的任何示例.帮助吗?
在Windows中进行任何拖放操作都涉及创建 IDataObject
,并将该对象提供给Windows.
虚拟树视图为您处理了很多繁琐的工作,创建了一个为您实现 IDataObject
的对象.然后,当您需要帮助填充树时,该树就会引发事件.
在通过复制粘贴或拖放操作传递类似文件" 的内容时,您需要向 IDataObject
添加两种剪贴板格式:>
-
CF_FILEDESCRIPTOR
和 -
CF_FILECONTENTS
除了支持虚拟树本身将添加的格式外,您还可以选择表示支持更多剪贴板格式.
OnGetUserClipboardFormats事件
在此事件中,您有机会向树将要创建的 IDataObject
添加其他剪贴板格式:
过程TForm1.lvAttachmentsGetUserClipboardFormats(发送者:TBaseVirtualTree;var格式:TFormatEtcArray);变种i:整数;开始//添加CF_FILEDESCRIPTOR和CF_FILECONTENTS的格式i:=长度(格式);SetLength(Formats,i + 1);Formats [i] .cfFormat:= CF_FILEDESCRIPTOR;Formats [i] .ptd:= nil;格式[i] .dwAspect:= DVASPECT_CONTENT;Formats [i] .lindex:= -1;格式[i] .tymed:= TYMED_HGLOBAL;i:=长度(格式);SetLength(Formats,i + 1);Formats [i] .cfFormat:= CF_FILECONTENTS;Formats [i] .ptd:= nil;格式[i] .dwAspect:= DVASPECT_CONTENT;Formats [i] .lindex:= 0;格式[i] .tymed:= TYMED_ISTREAM;结尾;
然后,作为拖放操作的一部分,树将向外壳赋予 IDataObject
.
稍后,用户放置项目的应用程序将枚举 IDataObject
中的所有格式,例如:
-
CF_HTML
("HTML格式") -
CFSTR_FILEDESCRIPTOR
("FileGroupDescriptorW") -
CFSTR_FILECONTENTS
("FileContents") -
CF_ENHMETAFILE
它将看到 IDataObject
包含 FileDescriptor 和 FileContents .
接收方的应用程序然后将要求 IDataObject
实际咳嗽数据.(此延迟渲染" 是一件好事,这意味着您的源应用程序实际上不需要读取任何内容,除非实际收到了请求).
OnRenderOleData事件
这是虚拟树意识到要求其渲染 IDataObject
来渲染某些东西的事件,它最终需要您渲染该实际内容.
使用这两种剪贴板格式的总体思路是:
-
CF_FILEDESCRIPTOR
可让您返回描述类似文件的记录(例如,文件名,文件大小,创建日期,最后修改日期,最后访问日期) -
CF_FILECONTENTS
允许您返回包含实际文件内容的IStream
过程TForm1.lvAttachmentsRenderOLEData(发件人:TBaseVirtualTree; const FormatEtcIn:tagFORMATETC;中等:tagSTGMEDIUM;ForClipboard:布尔值;var结果:HRESULT);变种全球:HGLOBAL;stm:IStream;开始如果FormatEtcIn.cfFormat = CF_FILEDESCRIPTOR,则开始全局:= GetAttachmentFileDescriptorsFromListView(lvAttachments,ForClipboard);如果global = 0,那么出口;ZeroMemory(@Medium,SizeOf(Medium));Medium.tymed:= TYMED_HGLOBAL;Medium.hGlobal:=全局;结果:= S_OK;结尾否则,如果FormatEtcIn.cfFormat = CF_FILECONTENTS,则开始ZeroMemory(@Medium,SizeOf(Medium));Medium.tymed:= TYMED_ISTREAM;结果:= GetAttachmentStreamFromListView(lvAttachments,ForClipboard,FormatEtcIn.lindex,stm);如果失败(结果),则出口;Medium.stm:= Pointer(stm);IUnknown(Medium.stm)._ AddRef;结果:= S_OK;结尾;结尾;
第一个帮助函数创建一个 FILE_DESCRIPTOR
对象的数组,并将它们复制到 HGLOBAL
分配的内存中:
function GetAttachmentFileDescriptorsFromListView(来源:TVirtualStringTree; ForClipboard:布尔值):HGLOBAL;变种i:整数;nCount:整数;节点:TNodeArray;描述符:TFileDescriptorDynArray;数据:附件;开始结果:= 0;如果是ForClipboard,则节点:= Source.GetSortedCutCopySet(False)别的节点:= Source.GetSortedSelection(False);如果Length(nodes)= 0则出口;nCount:= 0;对于i:= 0到Length(nodes)-1做开始//从该节点获取文件数据:= GetNodeDataFromNode(nodes [i]);如果未分配(数据),则继续;//将描述符数组的大小增加一Inc(nCount);SetLength(Descriptors,nCount);//填写下一个描述符描述符[nCount-1]:= data.ToWindowsFileDescriptor;结尾;结果:= FileDescriptorsToHGLOBAL(descriptors);结尾;
第二个帮助程序将类文件的二进制内容复制到 IStream
:
function GetAttachmentStreamFromListView(来源:TVirtualStringTree; ForClipboard:布尔; lindex:整数; var stm:IStream):HResult;变种节点:TNodeArray;数据:附件;开始结果:= E_FAIL;如果是ForClipboard,则节点:= Source.GetSortedCutCopySet(False)别的节点:= Source.GetSortedSelection(False);如果Length(nodes)= 0则出口;如果(lIndex< Low(Nodes))或(lIndex> High(Nodes))则开始结果:= DV_E_LINDEX;出口;结尾;//从该节点获取文件数据:= GetNodeDataFromNode(nodes [i]);如果未分配(数据),则继续;//将内容提取到IStream包装的内存流中stm:= data.GetStream(nil);结果:= S_OK;结尾;
您的附件对象,无论需要知道什么:
- 如何将自己表示为
TFileDescriptor
- 如何将内容作为
IStream
返回
I am trying to drag and drop from VirtualTreeView
to create a file in shell (drag and drop from VirtualTreeView
to a folder in File Explorer or desktop folder).
I only found example of doing the opposite (shell to VirtualTreeView), but I cannot find any example for doing that. Help?
Doing any drag-drop operations in Windows involves creating an IDataObject
, and giving that object to Windows.
The Virtual Treeview handles a lot of that grunt-work for you, creating an object that implements IDataObject
for you. The tree then raises events when you need to help populate it.
When passing "file-like" things through a copy-paste or a drag-drop, you are require to add two clipboard formats to the IDataObject
:
CF_FILEDESCRIPTOR
, andCF_FILECONTENTS
In addition to support for formats that the virtualtree itself will add, you can choose to indicate support for more clipboard format.
OnGetUserClipboardFormats Event
This is the event where you are given a chance to add additional clipboard formats to the IDataObject
that the tree will be creating:
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;
The tree will then given the IDataObject
to the shell as part of the drag-drop operation.
Later, an application that the user dropped items onto will enumerate all formats in the IDataObject
, e.g.:
CF_HTML
("HTML Format")CFSTR_FILEDESCRIPTOR
("FileGroupDescriptorW")CFSTR_FILECONTENTS
("FileContents")CF_ENHMETAFILE
And it will see that the IDataObject
contains FileDescriptor and FileContents.
The receiving application will then ask the IDataObject
to actually cough up data. (This "delayed-rendering" is a good thing, it means your source application doesn't actually have to read any content unless it actually gets requested).
OnRenderOleData Event
This is the event where the virtual tree realizes its IDataObject
has been asked to render something, and it needs you to finally render that actual content.
The general idea with these two clipboard formats is:
CF_FILEDESCRIPTOR
lets you return a record that describes the file-like thing (e.g. filename, file size, created date, last modified date, last accessed date)CF_FILECONTENTS
lets you return anIStream
that contains the actual file contents
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;
The first helper function creates an array of FILE_DESCRIPTOR
objects, and copies them to a HGLOBAL
allocated memory:
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;
The second helper copies your file-like thing's binary contents to an 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;
Your attachment object, whatever it is has to know:
- how to represent itself as a
TFileDescriptor
- how to return the contents as an
IStream
这篇关于从VirtualTreeView拖放到外壳(Ole拖放)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!