第五部分:IDropTarget实现

这一节我们讲如何来实现一个自己的Drop Target,我们需要实现IDropTarget接口。

1.如何成为一个"Drop Target"

为了使窗体能接收拖放的数据,窗口必须注册为drop目标,调用OLE API RegisterDragDrop来完成这件事情,函数原型如下:

[cpp]  view plain  copy
  1. WINOLEAPI RegisterDragDrop(  
  2.   HWND hwnd,                    //Handle to a window that can accept drops  
  3.   IDropTarget * pDropTarget        //Pointer to object that is to be target of drop  
  4. );  

这个函数第一个参数就是一个窗体的HWND,表示哪个窗体注册为drop目标。第二个参数就是一个IDropTarget指针,在拖放过程中,就会调用到这个接口的各种方法。
与这个函数功能相反的一个API RevokeDragDrop,表示取消这个窗体drop目标,其原型如下:

[cpp]  view plain  copy
  1. WINOLEAPI RevokeDragDrop(  
  2.   HWND hwnd                    //Handle to a window that can accept drops  
  3. );  

这个方法在窗体销毁之前调用,在注册窗口成为一个drop目标时,它内部会调用IDropTarget接口的AddRef函数,相反,在反注册时,应该会调用这个IDataObject接口的Release函数。

在调用RegisterDragDrop之间,你应当保证OleInitialize调用,不要用CoInitialize或CoInitializeEx来初始化COM,应当初始化OLE。

2.IDropTarget接口实现

IDropTarget接口有四个方法:
a) DragEnter: 判断是否可以接受一个拖放操作,以及接受之后的效果。
b) DragOver: 提供通过DoDragDrop函数执行的目标反馈。
c) DragLeave: 导致一个drop 目标挂起它的返回行为。
d) Drop: 数据放在目标窗口。

这些函数都是由COM/OLE运行时在一个对象被拖到我们注册的窗口的时候来调用,最本质是在DoDragDrop函数里面被告调用。它们每一个函数都有不同的功能,我们要最的就是实现这些函数,使拖放操作表现正确。

2.2 SdkDropTarget.h

[cpp]  view plain  copy
  1. #ifdef __cplusplus  
  2. #ifndef _SDKDROPTARGET_H_  
  3. #define _SDKDROPTARGET_H_  
  4.   
  5. #include "SdkDataObject.h"  
  6. typedef DWORD DROPEFFECT;  
  7.   
  8. class CLASS_DECLSPEC SdkDropTarget : public IDropTarget  
  9. {  
  10. public:  
  11.   
  12.     SdkDropTarget();  
  13.     ~SdkDropTarget();  
  14.   
  15.     BOOL RegisterDropTarget(HWND hWnd);  
  16.     void RevokeDropTarget();  
  17.     void AllowDragDrop(BOOL bAllowDrop = TRUE);  
  18.     DROPEFFECT FilterDropEffect(DWORD grfKeyState, DROPEFFECT dwEffectSrc);  
  19.   
  20.     IFACEMETHODIMP QueryInterface(REFIID riid, void **ppv);  
  21.     IFACEMETHODIMP_(ULONG) AddRef(void);  
  22.     IFACEMETHODIMP_(ULONG) Release(void);  
  23.   
  24.     IFACEMETHODIMP DragEnter(IDataObject *pDataObj,   
  25.         DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);  
  26.     IFACEMETHODIMP DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);  
  27.     IFACEMETHODIMP DragLeave(void);  
  28.     IFACEMETHODIMP Drop(IDataObject *pDataObj,   
  29.         DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);  
  30.   
  31. private:  
  32.   
  33.     //!< The flag indicates whether allow drag or not  
  34.     BOOL                m_isAllowDrag;       
  35.     //!< The flag indicates the data available or not       
  36.     BOOL                m_isDataAvailable;        
  37.     //!< The target window handle  
  38.     HWND                m_hTargetWnd;       
  39.     //!< The reference count        
  40.     volatile LONG       m_lRefCount;              
  41.     //!< The pointer of IDropTargetHelper interface  
  42.     IDropTargetHelper  *m_pDropTargetHelper;      
  43. };  

上面是SdkDropTarget的类声明,除了存引用计数的变量之外,我们还有一个BOOL类型的变量m_isDataAvailable,用来表示当前的是否包含我们需要的数据,这样做就不会每次都去调用方法检测了。
m_hTargetWnd就是我们注册drop目标的窗体的HWND。
m_pDropTargetHelper这是一个Shell的接口,它帮我们实现了一些拖放操作,这里我们用它来帮我们实现,当然你可以不用,关键是要明白本质。

上面还提供了一些公有的方法,如AllowDragDrop表示是否允许拖放操作,RevokeDropTarget用来反注册窗体,取消drop目标,这些函数都是一些辅助性函数,不是最本质的。

关于AddRef, Release, QueryInterface这几个函数,我就不重点说明了。

3.DragEnter实现

IDropTarget::DragEnter的函数原型如下:

[cpp]  view plain  copy
  1. HRESULT DragEnter(  
  2.   IDataObject * pDataObject,  //Pointer to the interface of the source data object  
  3.   DWORD grfKeyState,          //Current state of keyboard modifier keys  
  4.   POINTL pt,                  //Current cursor coordinates  
  5.   DWORD * pdwEffect           //Pointer to the effect of the drag-and-drop operation  
  6. );  

