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 = 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;
}

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)-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;
}

自定义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) == -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)
{

}

你可能感兴趣的:(MFC Drag & Drop)