当前位置: BOLT界面引擎 > 知识库文章 > 引擎里的拖放指南

引擎里的拖放指南

作者:李亚星 2013-01-06

在使用BOLT开发中,一般来说会使用到下面三种形式的拖放:

l  单个窗口内部的拖放,也就是从窗口的一个部位拖到另一个部位

l  从其它窗口拖放到本窗口内,比如从资源管理器里面拖一个文件到本窗口来打开

l  从本窗口拖一个东西到其它窗口,比如从窗口内拖放一个文件到桌面上

 

 

对于第一种拖放如果只是需要这种拖放,那么dragdrop目标都在同一个对象树,就很简单,可以按照以下步骤:

1.         鼠标开始拖动时候,创建一个zorder高于该对象树上所有对象的对象,作为拖动对象

2.         鼠标在拖动过程中,拖动对象跟随鼠标移动(必要时候需要SetCaptureMouse)

3.         鼠标在拖动过程中,拖动对象响应OnMouseMove消息,并把鼠标坐标点转换到对象树坐标系,然后调用对象树的HitTest进行命中测试,看是否落到了可drop对象上

4.         如果取到了可drop对象,那么终止拖放,隐藏拖动对象,并处理真正的drop操作

 

对于第二种拖放如果不考虑支持第三种拖放,那么可以使用windows的文件管理器拖放,步骤如下:

1.         对可接受drophostwnd,调用GetWndHandle拿到窗口的系统句柄HWND,调用DragAcceptFiles或者SetWindowLongPtr+WS_EX_ACCEPTFILES属性,注册成可接受drop

2.         挂接hostwndInputFilter消息过滤器(AddInputFilter/RemoveInputFilter),在里面监听WM_DROPFILES (0x0233)消息

3.         收到WM_DROPFILES拖放消息后,使用DragQueryFile/DragQueryPoint/DragFinish进行需要的操作

4.         如果在3步骤里面,还需要对象粒度的命中测试,那么使用DragQueryPoint得到的坐标点,转到到对象树坐标点(HostWndPtToTreePt),然后调用所在hostwnd上对象树的HitTest进行对象命中测试,看是否落到了可drop对象上

5.         如果hostwnd可以处理此拖放,或者有合适的对象可处理,那么处理;否则忽略此次WM_DROPFILES消息

 

 

对于第三种拖放,或者三种拖放都需要支持,那么需要使用引擎核心支持的拖放机制(win32平台下是依赖于系统的OLE拖放机制),该拖放功能强大,但是使用起来也较为复杂,需要对win32下的OLE拖放有所了解

 

注意:如果有任何一个hostwnd需要使用该drop/drag机制,都需要在引擎的工作线程(一般都是主UI线程)开始的地方,调用OleInitialize

 

如果一个hostwnd上某个对象要作为drag(也就是可以拖),那么需要按照下面步骤

1.         准备好数据源IDataObject,该数据源里面有你准备用来drag的数据

2.         调用hostwndDoDragDrop开始拖放,注意在本次拖放终止前,该函数都不会返回

3.         在拖放过程中,hostwnd会反复触发OnQueryContinueDragOnGiveFeedback两个事件,其中

a)         OnGiveFeedback 来决定当前点的拖放效果,如果没有响应该函数,或者bhandledfalse,或者返回值是DRAGDROP_S_USEDEFAULTCURSORS,那么鼠标形状由当前drag到的目标来决定;否则使用返回值指定的effect,包括

DROPEFFECT_NONE( 0 )

DROPEFFECT_COPY         ( 1 )

DROPEFFECT_MOVE( 2 )

DROPEFFECT_LINK  ( 4 )

详细内容可参考MSDN

 

b)         OnQueryContinueDrag 来决定是否继续拖放,如果该事件被响应,并且handledtrue,那么根据返回值来决定是否继续进行拖放:

l  返回0表示继续

l  返回DRAGDROP_S_DROP表示终止拖放,进行drop操作

l  返回DRAGDROP_S_CANCEL或者其它值表示终止拖放,不做drop操作

 

否则按照默认处理,也即通过下面条件判定是否继续拖放:

l  esc键被按下

l  拖动开始键弹起(如果开始拖放时是左键按下,那么就记录左键是否弹起),那么进行drop操作