pDataObject: 一个IDataObject指针,就是调用DoDragDrop传的那个指针。我们在DragEnter里面检查数据对象是否包含我们需要的数据。
grfKeyState: 保留键盘修饰符的状态,如Ctrl, Shift, Alt以及鼠标按键状态。
pt: 表示鼠标在我们窗口的位置,我们可以利用这个点来判断拖放是在发生在我们指定的拖放区域。
pdwEffect: 输出参数,表示当前允许源的drop效果。

我们在实现DragEnter时,通常需要做以下几件事:

1、检查数据对象,看数据对象是否包含我们的数据。
2、检查存储在grfKeyState的键盘状态,并且计算是什么的样drop效果,然后通过输出参数返回,如果返回一个DROPEFFECT_NONE的话,表明这个数据不能被接受。
3、存储最终的drop效果到pdwEffect的指针。

最本质的,我们应当在这个函数里面决定数据是否可以被接受。

实现代码如下:

[cpp]  view plain  copy
  1. STDMETHODIMP SdkDropTarget::DragEnter(IDataObject *pDataObj,  
  2.      DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)  
  3. {  
  4.     if ( NULL == pDataObj )  
  5.     {  
  6.         return E_INVALIDARG;  
  7.     }  
  8.   
  9.     POINT ppt = { pt.x, pt.y };  
  10.     DROPEFFECT dwEffect = OnDragEnter(m_hTargetWnd,   
  11.         (SdkDataObject*)pDataObj, grfKeyState, ppt);  
  12.     *pdwEffect = dwEffect;  
  13.     m_isDataAvailable = (DROPEFFECT_NONE == dwEffect) ? FALSE : TRUE;  
  14.   
  15.     if ( NULL != m_pDropTargetHelper )  
  16.     {  
  17.         m_pDropTargetHelper->DragEnter(m_hTargetWnd, pDataObj, &ppt, *pdwEffect);  
  18.     }  
  19.     return S_OK;  
  20. }  
这里面调用了我自己定义的一个虚方法OnDragEnter,这是我们自己定义的一个方法,以使派生类可以得到drag enter的通知。头文件里没有给出声明,因为它不是很重要。这里面,可以调用IDataObject的QueryGetData函数来检查指定的数据是否被当前的数据所支持。

4.DragOver实现

这个函数会被频繁调用,因些这个函数的实现最好尽量高效。当键盘状态改变或鼠标在窗口上面移动时,这个函数会被调用,我们一般在这个函数里面更改鼠标光标的状态。比如,当ctrl按下时,我们应当呈现什么光标,当shift按下时,又应该是什么光标等,下在是这个函数的原型。

[cpp]  view plain  copy
  1. HRESULT DragOver(  
  2.   DWORD grfKeyState, //Current state of keyboard modifier keys  
  3.   POINTL pt,         //Current cursor coordinates  
  4.   DWORD * pdwEffect  //Pointer to the effect of the drag-and-drop operation  
  5. );  
它的实现也比较简单。

[cpp]  view plain  copy
  1. STDMETHODIMP SdkDropTarget::DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)  
  2. {  
  3.     POINT ppt = { pt.x, pt.y };  
  4.     DWORD dwEffect = OnDragOver(m_hTargetWnd, grfKeyState, ppt);  
  5.     *pdwEffect = (FALSE == m_isDataAvailable) ? DROPEFFECT_NONE : dwEffect;  
  6.   
  7.     if ( NULL != m_pDropTargetHelper )  
  8.     {  
  9.         m_pDropTargetHelper->DragOver(&ppt, *pdwEffect);  
  10.     }  
  11.     return S_OK;  
  12. }  

5.DragLeave实现

这个函数在鼠标光标移动到窗体(drop目标)之外时会被调用,或者当ESC按下时。这个函数一般不用做什么操作,可以用来作一些标志清理工作等。例如,当一个数据拖过目标窗体时,窗体改变一下背景色,当Drag Leave时,可以在这个函数里面来恢复原来的颜色。
[cpp]  view plain  copy
  1. STDMETHODIMP SdkDropTarget::DragLeave(void)  
  2. {  
  3.     OnDragLeave(m_hTargetWnd);  
  4.   
  5.     m_isDataAvailable = TRUE;  
  6.     if ( NULL != m_pDropTargetHelper )  
  7.     {  
  8.         m_pDropTargetHelper->DragLeave();  
  9.     }  
  10.     return S_OK;  
  11. }  

6.Drop实现

这个函数在是鼠标左键(一般情况是左键)释放时会被调用。在这个函数中,我们就可以把IDataObject里面的数据取出来,执行相应的逻辑操作。

[cpp]  view plain  copy
  1. STDMETHODIMP SdkDropTarget::Drop(IDataObject *pDataObj,   
  2.     DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)  
  3. {  
  4.     if ( NULL == pDataObj )  
  5.     {  
  6.         return E_INVALIDARG;  
  7.     }  
  8.   
  9.   
  10.     POINT ppt = { pt.x, pt.y };  
  11.     if ( NULL != m_pDropTargetHelper )  
  12.     {  
  13.         m_pDropTargetHelper->Drop(pDataObj, &ppt, *pdwEffect);  
  14.     }  
  15.   
  16.   
  17.     BOOL isOK = OnDrop(m_hTargetWnd,   
  18.         (SdkDataObject*)pDataObj, grfKeyState, ppt);  
  19.     return (TRUE == isOK) ? S_OK : E_FAIL;  
  20. }  

我们在这一节给出了IDropTarget接口的实现,下一节我们将给出一个例子,完整说明这些接口之间的调用关系。

猜你喜欢

转载自blog.csdn.net/yousss/article/details/79994397