如何将程序中的内容“拖”到桌面或文件夹中(DoDragDrop高难度高分问题)

chenybin 2007-01-30 11:30:17
实现拖出,

现在程序的数据源是Listview,具体的内容根据Listview选中的文件从ftp下载(这个无所谓,反正从其他地方来的,数据库的stream等等都可以,不同于普通文本拖放),希望拖放完毕,鼠标放开以后开始下载ftp的内容,并保存到本地,

(从程序窗口中的listview拖到桌面等地方)

不想用控件,主要想知道原理,如果哪位兄弟帮我把代码从控件中分离出来夜行,非常感谢
...全文
785 点赞 收藏 10
写回复
10 条回复
SonicX 2007年01月31日
转变下思路吧
在GetData过程做点改动,比如弄个文件进度条什么的,后台下载数据咯
回复 点赞
chenybin 2007年01月31日
是的,不过会有个小问题,就是如果网络不是很好,我们在DoDragDrop前准备数据这个过程会很漫长,这样不太好
回复 点赞
SonicX 2007年01月31日
根据MSDN说法就是先将文件存到硬盘上再进行拖拽,拖拽文件没有内存操作方式
你可以看下其他软件的拖拽,都是将文件先存到TEMP目录的,比如WINRAR,FOXMAIL...
回复 点赞
chenybin 2007年01月31日
谢谢楼上两位兄弟,dabaicai(小老头) 思路可以考虑,不知道算不算“正统”的做法

谢谢

to SonicX(SonicX),现在的问题是文件其实并不存在本地

楼下继续
回复 点赞
dabaicai 2007年01月31日
事实上你拖拽的时候是先已经选定好了的,比如从A拖到B里面,B里面显示的只是A里面选中的东西(桌面也是一个文件夹),而实际上是开了线程开始下载(处理)的
回复 点赞
SonicX 2007年01月31日
拖放要放在OLE对象里涉及到的API
DoDragDrop

DoDragDrop需要用到IDataObject,IDropSource
这个DELPHI没有申明需要自己做,过程要根据需要自己写了
关键过程为
GetData 向目标窗口传送数据
QueryGetData 目标窗口查询数据接口类型
其他可以直接Result := E_NOTIMPL;表示为接口不支持
还有个SetData接口,这个是将OLE数据存入IDataObject类,因为过程是自己写的,可以直接使用程序中的公共变量数据,不用通过SetData将数据写入IDataObject类

uses ShlObj,ActiveX, ComObj;
Tform1 = class(TForm, IDropSource)
....
....
Pubilc
//IDropSource
function QueryContinueDrag(fEscapePressed: BOOL; grfKeyState: Longint): HResult; stdcall;
function GiveFeedback(dwEffect: Longint): HResult; stdcall;
end;

TDataObject = class(TInterfacedObject,IDataObject)
public
constructor Create;
procedure Free;

function GetData(const formatetcIn: TFormatEtc; out medium: TStgMedium): HResult; stdcall;
function GetDataHere(const formatetc: TFormatEtc; out medium: TStgMedium): HResult; stdcall;
function QueryGetData(const formatetc: TFormatEtc): HResult; stdcall;
function GetCanonicalFormatEtc(const formatetc: TFormatEtc; out formatetcOut: TFormatEtc): HResult;stdcall;
function SetData(const formatetc: TFormatEtc; var medium: TStgMedium; fRelease: BOOL): HResult; stdcall;
function EnumFormatEtc(dwDirection: Longint; out enumFormatEtc: IEnumFormatEtc): HResult; stdcall;
function DAdvise(const formatetc: TFormatEtc; advf: Longint; const advSink: IAdviseSink; out dwConnection: Longint): HResult; stdcall;
function DUnadvise(dwConnection: Longint): HResult; stdcall;
function EnumDAdvise(out enumAdvise: IEnumStatData): HResult; stdcall;
end;