l  拖动开始的对称键弹起(如果开始拖放时是左键按下,那么就记录右键是否弹起),那么取消当前拖放

l  如果上面几点都不符合,那么继续拖放操作

4.         在拖动开始和结束时候,所在hostwnd分别会触发OnBeginDragOnEndDrag事件,可以在里面做一些初始化和收尾工作

5.         OnBeginDrag里面,可以调用hostwnd的以下接口,来设置拖动时候的显示图片:

l  SetDragImageFromWindow

把一个hostwnd渲染成一张位图,并作为拖动时候的图片

l  SetDragImageFromBitmap

直接指定一张xlbitmap,作为拖动时候的图片

l  SetDragImageFromObject

把一个obj和它的子对象渲染成一张位图,并作为拖动时候的图片

 

如果一个hostwnd上有一个或者几个对象可以作为drop目标(接受拖放),那么需要按照下面步骤:

1.         所在的hostwnd,配置属性<enabledrop>1</enabledrop>,或者动态调用SetDropEnable(true)来开启,否则窗口上任何对象无法作为拖放目标

2.         对象树上任何一个obj(仅限非实窗体对象),如果想作为drop目标,那么需要配置属性: <enabledrop>1</enabledrop>,或者动态调用SetDropEnable(true)来开启,否则该对象无法作为拖放目标

3.         当用户拖放操作到所在hostwnd上面时候,会根据下面规则进行obj命中测试:

a)         按照zorder由高到低,依次命中测试,zorder越高优先级越高

b)         如果一个obj不同时满足visible=trueenable=trueenabledrop=true三个条件,那么该obj不可作为drop目标

c)         如果满足以上条件,那么调用该objOnDragQuery事件,如果没有响应该事件,或者bhandled返回值为false,那么认为该obj可接受拖放,并且默认effectcopy;否则根据默认返回值result,来决定当前的drop类型:

DROPEFFECT_NONE( 0 )

DROPEFFECT_COPY( 1 )

DROPEFFECT_MOVE( 2 )

DROPEFFECT_LINK  ( 4 )

其中none表示不接受drop,其它值表示当前接受的drop类型,这个一般情况下会影响到拖放操作的鼠标类型等,视drag源而定

4.         如果一个obj满足3里面的所有条件,那么该obj便作为当前的drop目标。在该obj有效区域上拖动期间,会依次触发下面四个事件:

a)         OnDragEnter

b)         OnDragOver

c)         OnDragLeave

d)         OnDrop

 

在刚拖入该obj时候,会触发OnDragEnter事件,在离开时候会触发OnDragLeave事件,在期间移动的时候会连续的触发OnDragOver事件,如果在该obj上面拖动时用户发出了drop操作,那么会触发OnDrop

事件

 

四个事件原型如下

long OnDragEnter(self, IDataObject* dataObj, int keyState, int x, int y )

long OnDragOver(self, IDataObject* dataObj, int keyState, int x, int y )

long OnDragLeave(self)

long OnDrop(self, IDataObject* dataObj, int keyState, int x, int y )

        

其中dataObj就是OLE拖放里面的IDataObject*,该数据需要使用者自己来解析,看看里面有没有自己期望的格式或者数据内容

keyState就是当前的按键状态,和MSDN里面的定义是一致的

xy是当前拖到的位置,相对于每个object的左上角的

 

在使用引擎内置的拖放机制时候,需要注意下面几点:

n  引擎所在线程需要调用OleInitialize进行初始化操作

n  drag/drop所在hostwnd,不得在外部自己调用RegisterDragDrop来注册自己的IDropTarget接口,否则会导致引擎内置拖放机制失效

n  要注意拖放时候对象树上的对象命中规则,不可见和disableobj是不可被命中的, objtreeHitTest规则有所不同

n  拖放过程中的IDataObject是核心,但是对引擎本身是透明的,需要使用者在自己的C/C++代码里面构造合适的IDataObject结构,也需要对这块技术有所了解

n  对于vista以及以后的windows操作系统,如果系统开启了UAC,那么需要注意拖放时候的进程权限限制

 

 

迅雷公司 版权所有 Copyright 2003-2010 Thunder Inc.All Rights Reserved. 意见反馈:xl7doc@xunlei.com