这一节我们讲如何来实现一个自己的Drop Target,我们需要实现IDropTarget接口。
为了使窗体能接收拖放的数据,窗口必须注册为drop目标,调用OLE API RegisterDragDrop来完成这件事情,函数原型如下:
WINOLEAPI RegisterDragDrop( HWND hwnd, //Handle to a window that can accept drops IDropTarget * pDropTarget //Pointer to object that is to be target of drop );
WINOLEAPI RevokeDragDrop( HWND hwnd //Handle to a window that can accept drops );
IDropTarget接口有四个方法:
a) DragEnter: 判断是否可以接受一个拖放操作,以及接受之后的效果。
b) DragOver: 提供通过DoDragDrop函数执行的目标反馈。
c) DragLeave: 导致一个drop 目标挂起它的返回行为。
d) Drop: 数据放在目标窗口。
这些函数都是由COM/OLE运行时在一个对象被拖到我们注册的窗口的时候来调用,最本质是在DoDragDrop函数里面被告调用。它们每一个函数都有不同的功能,我们要最的就是实现这些函数,使拖放操作表现正确。
#ifdef __cplusplus #ifndef _SDKDROPTARGET_H_ #define _SDKDROPTARGET_H_ #include "SdkDataObject.h" typedef DWORD DROPEFFECT; class CLASS_DECLSPEC SdkDropTarget : public IDropTarget { public: SdkDropTarget(); ~SdkDropTarget(); BOOL RegisterDropTarget(HWND hWnd); void RevokeDropTarget(); void AllowDragDrop(BOOL bAllowDrop = TRUE); DROPEFFECT FilterDropEffect(DWORD grfKeyState, DROPEFFECT dwEffectSrc); IFACEMETHODIMP QueryInterface(REFIID riid, void **ppv); IFACEMETHODIMP_(ULONG) AddRef(void); IFACEMETHODIMP_(ULONG) Release(void); IFACEMETHODIMP DragEnter(IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect); IFACEMETHODIMP DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect); IFACEMETHODIMP DragLeave(void); IFACEMETHODIMP Drop(IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect); private: //!< The flag indicates whether allow drag or not BOOL m_isAllowDrag; //!< The flag indicates the data available or not BOOL m_isDataAvailable; //!< The target window handle HWND m_hTargetWnd; //!< The reference count volatile LONG m_lRefCount; //!< The pointer of IDropTargetHelper interface IDropTargetHelper *m_pDropTargetHelper; };
IDropTarget::DragEnter的函数原型如下:
HRESULT DragEnter( IDataObject * pDataObject, //Pointer to the interface of the source data object DWORD grfKeyState, //Current state of keyboard modifier keys POINTL pt, //Current cursor coordinates DWORD * pdwEffect //Pointer to the effect of the drag-and-drop operation );
STDMETHODIMP SdkDropTarget::DragEnter(IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) { if ( NULL == pDataObj ) { return E_INVALIDARG; } POINT ppt = { pt.x, pt.y }; DROPEFFECT dwEffect = OnDragEnter(m_hTargetWnd, (SdkDataObject*)pDataObj, grfKeyState, ppt); *pdwEffect = dwEffect; m_isDataAvailable = (DROPEFFECT_NONE == dwEffect) ? FALSE : TRUE; if ( NULL != m_pDropTargetHelper ) { m_pDropTargetHelper->DragEnter(m_hTargetWnd, pDataObj, &ppt, *pdwEffect); } return S_OK; }这里面调用了我自己定义的一个虚方法OnDragEnter,这是我们自己定义的一个方法,以使派生类可以得到drag enter的通知。头文件里没有给出声明,因为它不是很重要。这里面,可以调用IDataObject的QueryGetData函数来检查指定的数据是否被当前的数据所支持。
HRESULT DragOver( DWORD grfKeyState, //Current state of keyboard modifier keys POINTL pt, //Current cursor coordinates DWORD * pdwEffect //Pointer to the effect of the drag-and-drop operation );它的实现也比较简单。
STDMETHODIMP SdkDropTarget::DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) { POINT ppt = { pt.x, pt.y }; DWORD dwEffect = OnDragOver(m_hTargetWnd, grfKeyState, ppt); *pdwEffect = (FALSE == m_isDataAvailable) ? DROPEFFECT_NONE : dwEffect; if ( NULL != m_pDropTargetHelper ) { m_pDropTargetHelper->DragOver(&ppt, *pdwEffect); } return S_OK; }
STDMETHODIMP SdkDropTarget::DragLeave(void) { OnDragLeave(m_hTargetWnd); m_isDataAvailable = TRUE; if ( NULL != m_pDropTargetHelper ) { m_pDropTargetHelper->DragLeave(); } return S_OK; }
STDMETHODIMP SdkDropTarget::Drop(IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) { if ( NULL == pDataObj ) { return E_INVALIDARG; } POINT ppt = { pt.x, pt.y }; if ( NULL != m_pDropTargetHelper ) { m_pDropTargetHelper->Drop(pDataObj, &ppt, *pdwEffect); } BOOL isOK = OnDrop(m_hTargetWnd, (SdkDataObject*)pDataObj, grfKeyState, ppt); return (TRUE == isOK) ? S_OK : E_FAIL; }