Drag & Drop
MFC drag&drop有兩種:OLE拖放和檔案管理器(WM_DROPFILES消息)拖放。這是兩種不同的機制。
OLE拖放的實作 - Drop的實作 - COleDropTarget
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 = ;
// 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 != -)
{
// 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) == )
{
// 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) == )
{
// 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 = ;
// 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;
}
OLE拖放的實作 - Drop的實作 - MFC CView類
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)-; // 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;
}
自定義Drop視窗的實作
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) == -)
return -;
// TODO: Add your specialized creation code here
m_oDropTarget.Register(this);
return ;
}
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)
{
}