天天看點

MFC Drag & Drop

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)
{

}