扩展COleDropTarget类来支持任意窗口拖放

        本文详细论述了拖放的基本原理并提供一个支持窗口拖放的普遍类COleDropTargetEx,任何需要提供拖放的窗口包含此类后,将响应拖放消息函数加入,便可接收拖放。此类利用Windows消息来支持拖放,机制不同于COleDropTarget类与CView类那种直接的搭配关系,因此较以往的拖放方法简便、灵活,代码重用性非常好。

这是本文示例代码的运行效果图

 

一、拖放原理及MFC类库对拖放的支持

        拖放(Drag and Drop)是OLE的一部分,是指对某一指定的对象,利用鼠标拖动的方法,在不同应用的窗口之间、同一应用的不同窗口之间或同一应用的同一窗口内进行移动、复制(粘贴)等操作的技术。
MFC为实现对象拖放提供了如下类:COleDataSource、COleDropSource、COleDropTarget、COleDataObject。对于上述几个类的用法,读者可参考有关资料。因拖放操作中的启动拖放部分,实现较模式化,很多文献都有详细的实现,本文在此不再赘述。这里着重说明MFC拖放操作中接收部分的实现原理,以使读者了解为何要扩展MFC拖放类的功能。
        MFC通过提供COleDropTarget和CView类来支持拖放操作的接收。在CView及其继承类中创建COleDropTarget类对象,并在视图窗口初始化时,调用其成员函数Register(),以此在系统中注册该视图窗口为拖放接收窗口。当进行拖放操作的鼠标指针处于视图窗口范围内时,COleDropTarge类的OnDragEnter、OnDragOver、OnDropEx、OnDrop等成员函数被依次调用,这些函数默认调用与其相对应的CView类成员函数OnDragEnter、OnDragOver、OnDropEx、OnDrop等,在这些CView类成员函数中,用户可对拖动的过程及结果进行控制。但MFC这种内在的对拖放的实现是很不够的,一个用户界面友好的应用程序,很多时候要求不仅视图窗口支持拖放,而且对话框以及编辑框等控制窗口也需要拖放的支持。虽然现在已经有一些拖放目标类的扩展类了,但通常都是需要按照CView类对拖放的实现模式来实现这些窗口类,再在COleDropTarge继承类的响应拖放函数中利用RTTI(Run-time type information)来调用这些特定的窗口类拖放响应函数。而利用RTTI机制不可避免地会使拖放类只支持特定的窗口类,如果要增加新的可支持拖放的窗口类,则必须改写已实现的COleDropTarge继承类,这种情况是我们所不愿看到的。如果直接在COleDropTarge继承类中处理拖放呢?显然,只会造就一个针对性更强的类。针对这种情况,需要运用其它方式来支持无类型要求的窗口类拖放操作。

二、拖放扩展类运行原理

        我们知道,使用消息是可以传递信息的。对于复杂的结构,可以通过在消息参数中传递结构的指针来传递结构信息。因此,在COleDropTarge扩展类响应拖放成员函数中,将拖放信息打包,存入拖放信息类中,向注册了拖放功能的控制窗口发送用户自定义消息,并传递拖放信息类地址。在窗口类中,映射该自定义消息到消息响应函数中,在该函数中,将消息参数转换回拖放信息类指针,以此来获得拖放信息,进行相应操作后,返回对拖放操作的控制信息。这样,经过将MFC对拖放操作的函数调用转化为类型无关的对窗口的消息发送,扩展的拖放目标类便具有了与拖放窗口类无关的特性,而且这种消息机制符合程序编写习惯。

三、实例实现

        下面我们通过一个程序实例来说明该扩展类的使用。在这个实例中演示几个通过嵌入COleDropTargetEx类方便的获得了拖放能力的控件,这里只介绍支持拖放的编辑控件的编程过程,其它控件过程类似。
