拖拽的一些文章摘录
来源:互联网 发布:精灵服务端外网端口 编辑:程序博客网 时间:2024/06/05 05:40
首先纠正大家一个概念性的错误
Delphi中的所有组件的Drag&Drop的相关操作都是伪造的,
Delphi在controls.pas单元中定义了一个自定义消息,如下:
...
const
CM_BASE = $B000;
...
<font color=red>CM_DRAG = CM_BASE + 47;</font>
...
在controls.pas单元中可以看到,Delphi在这里完整的模拟了一套Drag&Drop体系,
基本上大多数的Drag&Drop相关操作都在这里实现了,
少部分的如TreeView和ListView的在ComCtrls.pas中实现
整个体系简单地说分成两部分
1、判断鼠标的动作,并解释为等价的Drag&Drop消息
2、组件接收CMDrag消息,判断参数含义,作出相应的动作,触发相应的事件
其中1可以参见controls.pas中的TDragObject.MouseMsg方法
2则可以参见各控件的CMDrag方法,这个方法被定义为响应CM_Drag消息
到此可以基本得出结论:
Delphi中的Drag&Drop的相关操作都是伪造的,
因此如果我们使用Delphi自身提供的Drag&Drop的相关操作,
则只有Drag&Drop在同一个应用程序中发生,而且所有相关控件都是Delphi控件时才可能正确运行。
这里luket提出的问题是要从TWebBrowser中选择文本然后Drop到TreeView中,
但是这里TWebBrowser不是Delphi组件,Delphi只是引用了系统中注册的COM对象,并封装为Delphi类而已
因此TWebBrowser显然不会与Delphi组件那样运行,因而TreeView的全部Drag&Drop相关事件都根本不会发生
大家请注意看上面SuperMMX贴的那段代码,其实也在不断的计算鼠标位置、判断鼠标状态,
很明显也是在伪造Drag&Drop操作!
Delphi中的所有组件的Drag&Drop的相关操作都是伪造的,
Delphi在controls.pas单元中定义了一个自定义消息,如下:
...
const
CM_BASE = $B000;
...
<font color=red>CM_DRAG = CM_BASE + 47;</font>
...
在controls.pas单元中可以看到,Delphi在这里完整的模拟了一套Drag&Drop体系,
基本上大多数的Drag&Drop相关操作都在这里实现了,
少部分的如TreeView和ListView的在ComCtrls.pas中实现
整个体系简单地说分成两部分
1、判断鼠标的动作,并解释为等价的Drag&Drop消息
2、组件接收CMDrag消息,判断参数含义,作出相应的动作,触发相应的事件
其中1可以参见controls.pas中的TDragObject.MouseMsg方法
2则可以参见各控件的CMDrag方法,这个方法被定义为响应CM_Drag消息
到此可以基本得出结论:
Delphi中的Drag&Drop的相关操作都是伪造的,
因此如果我们使用Delphi自身提供的Drag&Drop的相关操作,
则只有Drag&Drop在同一个应用程序中发生,而且所有相关控件都是Delphi控件时才可能正确运行。
这里luket提出的问题是要从TWebBrowser中选择文本然后Drop到TreeView中,
但是这里TWebBrowser不是Delphi组件,Delphi只是引用了系统中注册的COM对象,并封装为Delphi类而已
因此TWebBrowser显然不会与Delphi组件那样运行,因而TreeView的全部Drag&Drop相关事件都根本不会发生
大家请注意看上面SuperMMX贴的那段代码,其实也在不断的计算鼠标位置、判断鼠标状态,
很明显也是在伪造Drag&Drop操作!
好啦,按照我的观点得到的结论必然是直接使用Delphi的相关事件是绝对行不通的,
只好用Windows标准的方法了,按照Win32 Help的说法,
要编程控制控件的Drag&Drop动作,必须按照以下步骤:
(a)对于Drag&Drop动作的Target控件: (对于本问就是TreeView)
1、实现一个IDropTarget接口
2、调用RegisterDragDrop将一个控件与一个IDropTarget接口实例关联起来
3、调用RevokeDragDrop取消2中的关联
(b)对于Drag&Drop动作的Source控件: (对于本问就是TWebBrowser)
1、实现一个IDropSource接口,控制Drag&Drop动作的Source控件
2、实现一个IDataObject接口,控制被Drag&Drop的数据
2、调用DoDragDrop方法,开始Drag&Drop动作
(其中RegisterDragDrop、RevokeDragDrop、DoDragDrop都是API,
Delphi中的接口定义在....../Source/Rtl/Win/Ole2.pas中)
在本问中TWebBrowser不需要我们负责,因此(b)不在讨论之列,我们主要讨论(a)
a.2调用RegisterDragDrop将一个控件与一个IDropTarget接口实例关联起来
程序如下:
type
TfmMain = class(TForm)
private
DropTarget: IDropTarget;
...
end;
procedure TfmMain.FormCreate(Sender: TObject);
begin
DropTarget := ITreeViewDropTarget.Create(TreeView);
RegisterDragDrop(TreeView.Handle, DropTarget);
end;
a.3、调用RevokeDragDrop取消2中的关联
程序如下:
procedure TfmMain.FormDestroy(Sender: TObject);
begin
RevokeDragDrop(TreeView.Handle);
DropTarget.Free;
end;
好了,上面的代码很简单,不用多说,再来重点看如何实现一个符合我们要求的IDropTarget:
a.1实现一个IDropTarget接口:
首先要作一些和本问不太相关的工作,实现IUnknow中的抽象方法(abstract method),如下:
type
ITreeViewDropTarget = class(IDropTarget)
private
FRefCount: LongInt;
protected
TreeView: TTreeView;
public
constructor Create(ATreeView: TTreeView);
function QueryInterface(const iid: TIID; var obj): HResult; override; stdcall;
function AddRef: Longint; override; stdcall;
function Release: Longint; override; stdcall;
...
property RefCount: LongInt read FRefCount;
end;
// 因为要和TreeView绑定
// 所以增加了一个TreeView,保存目标TreeView的引用
constructor ITreeViewDropTarget.Create(ATreeView: TTreeView);
begin
Inherited Create;
FRefCount := 0;
TreeView := ATreeView;
end;
function ITreeViewDropTarget.QueryInterface(const iid: TIID; var obj): HResult;
begin
IUnknown(Obj) := Nil;
Result := S_OK;
end;
function ITreeViewDropTarget.AddRef: Longint;
begin
Inc(FRefCount);
Result := RefCount;
end;
function ITreeViewDropTarget.Release: Longint;
begin
Dec(FRefCount);
Result := RefCount;
end;
各位,下面就是最重要的工作了,实现IDropTarget的四个方法:
如下:
type
ITreeViewDropTarget = class(IDropTarget)
public
...
function DragEnter(dataObj: IDataObject; grfKeyState: Longint;
pt: TPoint; var dwEffect: Longint): HResult; override; stdcall;
function DragOver(grfKeyState: Longint; pt: TPoint;
var dwEffect: Longint): HResult; override; stdcall;
function DragLeave: HResult; override; stdcall;
function Drop(dataObj: IDataObject; grfKeyState: Longint; pt: TPoint;
var dwEffect: Longint): HResult; override; stdcall;
...
end;
这四个方法的作用请大家去看Win32 Help,写得很详细,我就不翻译了,
实现的代码如下:(这里只是说明原理,因此一切从简)
function ITreeViewDropTarget.DragEnter(dataObj: IDataObject; grfKeyState: Longint;
pt: TPoint; var dwEffect: Longint): HResult;
begin
dwEffect := DropEffect_Copy;
Result := S_OK;
end;
function ITreeViewDropTarget.DragOver(grfKeyState: Longint; pt: TPoint;
var dwEffect: Longint): HResult;
begin
dwEffect := DropEffect_Copy;
Result := S_OK;
end;
function ITreeViewDropTarget.DragLeave: HResult;
begin
Result := S_OK;
end;
function ITreeViewDropTarget.Drop(dataObj: IDataObject; grfKeyState: Longint; pt: TPoint;
var dwEffect: Longint): HResult;
begin
dwEffect := DropEffect_Copy;
Result := S_OK;
end;
现在我们可以修改上面四个方法,详细地控制Drag&Drop操作,
我懒得作了,luket同志自己搞定吧
最后剩余的问题就是在ITreeViewDropTarget.Drop方法中应该对Drop过来的数据进行处理
数据全部在DataObj: IDataObject中,具体如何使用luket同志自己去看Win32 Help吧,
有很详细的说明,我没仔细看,想来应该不难
好啦,我就到此为止了,
各位有兴趣的同志继续研究一下,根据luket把上面四个方法写完吧!
只好用Windows标准的方法了,按照Win32 Help的说法,
要编程控制控件的Drag&Drop动作,必须按照以下步骤:
(a)对于Drag&Drop动作的Target控件: (对于本问就是TreeView)
1、实现一个IDropTarget接口
2、调用RegisterDragDrop将一个控件与一个IDropTarget接口实例关联起来
3、调用RevokeDragDrop取消2中的关联
(b)对于Drag&Drop动作的Source控件: (对于本问就是TWebBrowser)
1、实现一个IDropSource接口,控制Drag&Drop动作的Source控件
2、实现一个IDataObject接口,控制被Drag&Drop的数据
2、调用DoDragDrop方法,开始Drag&Drop动作
(其中RegisterDragDrop、RevokeDragDrop、DoDragDrop都是API,
Delphi中的接口定义在....../Source/Rtl/Win/Ole2.pas中)
在本问中TWebBrowser不需要我们负责,因此(b)不在讨论之列,我们主要讨论(a)
a.2调用RegisterDragDrop将一个控件与一个IDropTarget接口实例关联起来
程序如下:
type
TfmMain = class(TForm)
private
DropTarget: IDropTarget;
...
end;
procedure TfmMain.FormCreate(Sender: TObject);
begin
DropTarget := ITreeViewDropTarget.Create(TreeView);
RegisterDragDrop(TreeView.Handle, DropTarget);
end;
a.3、调用RevokeDragDrop取消2中的关联
程序如下:
procedure TfmMain.FormDestroy(Sender: TObject);
begin
RevokeDragDrop(TreeView.Handle);
DropTarget.Free;
end;
好了,上面的代码很简单,不用多说,再来重点看如何实现一个符合我们要求的IDropTarget:
a.1实现一个IDropTarget接口:
首先要作一些和本问不太相关的工作,实现IUnknow中的抽象方法(abstract method),如下:
type
ITreeViewDropTarget = class(IDropTarget)
private
FRefCount: LongInt;
protected
TreeView: TTreeView;
public
constructor Create(ATreeView: TTreeView);
function QueryInterface(const iid: TIID; var obj): HResult; override; stdcall;
function AddRef: Longint; override; stdcall;
function Release: Longint; override; stdcall;
...
property RefCount: LongInt read FRefCount;
end;
// 因为要和TreeView绑定
// 所以增加了一个TreeView,保存目标TreeView的引用
constructor ITreeViewDropTarget.Create(ATreeView: TTreeView);
begin
Inherited Create;
FRefCount := 0;
TreeView := ATreeView;
end;
function ITreeViewDropTarget.QueryInterface(const iid: TIID; var obj): HResult;
begin
IUnknown(Obj) := Nil;
Result := S_OK;
end;
function ITreeViewDropTarget.AddRef: Longint;
begin
Inc(FRefCount);
Result := RefCount;
end;
function ITreeViewDropTarget.Release: Longint;
begin
Dec(FRefCount);
Result := RefCount;
end;
各位,下面就是最重要的工作了,实现IDropTarget的四个方法:
如下:
type
ITreeViewDropTarget = class(IDropTarget)
public
...
function DragEnter(dataObj: IDataObject; grfKeyState: Longint;
pt: TPoint; var dwEffect: Longint): HResult; override; stdcall;
function DragOver(grfKeyState: Longint; pt: TPoint;
var dwEffect: Longint): HResult; override; stdcall;
function DragLeave: HResult; override; stdcall;
function Drop(dataObj: IDataObject; grfKeyState: Longint; pt: TPoint;
var dwEffect: Longint): HResult; override; stdcall;
...
end;
这四个方法的作用请大家去看Win32 Help,写得很详细,我就不翻译了,
实现的代码如下:(这里只是说明原理,因此一切从简)
function ITreeViewDropTarget.DragEnter(dataObj: IDataObject; grfKeyState: Longint;
pt: TPoint; var dwEffect: Longint): HResult;
begin
dwEffect := DropEffect_Copy;
Result := S_OK;
end;
function ITreeViewDropTarget.DragOver(grfKeyState: Longint; pt: TPoint;
var dwEffect: Longint): HResult;
begin
dwEffect := DropEffect_Copy;
Result := S_OK;
end;
function ITreeViewDropTarget.DragLeave: HResult;
begin
Result := S_OK;
end;
function ITreeViewDropTarget.Drop(dataObj: IDataObject; grfKeyState: Longint; pt: TPoint;
var dwEffect: Longint): HResult;
begin
dwEffect := DropEffect_Copy;
Result := S_OK;
end;
现在我们可以修改上面四个方法,详细地控制Drag&Drop操作,
我懒得作了,luket同志自己搞定吧
最后剩余的问题就是在ITreeViewDropTarget.Drop方法中应该对Drop过来的数据进行处理
数据全部在DataObj: IDataObject中,具体如何使用luket同志自己去看Win32 Help吧,
有很详细的说明,我没仔细看,想来应该不难
好啦,我就到此为止了,
各位有兴趣的同志继续研究一下,根据luket把上面四个方法写完吧!
这是 DFW 上最早有关 Drag&Drop 的完整描述,也是我第一次看到 李颖 这个名字。今天
是 2003 年的 5 月,DFW 新建了一个 Keylife 的功能,几天前,我看到论坛上有关这个
Drag&Drop 的问题还有很多人在问,有趣的是,回答几乎都是:“用 Drag&Drop 控件包”!
于是想在自己的 Keylife 中写一篇这样的主题。写前,总想先看看 DFW 上有关这个主题
的提问和回答的质量,于是我就查到了这里(也有幸看到了李颖和他的帖子)。看完后,基
本已经打消了写这个主题的念头,DFW 什么都有,有控件包用,也不是什么坏事,想深入
了解的,查就是了,更何况最早的帖子是在 1999 年。
做为补充,我在这里写一点直接使用这个 IDropTarget 的代码,原理李颖已经说完了。
1.直接在类声明中声明(其实是继承) IDropTarget 接口,不需要使用代理。
type
TForm1 = class(TForm,IDropTarget) // Delphi 中,类不支持多重继承,但接口可以
private
// 实现 IDropTarget 接口需要实现的全部方法
// 标准的做法:使用别名,以免和 Delphi 自己的控件同名方法重名。
// 显然,不怕编译警告,可以忽略下面四个别名声明。
function IDropTarget.DragEnter=ADragEnter;
function IDropTarget.DragOver=ADragOver;
function IDropTarget.DragLeave=ADragLeave;
function IDropTarget.Drop=ADrop;
function ADragEnter(const dataObj:IDataObject; grfKeyState: Longint;
pt: TPoint; var dwEffect: Longint): HResult; stdcall;
function ADragOver(grfKeyState: Longint; pt: TPoint;
var dwEffect: Longint): HResult; stdcall;
function ADragLeave: HResult; stdcall;
function ADrop(const dataObj: IDataObject; grfKeyState: Longint;
pt: TPoint; var dwEffect: Longint): HResult; stdcall;
end;
2.在创建过程中绑定拖拉。
procedure TForm1.FormCreate(Sender: TObject);
begin
OleInitialize(nil); // 在调用前初始化 Ole 库
if RegisterDragDrop(Handle,Self) <> S_OK then // 注册成功了?
ShowMessage('拖放注册失败');
end;
3.在析构中解除绑定。
procedure TForm1.FormDestroy(Sender: TObject);
begin
RevokeDragDrop(Handle); // 终止拖拉授权
OleUninitialize;
end;
4.在 IDropTarget 接口的 Drop 方法中执行处理。
function TForm1.ADrop(const dataObj: IDataObject; grfKeyState: Integer;
pt: TPoint; var dwEffect: Integer): HResult;
var
EnumFormat:IEnumFormatEtc;
FormatEtc: TFormatEtc;
begin
DataObj.EnumFormatEtc(DATADIR_GET,EnumFormat); // 取得 IEnumFORMATETC 接口
while (EnumFormat.Next(1,FormatEtc, nil) <> S_FALSE) do // 枚举开始
begin
case FormatEtc.cfFormat of
CF_TEXT:GetText(DataObj,FormatEtc); // GetText(...) 是一个自己的处理函数
end;
end;
Result := S_OK;
end;
5.在 IDropTarget 接口的 DragOver 方法中定义光标样式。
function TForm1.ADragOver(grfKeyState: Integer; pt: TPoint;
var dwEffect: Integer): HResult;
begin
dwEffect := DROPEFFECT_COPY;
Result := S_OK;
end;
6.剩下的 2 个接口方法,可以直接返回 S_OK 。
7.一个处理函数的样式 —— GetText(...) 。
procedure TForm1.GetText(DataObj: IDataObject; FormatEtc: TFormatEtc);
var
p: pointer;
StgMed:TStgMedium;
begin
if (DataObj.QueryGetData(FormatEtc) = NOERROR) then
begin
DataObj.GetData(FormatEtc,StgMed); // 获取 stgMEDIUM 结构
p := GlobalLock(StgMed.hGlobal); // 获取内存首地址指针
ListBox1.Items.Add(string(p)); // 文本装入一个 ListBox 控件
GlobalFree(StgMed.hGlobal);
ReleaseStgMedium(StgMed);
end;
end;
以上这些希望能够作为补充。为希望真正了解“拖拉”实质而查找到这里的富翁提供一点帮助。
是 2003 年的 5 月,DFW 新建了一个 Keylife 的功能,几天前,我看到论坛上有关这个
Drag&Drop 的问题还有很多人在问,有趣的是,回答几乎都是:“用 Drag&Drop 控件包”!
于是想在自己的 Keylife 中写一篇这样的主题。写前,总想先看看 DFW 上有关这个主题
的提问和回答的质量,于是我就查到了这里(也有幸看到了李颖和他的帖子)。看完后,基
本已经打消了写这个主题的念头,DFW 什么都有,有控件包用,也不是什么坏事,想深入
了解的,查就是了,更何况最早的帖子是在 1999 年。
做为补充,我在这里写一点直接使用这个 IDropTarget 的代码,原理李颖已经说完了。
1.直接在类声明中声明(其实是继承) IDropTarget 接口,不需要使用代理。
type
TForm1 = class(TForm,IDropTarget) // Delphi 中,类不支持多重继承,但接口可以
private
// 实现 IDropTarget 接口需要实现的全部方法
// 标准的做法:使用别名,以免和 Delphi 自己的控件同名方法重名。
// 显然,不怕编译警告,可以忽略下面四个别名声明。
function IDropTarget.DragEnter=ADragEnter;
function IDropTarget.DragOver=ADragOver;
function IDropTarget.DragLeave=ADragLeave;
function IDropTarget.Drop=ADrop;
function ADragEnter(const dataObj:IDataObject; grfKeyState: Longint;
pt: TPoint; var dwEffect: Longint): HResult; stdcall;
function ADragOver(grfKeyState: Longint; pt: TPoint;
var dwEffect: Longint): HResult; stdcall;
function ADragLeave: HResult; stdcall;
function ADrop(const dataObj: IDataObject; grfKeyState: Longint;
pt: TPoint; var dwEffect: Longint): HResult; stdcall;
end;
2.在创建过程中绑定拖拉。
procedure TForm1.FormCreate(Sender: TObject);
begin
OleInitialize(nil); // 在调用前初始化 Ole 库
if RegisterDragDrop(Handle,Self) <> S_OK then // 注册成功了?
ShowMessage('拖放注册失败');
end;
3.在析构中解除绑定。
procedure TForm1.FormDestroy(Sender: TObject);
begin
RevokeDragDrop(Handle); // 终止拖拉授权
OleUninitialize;
end;
4.在 IDropTarget 接口的 Drop 方法中执行处理。
function TForm1.ADrop(const dataObj: IDataObject; grfKeyState: Integer;
pt: TPoint; var dwEffect: Integer): HResult;
var
EnumFormat:IEnumFormatEtc;
FormatEtc: TFormatEtc;
begin
DataObj.EnumFormatEtc(DATADIR_GET,EnumFormat); // 取得 IEnumFORMATETC 接口
while (EnumFormat.Next(1,FormatEtc, nil) <> S_FALSE) do // 枚举开始
begin
case FormatEtc.cfFormat of
CF_TEXT:GetText(DataObj,FormatEtc); // GetText(...) 是一个自己的处理函数
end;
end;
Result := S_OK;
end;
5.在 IDropTarget 接口的 DragOver 方法中定义光标样式。
function TForm1.ADragOver(grfKeyState: Integer; pt: TPoint;
var dwEffect: Integer): HResult;
begin
dwEffect := DROPEFFECT_COPY;
Result := S_OK;
end;
6.剩下的 2 个接口方法,可以直接返回 S_OK 。
7.一个处理函数的样式 —— GetText(...) 。
procedure TForm1.GetText(DataObj: IDataObject; FormatEtc: TFormatEtc);
var
p: pointer;
StgMed:TStgMedium;
begin
if (DataObj.QueryGetData(FormatEtc) = NOERROR) then
begin
DataObj.GetData(FormatEtc,StgMed); // 获取 stgMEDIUM 结构
p := GlobalLock(StgMed.hGlobal); // 获取内存首地址指针
ListBox1.Items.Add(string(p)); // 文本装入一个 ListBox 控件
GlobalFree(StgMed.hGlobal);
ReleaseStgMedium(StgMed);
end;
end;
以上这些希望能够作为补充。为希望真正了解“拖拉”实质而查找到这里的富翁提供一点帮助。
- 拖拽的一些文章摘录
- 一些文章摘录
- vagrxie的文章(摘录)
- 摘录的一些算法
- 关于程序员的文章摘录
- 摘录ibatis的一些优点
- 关于DLL的一些摘录
- 一些vim知识的摘录
- 摘录的一些 C++ 代码
- 摘录ibatis的一些优点
- 学习scanf的一些摘录
- 摘录的一些调度算法
- mount的一些理解和一些摘录
- 几篇关于AdSense的文章摘录
- yingkou的Blog 精华文章摘录
- 搜到几篇关于模式识别的文章,做摘录
- 一些摘录
- .Net的一些术语(学习摘录)
- webservice安全-两种方式
- 【散分】公布一些常用的WebServices,希望对大家的应用有帮助
- 便携设备:小终端激起大变局
- 关于Erlang
- 通用USB设备驱动源码分析
- 拖拽的一些文章摘录
- Sqlserver 在查询分析器访问远程的数据库,进行数据查询更新
- 讲解SQL Server危险扩展存储删除和恢复
- operator操作符
- Zoomla!逐浪CMS2.0精彩预报之SNS功能抢先看
- LPCTSTR,LPTSTR,CSTRING,CHAR *等等的区别
- 浅谈MDIChild的showmodal问题
- split命令使用
- treeview控件的几种用法