MFC drag&drop有两种:OLE拖放和文件管理器(WM_DROPFILES消息)拖放。这是两种不同的机制。
Drop里面很明确的先使用OnDropEx处理drop操作,如果OnDropEx没有处理成功,就让给OnDrop处理。这个优先级要清楚。
STDMETHODIMP COleDropTarget::XDropTarget::Drop(THIS_ LPDATAOBJECT lpDataObject, DWORD dwKeyState, POINTL pt, LPDWORD pdwEffect)
{
METHOD_PROLOGUE_EX(COleDropTarget, DropTarget)
ASSERT_VALID(pThis);
ASSERT(pdwEffect != NULL);
ASSERT(lpDataObject != NULL);
if (lpDataObject == NULL || pdwEffect == NULL)
{
return E_INVALIDARG;
}
SCODE sc = E_UNEXPECTED;
TRY
{
// cancel drag scrolling
pThis->m_nTimerID = 0xffff;
// prepare for call to OnDragOver
CWnd* pWnd = CWnd::FromHandle(pThis->m_hWnd);
ASSERT_VALID(pWnd);
COleDataObject dataObject;
dataObject.Attach(lpDataObject, FALSE);
CPoint point((int)pt.x, (int)pt.y);
pWnd->ScreenToClient(&point);
// verify that drop is legal
DROPEFFECT dropEffect = _AfxFilterDropEffect(pThis->OnDragOver(pWnd, &dataObject, dwKeyState, point), *pdwEffect);
// execute the drop (try OnDropEx then OnDrop for backward compatibility)
DROPEFFECT temp = pThis->OnDropEx(pWnd, &dataObject, dropEffect, *pdwEffect, point);
if (temp != -1)
{
// OnDropEx was implemented, return its drop effect
dropEffect = temp;
}
else if (dropEffect != DROPEFFECT_NONE)
{
// OnDropEx not implemented
if (!pThis->OnDrop(pWnd, &dataObject, dropEffect, point))
dropEffect = DROPEFFECT_NONE;
}
else
{
// drop not accepted, allow cleanup
pThis->OnDragLeave(pWnd);
}
// release potentially cached data object
RELEASE(pThis->m_lpDataObject);
*pdwEffect = dropEffect;
sc = S_OK;
}
END_TRY
return sc;
}
下面是拖动过程控制的3个函数
STDMETHODIMP COleDropTarget::XDropTarget::DragEnter(THIS_ LPDATAOBJECT lpDataObject, DWORD dwKeyState, POINTL pt, LPDWORD pdwEffect)
{
METHOD_PROLOGUE_EX(COleDropTarget, DropTarget)
ASSERT_VALID(pThis);
ASSERT(pdwEffect != NULL);
ASSERT(lpDataObject != NULL);
if (lpDataObject == NULL || pdwEffect == NULL)
{
return E_INVALIDARG;
}
SCODE sc = E_UNEXPECTED;
TRY
{
// cache lpDataObject
lpDataObject->AddRef();
RELEASE(pThis->m_lpDataObject);
pThis->m_lpDataObject = lpDataObject;
CWnd* pWnd = CWnd::FromHandle(pThis->m_hWnd);
ASSERT_VALID(pWnd);
CPoint point((int)pt.x, (int)pt.y);
pWnd->ScreenToClient(&point);
// check first for entering scroll area
DROPEFFECT dropEffect = pThis->OnDragScroll(pWnd, dwKeyState, point);
if ((dropEffect & DROPEFFECT_SCROLL) == 0)
{
// funnel through OnDragEnter since not in scroll region
COleDataObject dataObject;
dataObject.Attach(lpDataObject, FALSE);
dropEffect = pThis->OnDragEnter(pWnd, &dataObject, dwKeyState, point);
}
*pdwEffect = _AfxFilterDropEffect(dropEffect, *pdwEffect);
sc = S_OK;
}
END_TRY
return sc;
}
STDMETHODIMP COleDropTarget::XDropTarget::DragOver(THIS_ DWORD dwKeyState, POINTL pt, LPDWORD pdwEffect)
{
METHOD_PROLOGUE_EX(COleDropTarget, DropTarget)
ASSERT_VALID(pThis);
ASSERT(pdwEffect != NULL);
ASSERT(pThis->m_lpDataObject != NULL);
if (pdwEffect == NULL)
{
return E_INVALIDARG;
}
SCODE sc = E_UNEXPECTED;
TRY
{
CWnd* pWnd = CWnd::FromHandle(pThis->m_hWnd);
ASSERT_VALID(pWnd);
CPoint point((int)pt.x, (int)pt.y);
pWnd->ScreenToClient(&point);
// check first for entering scroll area
DROPEFFECT dropEffect = pThis->OnDragScroll(pWnd, dwKeyState, point);
if ((dropEffect & DROPEFFECT_SCROLL) == 0)
{
// funnel through OnDragOver
COleDataObject dataObject;
dataObject.Attach(pThis->m_lpDataObject, FALSE);
dropEffect = pThis->OnDragOver(pWnd, &dataObject, dwKeyState, point);
}
*pdwEffect = _AfxFilterDropEffect(dropEffect, *pdwEffect);
sc = S_OK;
}
END_TRY
return sc;
}
STDMETHODIMP COleDropTarget::XDropTarget::DragLeave(THIS)
{
METHOD_PROLOGUE_EX(COleDropTarget, DropTarget)
ASSERT_VALID(pThis);
CWnd* pWnd = CWnd::FromHandle(pThis->m_hWnd);
ASSERT_VALID(pWnd);
// cancel drag scrolling
pThis->m_nTimerID = 0xffff;
// allow derivative to do own cleanup
COleDataObject dataObject;
dataObject.Attach(pThis->m_lpDataObject, FALSE);
pThis->OnDragLeave(pWnd);
// release cached data object
RELEASE(pThis->m_lpDataObject);
return S_OK;
}
CView类中有一个COleDropTarget类的对象,在CView窗口初始化时,调用COleDropTarget类成员函数Register(),注册该视图窗口为拖放的接收窗口。
当进行拖放操作的鼠标指针移动到CView窗口范围内时,COleDropTarge类会做出反应,它的OnDragEnter、OnDragOver、OnDropEx、OnDrop等成员函数被依次调用,这些函数默认均是调用CView类的同名成员函数。
DROPEFFECT COleDropTarget::OnDragEnter(CWnd* pWnd, COleDataObject* pDataObject,
DWORD dwKeyState, CPoint point)
{
ASSERT_VALID(this);
if (!pWnd->IsKindOf(RUNTIME_CLASS(CView)))
return DROPEFFECT_NONE;
// default delegates to view
CView* pView = (CView*)pWnd;
ASSERT_VALID(pView);
return pView->OnDragEnter(pDataObject, dwKeyState, point);
}
DROPEFFECT COleDropTarget::OnDragOver(CWnd* pWnd, COleDataObject* pDataObject,
DWORD dwKeyState, CPoint point)
{
ASSERT_VALID(this);
if (!pWnd->IsKindOf(RUNTIME_CLASS(CView)))
return DROPEFFECT_NONE;
// default delegates to view
CView* pView = (CView*)pWnd;
ASSERT_VALID(pView);
return pView->OnDragOver(pDataObject, dwKeyState, point);
}
BOOL COleDropTarget::OnDrop(CWnd* pWnd, COleDataObject* pDataObject,
DROPEFFECT dropEffect, CPoint point)
{
ASSERT_VALID(this);
if (!pWnd->IsKindOf(RUNTIME_CLASS(CView)))
return DROPEFFECT_NONE;
// default delegates to view
CView* pView = (CView*)pWnd;
ASSERT_VALID(pView);
return pView->OnDrop(pDataObject, dropEffect, point);
}
DROPEFFECT COleDropTarget::OnDropEx(CWnd* pWnd, COleDataObject* pDataObject,
DROPEFFECT dropEffect, DROPEFFECT dropEffectList, CPoint point)
{
ASSERT_VALID(this);
if (!pWnd->IsKindOf(RUNTIME_CLASS(CView)))
return (DROPEFFECT)-1; // not implemented
// default delegates to view
CView* pView = (CView*)pWnd;
ASSERT_VALID(pView);
return pView->OnDropEx(pDataObject, dropEffect, dropEffectList, point);
}
void COleDropTarget::OnDragLeave(CWnd* pWnd)
{
ASSERT_VALID(this);
if (!pWnd->IsKindOf(RUNTIME_CLASS(CView)))
return;
// default delegates to view
CView* pView = (CView*)pWnd;
ASSERT_VALID(pView);
pView->OnDragLeave();
return;
}
1)从COleDropTarget派生自己的DropTarget类,比如CMyOleDropTarget
class CMyOleDropTarget : public COleDropTarget
{
DECLARE_DYNAMIC(CMyOleDropTarget)
public:
CMyOleDropTarget();
virtual ~CMyOleDropTarget();
public:
// Overridables
virtual DROPEFFECT OnDragEnter(CWnd* pWnd, COleDataObject* pDataObject, DWORD dwKeyState, CPoint point);
virtual DROPEFFECT OnDragOver(CWnd* pWnd, COleDataObject* pDataObject, DWORD dwKeyState, CPoint point);
virtual BOOL OnDrop(CWnd* pWnd, COleDataObject* pDataObject, DROPEFFECT dropEffect, CPoint point);
virtual DROPEFFECT OnDropEx(CWnd* pWnd, COleDataObject* pDataObject, DROPEFFECT dropDefault, DROPEFFECT dropList, CPoint point);
virtual void OnDragLeave(CWnd* pWnd);
protected:
DECLARE_MESSAGE_MAP()
};
2)重载基类COleDropTarget的虚函数:
virtual DROPEFFECT OnDragEnter(CWnd* pWnd, COleDataObject* pDataObject, DWORD dwKeyState, CPoint point);
virtual DROPEFFECT OnDragOver(CWnd* pWnd, COleDataObject* pDataObject, DWORD dwKeyState, CPoint point);
virtual BOOL OnDrop(CWnd* pWnd, COleDataObject* pDataObject, DROPEFFECT dropEffect, CPoint point);
virtual DROPEFFECT OnDropEx(CWnd* pWnd, COleDataObject* pDataObject, DROPEFFECT dropDefault, DROPEFFECT dropList, CPoint point);
virtual void OnDragLeave(CWnd* pWnd);
把原来绑定CView窗口的操作,改成绑定我们自己的窗口(比如CMyListCtrl窗口),详细代码如下:
DROPEFFECT CMyOleDropTarget::OnDragEnter(CWnd* pWnd, COleDataObject* pDataObject, DWORD dwKeyState, CPoint point)
{
ASSERT_VALID(this);
if (pWnd->IsKindOf(RUNTIME_CLASS(CMyListCtrl)))
{
CMyListCtrl* pCtrl = (CMyListCtrl*)pWnd;
ASSERT_VALID(pCtrl);
return pCtrl->OnDragEnter(pWnd, pDataObject, dwKeyState, point);
}
else
{
return COleDropTarget::OnDragEnter(pWnd, pDataObject, dwKeyState, point);
}
}
DROPEFFECT CMyOleDropTarget::OnDragOver(CWnd* pWnd, COleDataObject* pDataObject, DWORD dwKeyState, CPoint point)
{
ASSERT_VALID(this);
if (pWnd->IsKindOf(RUNTIME_CLASS(CMyListCtrl)))
{
CMyListCtrl* pCtrl = (CMyListCtrl*)pWnd;
ASSERT_VALID(pCtrl);
return pCtrl->OnDragOver(pWnd, pDataObject, dwKeyState, point);
}
else
{
return COleDropTarget::OnDragOver(pWnd, pDataObject, dwKeyState, point);
}
}
BOOL CMyOleDropTarget::OnDrop(CWnd* pWnd, COleDataObject* pDataObject, DROPEFFECT dropEffect, CPoint point)
{
ASSERT_VALID(this);
if(pWnd->IsKindOf(RUNTIME_CLASS(CMyListCtrl)))
{
CMyListCtrl* pCtrl = (CMyListCtrl*)pWnd;
ASSERT_VALID(pCtrl);
return pCtrl->OnDrop(pWnd, pDataObject, dropEffect, point);
}
else
{
return COleDropTarget::OnDrop(pWnd, pDataObject, dropEffect, point);
}
}
DROPEFFECT CMyOleDropTarget::OnDropEx(CWnd* pWnd, COleDataObject* pDataObject, DROPEFFECT dropEffect, DROPEFFECT dropEffectList, CPoint point)
{
ASSERT_VALID(this);
if (pWnd->IsKindOf(RUNTIME_CLASS(CMyListCtrl)))
{
CMyListCtrl* pCtrl = (CMyListCtrl*)pWnd;
ASSERT_VALID(pCtrl);
return pCtrl->OnDropEx(pWnd, pDataObject, dropEffect, dropEffectList, point);
}
else
{
return COleDropTarget::OnDropEx(pWnd, pDataObject, dropEffect, dropEffectList, point);
}
}
void CMyOleDropTarget::OnDragLeave(CWnd* pWnd)
{
ASSERT_VALID(this);
if (pWnd->IsKindOf(RUNTIME_CLASS(CMyListCtrl)))
{
CMyListCtrl* pCtrl = (CMyListCtrl*)pWnd;
ASSERT_VALID(pCtrl);
pCtrl->OnDragLeave(pWnd);
}
else
{
COleDropTarget::OnDragLeave(pWnd);
}
}
3)把我们自己要准备支持Drop的窗口,注册成拖放的接收窗口
3.1)在CMyListCtrl中声明CMyOleDropTarget类型的成员变量
#include "MyOleDropTarget.h"
class CMyListCtrl : public CListCtrl
{
DECLARE_DYNAMIC(CMyListCtrl)
public:
CMyListCtrl();
virtual ~CMyListCtrl();
virtual DROPEFFECT OnDragEnter(CWnd* pWnd, COleDataObject* pDataObject, DWORD dwKeyState, CPoint point);
virtual DROPEFFECT OnDragOver(CWnd* pWnd, COleDataObject* pDataObject, DWORD dwKeyState, CPoint point);
virtual BOOL OnDrop(CWnd* pWnd, COleDataObject* pDataObject, DROPEFFECT dropEffect, CPoint point);
virtual DROPEFFECT OnDropEx(CWnd* pWnd, COleDataObject* pDataObject, DROPEFFECT dropDefault, DROPEFFECT dropList, CPoint point);
virtual void OnDragLeave(CWnd* pWnd);
public:
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
protected:
DECLARE_MESSAGE_MAP()
private:
CMyOleDropTarget m_oDropTarget;
};
3.2)在CMyListCtrl::OnCreate中调用CMyOleDropTarget.Register(this)进行注册
int CMyListCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CListCtrl::OnCreate(lpCreateStruct) == -1)
return -1;
// TODO: Add your specialized creation code here
m_oDropTarget.Register(this);
return 0;
}
4)在CMyListCtrl中实现如下方法,控制和实现Drop过程:
virtual DROPEFFECT OnDragEnter(CWnd* pWnd, COleDataObject* pDataObject, DWORD dwKeyState, CPoint point);
virtual DROPEFFECT OnDragOver(CWnd* pWnd, COleDataObject* pDataObject, DWORD dwKeyState, CPoint point);
virtual BOOL OnDrop(CWnd* pWnd, COleDataObject* pDataObject, DROPEFFECT dropEffect, CPoint point);
virtual DROPEFFECT OnDropEx(CWnd* pWnd, COleDataObject* pDataObject, DROPEFFECT dropDefault, DROPEFFECT dropList, CPoint point);
virtual void OnDragLeave(CWnd* pWnd);
详细代码如下:
DROPEFFECT CMyListCtrl::OnDragEnter(CWnd* pWnd, COleDataObject* pDataObject, DWORD dwKeyState, CPoint point)
{
}
DROPEFFECT CMyListCtrl::OnDragOver(CWnd* pWnd, COleDataObject* pDataObject, DWORD dwKeyState, CPoint point)
{
}
BOOL CMyListCtrl::OnDrop(CWnd* pWnd, COleDataObject* pDataObject, DROPEFFECT dropEffect, CPoint point)
{
}
//OnDropEx函数会在OnDrop函数之前调用,如果OnDropEx函数没有对拖放动作进行处理,
//则应用程序框架会接着调用OnDrop函数进行处理。
//所以必须要在派生类中重载OnDropEx函数——即使什么动作都都没有做——否则我们的OnDrop函数将不会被执行到,
//因为没有重载的话,将会调用基类的OnDropEx函数,而基类的OnDropEx函数对拖放是进行了处理的。
//当然你也可以把对拖放进行处理的动作放在OnDropEx中——那样就不需要重载OnDrop了。
DROPEFFECT CMyListCtrl::OnDropEx(CWnd* pWnd, COleDataObject* pDataObject, DROPEFFECT dropDefault, DROPEFFECT dropList, CPoint point)
{
}
void CMyListCtrl::OnDragLeave(CWnd* pWnd)
{
}