利用AppWizard新建一工程"DropExDemo",选择基于对话框的应用,其它可取默认值。
将本文附录中拖放目标扩展类COleDropTargetEx的头文件与实现文件添加到工程中。
为了实现支持拖动的编辑框控制,从CEdit继承一新类CDropEdit。
添加如下成员变量与成员函数到CDropEdit类声明中并在文件首包含COleDropTargetEx类的头文件。

 

  1. public:
  2. virtual BOOL Register();
  3.   // Generated message map functions
  4. protected:
  5.         COleDropTargetEx m_dropEx;virtual BOOL OnDrop(WPARAM pDropInfoClass, LPARAM lParm);
  6.         virtual DROPEFFECT OnDropEx(WPARAM pDropInfoClass, LPARAM lParm);
  7.         virtual DROPEFFECT OnDragOver(WPARAM pDropInfoClass,LPARAM lParm);在实现文件中添加消息映射如下:
  8.         BEGIN_MESSAGE_MAP(CDropEdit, CEdit)
  9.         //{{AFX_MSG_MAP(CDropEdit)
  10.         // NOTE - the ClassWizard will add and remove mapping macros here.
  11.         //}}AFX_MSG_MAP
  12.         ON_MESSAGE(DROPM_DRAGOVER,OnDragOver)
  13.         ON_MESSAGE(DROPM_DROPEX,OnDropEx)
  14.         ON_MESSAGE(DROPM_DROP,OnDrop)
  15.         END_MESSAGE_MAP()

在实现文件中消息响应函数定义如下:

    

  1. /
  2. // CDropEdit message handlers
  3. BOOL CDropEdit::Register()
  4. {
  5.     return m_dropEx.Register( this );
  6. }
  7. DROPEFFECT CDropEdit::OnDragOver(WPARAM pDropInfoClass, LPARAM lParm)
  8. {
  9.         COleDropInfo* pInfo = (COleDropInfo* )pDropInfoClass;
  10.         ASSERT(pInfo->IsKindOf(RUNTIME_CLASS(COleDropInfo)));
  11.         if( pInfo->pDataObject->IsDataAvailable( CF_TEXT ) )
  12.             return DROPEFFECT_COPY;
  13.         else
  14.             return DROPEFFECT_NONE;
  15. }
  16. DROPEFFECT CDropEdit::OnDropEx(WPARAM pDropInfoClass, LPARAM lParm)
  17. {
  18.         return (DROPEFFECT)-1;
  19. }
  20. BOOL CDropEdit::OnDrop(WPARAM pDropInfoClass, LPARAM lParm)
  21. {
  22.         COleDropInfo* pInfo = (COleDropInfo* )pDropInfoClass;
  23.         ASSERT(pInfo->IsKindOf(RUNTIME_CLASS(COleDropInfo)));
  24.         if( pInfo->pDataObject->IsDataAvailable( CF_TEXT ) )//拖动对象为文本
  25.         {
  26.         HGLOBAL hMem = pInfo->pDataObject->GetGlobalData( CF_TEXT );
  27.         char* lp = (char *)GlobalLock((HGLOBAL) hMem);//lock source
  28.         if ( lp != NULL)
  29.         {
  30.         //Set Windows title with Drop text 
  31.         SetWindowText( lp );
  32.         }
  33.         GlobalUnlock( hMem );//unlock source
  34.         return TRUE;
  35.         }
  36.         else
  37.         return FALSE;
  38. }

        现在,一个支持拖放的编辑框就做好了。那么把它放到对话框中测试一下。 添加一个编辑控制m_dropEdit到对话框模板中,使用ClassWizard声明该编辑控制为CDropEdit类型。在对话框类的OnInitDialog()函数中注册该编辑对象为拖放目标窗口。

  1. BOOL CDropExDemoDlg::OnInitDialog()
  2. {
  3.         //其它内容
  4.         if( !m_dropEdit.Register() )
  5.         TRACE("register drop edit faile");
  6.         //其它内容
  7. }

        最后,别忘了初始化OLE。在应用程序类初始化函数中添加OLE初始化代码。

  1. BOOL CDropExDemoApp::InitInstance()
  2. {
  3.         //其它内容
  4.         if( !AfxOleInit() )
  5.         TRACE("Ole init faile");
  6.         //其它内容
  7. }

 

        现在,编译并运行程序。从VC ++ 6.0编辑窗口中选择一词,用鼠标拖动其到编辑控制上并释放鼠标左键,编辑控制中内容变为拖动来的文本。至此,测试成功完成。

四、总结
        通过扩展MFC的拖放目标类,做到了拖放操作与窗口类类型无关,相应的拖放目标窗口只需要响应COleDropTargetEx发送的拖动消息便可方便的响应拖动事件,而且扩展的拖放目标类与支持拖放的窗口类皆可再被继承,因此代码的重用性较好。

参考文献:
1、 Bruce Eckel,Thinking in C++,机械工业出版社,2001.01
2、 MFC Library Reference,Microsoft Press,北京希望电脑公司,1999.02
3、 Wang Zemin,利用MFC实现对象拖放,互联网资料

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值