我试图从VirtualTreeView
中拖放以在shell中创建文件(从VirtualTreeView
拖放到文件资源管理器或桌面文件夹中的文件夹)。
我只找到了一个相反的例子(shell到VirtualTreeView),但我找不到任何这样做的例子。帮助?
发布于 2020-04-05 19:54:10
在Windows中执行任何拖放操作都需要创建一个IDataObject
,并将该对象交给Windows.
Virtual为您处理大量此类工作,为您创建一个实现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;
然后,树将将IDataObject
作为拖放操作的一部分提供给shell。
稍后,用户将项放入的应用程序将枚举IDataObject
中的所有格式,例如:
CF_HTML
(“Format")CFSTR_FILEDESCRIPTOR
("FileGroupDescriptorW")CFSTR_FILECONTENTS
("FileContents")CF_ENHMETAFILE
”)
它将看到FileDescriptor和FileContents.包含IDataObject
。
然后,接收应用程序将要求IDataObject
实际提取数据。(这种“延迟呈现”是一件好事,它意味着您的源应用程序实际上不必读取任何内容,除非它确实被请求)。
OnRenderOleData事件
这是虚拟树实现其IDataObject
被要求呈现某些内容的事件,它需要您最终呈现该实际内容。
这两种剪贴板格式的一般思想是:
CF_FILEDESCRIPTOR
允许您返回描述类似文件的内容的记录(例如文件名、文件大小、创建日期、上次修改日期、上次访问日期),date)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
https://stackoverflow.com/questions/60546907
复制相似问题