如何将程序中的内容“拖”到桌面或文件夹中(DoDragDrop高难度高分问题) 实现拖出,现在程序的数据源是Listview,具体的内容根据Listview选中的文件从ftp下载(这个无所谓,反正从其他地方来的,数据库的stream等等都可以,不同于普通文本拖放),希望拖放完毕,鼠标放开以后开始下载ftp的内容,并保存到本地,(从程序窗口中的listview拖到桌面等地方)不想用控件,主要想知道原理,如果哪位兄弟帮我把代码从控件中分离出来夜行,非常感谢 解决方案 » 免费领取超大流量手机卡,每月29元包185G流量+100分钟通话, 中国电信官方发货 http://community.csdn.net/Expert/topic/5319/5319586.xml?temp=6.657046E-02 拖放要放在OLE对象里涉及到的APIDoDragDropDoDragDrop需要用到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;varBufferText : 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;//////////////在需要拖拽的时候varDataObject : TDataObject;beginDataObject:=TDataObject.Create;Effect := DROPEFFECT_NONE;try DoDragDrop(DataObject, Self, DROPEFFECT_MOVE, Effect);exceptend; DataObject.Free;end; 事实上你拖拽的时候是先已经选定好了的,比如从A拖到B里面,B里面显示的只是A里面选中的东西(桌面也是一个文件夹),而实际上是开了线程开始下载(处理)的 谢谢楼上两位兄弟,dabaicai(小老头) 思路可以考虑,不知道算不算“正统”的做法谢谢to SonicX(SonicX),现在的问题是文件其实并不存在本地楼下继续 根据MSDN说法就是先将文件存到硬盘上再进行拖拽,拖拽文件没有内存操作方式你可以看下其他软件的拖拽,都是将文件先存到TEMP目录的,比如WINRAR,FOXMAIL... 是的,不过会有个小问题,就是如果网络不是很好,我们在DoDragDrop前准备数据这个过程会很漫长,这样不太好 转变下思路吧在GetData过程做点改动,比如弄个文件进度条什么的,后台下载数据咯 看来一个简单的办法就是先建立一个很小的同名临时文件,鼠标放开以后在真正开始下载,并填充文件。msdn的例子是间建立临时文件,不知道有没有机制可以在DoDragDrop只有调用我们的方法来处理数据获取。比如类型为TYPED_ISTREAM等等,一直没有好的资料和示例 再探完全释放指针内存的问题! 怎么判断SQL数据库的安装程序已经安装完成 如何 dbgrid 输入时不要自动排序 调用DLL窗体后的问题? mdb数据库建立一对多关系后,如何保证数据完整性和一致性? 请问如何使RichEdit的底背景透明?(紧急求救) 程序员们,你们都是什么学历啊! 请高手指点!! 两个问题??? 我要用OpenDialog打开一个对话框,只让用户选择路径,不选择具体文件,该怎么办? 为什么我写的delphi程序,运行后关闭了,但是进程里面还有啊!! DELPHI怎样获得计算机名称?
DoDragDropDoDragDrop需要用到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;
你可以看下其他软件的拖拽,都是将文件先存到TEMP目录的,比如WINRAR,FOXMAIL...
在GetData过程做点改动,比如弄个文件进度条什么的,后台下载数据咯