function Tform1.QueryContinueDrag(fEscapePressed: BOOL;
grfKeyState: Longint): HResult; stdcall;
begin
if fEscapePressed or (grfKeyState and MK_RBUTTON = MK_RBUTTON) then
Result := DRAGDROP_S_CANCEL
else
Result := DRAGDROP_S_DROP;
end;
function Tform1.GiveFeedback(dwEffect: Longint): HResult; stdcall;
begin
Result := DRAGDROP_S_USEDEFAULTCURSORS;
end;

//QueryGetData过程
function TDataObject.QueryGetData(const formatetc: TFormatEtc): HResult; stdcall;
begin
Result := DV_E_FORMATETC; //不支持的格式

if (formatetc.cfFormat=CF_HDROP) and //表示支持文件拖拽格式
(formatetc.tymed=TYMED_HGLOBAL ) and
(formatetc.dwAspect=DVASPECT_CONTENT) then
Result := S_OK;
end;

//GetData过程
function TDataObject.GetData(const formatetcIn: TFormatEtc; out medium: TStgMedium): HResult; stdcall;
var
BufferText : String;
pGlobal : Pointer;
begin
Result := DV_E_FORMATETC; //不支持的格式
if not (Self.QueryGetData(formatetcIn)=S_OK) then exit;
FillChar(Medium,Sizeof(TStgMedium),0);
Medium.tymed:=formatetcIn.tymed;
BufferText:='c:\temp\aa.txt'+#0+#0;
//需要拖拽的文件,多个的如下
//'c:\temp\aa.txt'+#0+#0+'c:\temp\aa.txt'+#0+#0;+'c:\temp\aa.txt'+#0+#0;
Medium.hGlobal := GlobalAlloc(GMEM_ZEROINIT or GMEM_MOVEABLE or GMEM_SHARE, Length(BufferText)+1+Sizeof(TDropFiles));
pGlobal := GlobalLock(Medium.hGlobal);
PDropFiles(pGlobal)^.pFiles:=Sizeof(TDropFiles);
PDropFiles(pGlobal)^.pt:=Point(0,0);
PDropFiles(pGlobal)^.fNC:=False;
PDropFiles(pGlobal)^.fWide:=False;
inc(Longword(pGlobal),Sizeof(TDropFiles)); //指针后移
CopyMemory(PGlobal,Pchar(BufferText),Length(BufferText)+1);
GlobalUnlock(Medium.hGlobal);
Medium.unkForRelease := nil;
Result := S_OK;
end;


//////////////
在需要拖拽的时候
var
DataObject : TDataObject;
begin
DataObject:=TDataObject.Create;
Effect := DROPEFFECT_NONE;
try
DoDragDrop(DataObject, Self, DROPEFFECT_MOVE, Effect);
except
end;
DataObject.Free;
end;
回复 点赞
chenybin 2007年01月31日
因为用户体验来说,比较合理的感觉是拖只有数据才下载,而不是拖之前就下载了
回复 点赞
SonicX 2007年01月31日
拖拽和接收都是自己开发的程序用内存流什么的肯定没问题,如果拖到桌面或别的程序貌似只能按照公共的操作方式来进行
回复 点赞
chenybin 2007年01月31日
看来一个简单的办法就是先建立一个很小的同名临时文件,鼠标放开以后在真正开始下载,并填充文件。

msdn的例子是间建立临时文件,不知道有没有机制可以在DoDragDrop只有调用我们的方法来处理数据获取。比如类型为TYPED_ISTREAM等等,一直没有好的资料和示例
回复 点赞
chenybin 2007年01月30日
http://community.csdn.net/Expert/topic/5319/5319586.xml?temp=6.657046E-02
回复 点赞
发动态
发帖子
Windows SDK/API
创建于2007-08-02

797

社区成员

2.2w+

社区内容

Delphi Windows SDK/API
社区公告
暂无公告