Windows中Drag&Drop初探(一)

时间:2022-03-10 00:07:53

一、基本概念

    拖放,是指用鼠标拖动的方法,在不同程序的窗口之间、同一个程序的不同窗口之间或同一程序同一窗口的不同控件之间,进行移动、复制和粘贴等操作的技术。拖放操作是在操作系统的帮助下完成的。被拖动的对象首先向操作系统注册它使用的数据格式,并按指定的数据格式提供数据,拖放操作结束时,接收拖放的窗口按指定的数据格式提取有关数据,并根据提取的数据生成相应的对象。

 二、两种拖放方式

    拖放有两种类型:OLE拖放和文件管理器拖放。这两种方式是完全不同的机制。文件管理器拖放只能处理文件名,通过映射目的窗口的WM_DROPFILES消息,窗口就可以收到拖放进来的文件名。OLE拖放则更加通用一些,它允许你拖放可同时被保存在剪贴板上的任何数据。本文首先介绍文件管理器拖放,然后再介绍OLE拖放,最后给出一个用OLE实现的,支持文件拖放操作的增强列表控件CListCtrlEx

 三、文件管理器拖放原理及实例

    这种方式的实质就是产生一个消息WM_DROPFILES。技术上没有什么难点,主要用到下面几个API函数:DragQueryFileDragQueryPointDragFinish。它们的原型和注解分别如下:

        UINT DragQueryFile(HDROP hDrop, UINT iFile, LPTSTR lpszFile, UINT cch)

    本函数用来取得拖放的文件名。其中,hDrop是一个指向含有被拖放的文件名的结构体的句柄;iFiles是要查询的文件序号,因为一次可能同时拖动很多个文件;lpszFiles是出口缓冲区指针,保存iFiles指定序号的文件的路径名,cch指定该缓冲区的大小。有两点值得注意,第一,如果我们在调用该函数的时候,指定iFile0xFFFFFFFF,则DragQueryFile将忽略lpszFilecch参数,返回本次拖放操作的文件数目;第二,如果指定lpszFileNULL,则函数将返回实际所需的缓冲区长度。

         BOOL DragQueryPoint(HDROP hDrop, LPPOINT lppt);

  本函数用来获取,当拖放操作正在进行时,鼠标指针的位置。第二个参数lppt是一个指向POINT结构体的指针,用来保存文件放下时,鼠标指针的位置。窗口可以调用该函数以查询文件是否落在自己的窗口矩形中。

void DragFinish(HDROP hDrop);

当拖放操作处理完毕后需调用该函数释放系统分配来传输文件名的内存。

 

首先,建立一个对话框工程,确保选中对话框的Accept Files属性。如果不选,也可以在窗口创建的时候(譬如OnCreate函数中)调用DragAcceptFiles(TRUE),效果是一样的。

然后映射WM_DROPFILES消息。该消息处理函数原型如下:void OnDropFiles(HDROP hDrop),注意入口参数为HDROP型,它是一个结构体指针,所指向的结构体中包含了被拖放的文件的名称。接下来我们主要要完成两个动作:第一,通过调用DragQueryFile并指定其iFile参数为0xFFFFFFFF,得到本次拖放操作的文件数目;第二步,用一个循环依次取出各个文件名。示例如下:

void CListCtrlEx::OnDropFiles(HDROP hDrop)

{

        char   szFilePathName[_MAX_PATH+1] = {0};

UINT  nNumOfFiles = DragQueryFile(hDrop, 0xFFFFFFFF, NULL, 0); //得到文件个数

        for (UINT nIndex=0 ; nIndex< nFileCount; ++nIndex)

        {

            DragQueryFile(hDrop, nIndex, szFilePathName, _MAX_PATH);  //得到文件名

        }

        DragFinish(hDrop);

}

四、OLE拖放原理

MFC为实现对象拖放提供了如下类:COleDataSourceCOleDataObjectCOleDropTargetCOleDropSource。下面分别介绍它们,然后通过一个实例讲述实现的具体步骤。

·  COleDataSource

启动一次拖放操作,保存拖放的数据,并向系统提供拖放对象的数据。类中重要的成员函数根据用途分为如下三种:

1 用于设定提供数据的方式和使用的数据格式。

提供数据的方式有两种,一种是即时方式,另一种是延迟方式。延迟方式不需要立即提供数据,当需要提供数据时,系统将调用对应的函数来获得数据,一般都是重载OnRenderData函数或其他虚函数,以响应数据请求。数据格式可以是CF_TEXT等常用的剪贴板格式,也可以是自己利用函数RegisterClipboardFormat函数注册的特定格式。

CacheData:提供指定格式的数据,格式由结构STGMEDIUM指定,即时方式;

CacheGlobalData:利用全局句柄HGLOBAL,为指定格式提供数据,即时方式,适用小数据量;

DelayRenderData:使用延迟方式按指定格式提供数据,当系统需要数据时,会调用函数OnRenderGlobalData/OnRenderData来取得数据;

DelayRenderFileData:使用延迟方式利用CFile为指定格式提供数据,当需要数据时,会调用函数OnRenderFileData来取得数据;

2 响应请求,提供数据

OnRenderFileData:为延迟方式提供CFile型数据。

OnRenderGlobalData:为延迟方式提供HGLOBAL数据。

OnRenerData:为延迟方式提供各种所支持的类型的数据。

3 实施拖放操作

DoDragDrop:开始实施拖放操作

 

·  COleDataObject

用于代表拖放的数据,它是作为COleDataSource类的成员,类中主要成员函数有:

BeginEnumFormat:为枚举数据格式作准备;

GetNextFormat:返回下一个数据格式;

IsDataAvailable 检查指定的数据格式是否可用;

GetData:按指定数据格式,获得数据;

GetFileData:按指定数据格式,获得CFile型数据;

GetGlobalData:按指定数据格式,获得HGLOBAL型数据;

·  COleDropTarget

用于在窗口和OLE库之间提供通讯机制。任何一个窗口,要想能够接收拖放,必须包含一个COleDropTarget对象,并注册之。其中的成员函数可分为两大类:

1 注册

Register:注册该对象,以便使窗口能够接收拖放对象

2 响应拖放过程中的动作(虚函数)

OnDragEnter:当鼠标首次进入窗口时被调用;

OnDragLeave:当鼠标移出窗口时被调用;

OnDragOver:当鼠标停留在窗口内时,被重复调用;

OnDrop 当鼠标在窗口内落下被调用;

虚函数onDragEnterOnDragOver的返回值具有重要的含义,一般为以下三种之一:

DROPEFFECT_MOVE:移动操作,允许对象落在此窗口,落下时要删除原来的对象;

DROPEFFECT_COPY:复制操作,允许对象落在此窗口,落下时不删除原来的对象;

DROPFFECT_NONE:不允许对象落在此窗口;

 

·  COleDropSource

COleDropSource允许数据被拖放到一个拖放目标,它负责对何时启动一个拖放操作进行决断,反馈拖放操作状态,以及判断拖放操作何时结束。这个类比较简单,用得也较少。它的成员函数只有三个:

GiveFeedback:用于改变拖放期间鼠标的光标,把拖放状态反馈给用户知晓;

OnBeginDrag:在拖放期间捕捉鼠标指针,当应用程序框架觉得可能要发生一个拖放操作时,它会调用该函数;

QueryContinueDrag:检测拖放操作是否还在继续中。

 

                                                    to be continued...