【发布时间】:2020-03-05 13:51:33
【问题描述】:
我正在尝试从 VirtualTreeView 拖放以在 shell 中创建文件(从 VirtualTreeView 拖放到文件资源管理器或桌面文件夹中的文件夹)。
我只找到了相反的例子(VirtualTreeView 的shell),但我找不到任何这样做的例子。帮忙?
【问题讨论】:
标签: delphi drag-and-drop c++builder ole virtualtreeview
我正在尝试从 VirtualTreeView 拖放以在 shell 中创建文件(从 VirtualTreeView 拖放到文件资源管理器或桌面文件夹中的文件夹)。
我只找到了相反的例子(VirtualTreeView 的shell),但我找不到任何这样做的例子。帮忙?
【问题讨论】:
标签: delphi drag-and-drop c++builder ole virtualtreeview
在 Windows 中执行任何拖放操作都涉及创建 IDataObject,并将该对象提供给 Windows。
Virtual Treeview 为您处理了很多繁重的工作,为您创建了一个实现IDataObject 的对象。然后,当您需要帮助填充树时,树会引发事件。
当通过复制粘贴或拖放传递“类文件” 内容时,您需要将两种剪贴板格式添加到IDataObject:
CF_FILEDESCRIPTOR 和 CF_FILECONTENTS除了支持虚拟树本身会添加的格式外,您还可以选择指示支持更多剪贴板格式。
在此事件中,您有机会将其他剪贴板格式添加到树将创建的 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 格式")
CFSTR_FILEDESCRIPTOR ("FileGroupDescriptorW")
CFSTR_FILECONTENTS ("文件内容")
CF_ENHMETAFILE它会看到 IDataObject 包含 FileDescriptor 和 FileContents。
然后接收应用程序将要求IDataObject 实际咳出数据。 (这个
这是虚拟树意识到其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
【讨论】: