1. 程式人生 > >關於MFC中任意物件的拖拽功能的實現(COleDataSource, COleDropTarget)

關於MFC中任意物件的拖拽功能的實現(COleDataSource, COleDropTarget)

    拖拽功能的實現是一個全域性的功能實現,也就是沒有程序之隔,是一個類似windows的檔案拖拽管理和開啟的功能。而下面說記錄的是關於任意內容的全域性拖拽的實現細節。關於相關函式和物件的具體描述可以直接MSDN檢視,這裡就不對其進行詳細的簡介。

    大體的實現可以分為兩個主要的部分:

         1. 被拖拽物件中新增COleDataSource,以處理被拖拽物件

         2. 在拖拽目標中新增COleDropTarget, 以處理拖拽目標接受拖拽的相關事件

    下面是具體的程式碼示例,這個示例的具體功能實現的是把CTreeCtrl中的一個item所關聯的資料拖拽到指定的CView類中,類似與檔案的拖拽開啟。

     1. 關於被拖拽物件CTreeCtrl中的相關程式碼實現:

            1.1.  首先在CTreeCtrl的擴充套件類中新增COleDataSource物件,我這裡新增的是COleDataSource型別的指標。

class CDataViewTree : public CTreeCtrl

{

 private:

    COleDataSource* m_dropSource;

    ... ...

    //這是開始拖拽的訊息處理函式

public:
    afx_msg void OnTvnBegindrag(NMHDR *pNMHDR, LRESULT *pResult);

    ... ...

};

            1.2. 在訊息對映表中新增訊息對映,處理開始拖拽的訊息。

                     BEGIN_MESSAGE_MAP(CDataViewTree, CTreeCtrl)
                              ON_NOTIFY_REFLECT(TVN_BEGINDRAG, &CDataViewTree::OnTvnBegindrag)
                     END_MESSAGE_MAP()

            1.3. 具體的實現部分都在OnTvnBegingdrag()函式中。

void CDataViewTree::OnTvnBegindrag(NMHDR *pNMHDR, LRESULT *pResult)

{
    LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);
    // TODO: 在此新增控制元件通知處理程式程式碼
    *pResult = 0;

    char* hgMem = NULL, *htmpMem = NULL;

    HTREEITEM hItem = GetSelectedItem();

    CString dataBuf_dataName, dataBuf_dataID;
    int typeIndex, ctrlType, bufSize;
    //可以修改成多項同時拖拽
    if (hItem != NULL && GetChildItem(hItem) == NULL)
    {
         //這裡我的每個item關聯的是一個變數,所用資料包括了變數的名字,ID, 型別
        dataBuf_dataName = GetItemText(hItem);
        dataBuf_dataID = GetItemID(hItem);
        typeIndex     = GetItemTypeIndex(hItem);

        bufSize = 4 + 32*2 + dataBuf_dataName.GetLength()*2+2;

        m_dropSource = new COleDataSource;

        //開闢全域性快取,用來儲存被拖放物件的相關資料,這裡一個大小
        hgMem = (char*)GlobalAlloc(GPTR, bufSize);

        htmpMem = (char*)GlobalLock((HGLOBAL)hgMem);
        ASSERT(htmpMem != NULL);

        ZeroMemory(htmpMem, bufSize);

        // 快取中資料的儲存格式可以根據自己的需求來定,注意要便於快取的讀取
        *((int*)htmpMem) = typeIndex;
        lstrcpy((TCHAR*)(htmpMem + 4), dataBuf_dataID);
        lstrcpy((TCHAR*)(htmpMem + 68), dataBuf_dataName);

        // 快取關聯
        m_dropSource->Empty();
        m_dropSource->CacheGlobalData(CF_TEXT, (HGLOBAL)htmpMem);

        // 開始資料拖拽,函式會在拖拽結束後返回
        DROPEFFECT dropEffect = m_dropSource->DoDragDrop();

        // 空間的解鎖和釋放
        GlobalUnlock((HGLOBAL)hgMem);

        GlobalFree((HGLOBAL)hgMem);
        delete m_dropSource;
        m_dropSource = NULL;
    }
}

     2. 關於拖拽目標中的相關程式碼實現

         2.1. 拖拽接收目標中新增COleDropTarget物件。並新增相關的處理函式,其中包括了OnDragEnter(), OnDragOver(), OnDragLeave(), 和OnDrop()函式,這幾個函式都是虛擬函式,你可以使用MFC類嚮導新增,也可以自己手動新增。

class CWindowView : public CView
 {

    private:

        COleDropTarget m_oleTarget;

         ... ...

    public:

         virtual DROPEFFECT OnDragEnter(COleDataObject* pDataObject, DWORD dwKeyState, CPoint point);
         virtual void OnDragLeave();
         virtual DROPEFFECT OnDragOver(COleDataObject* pDataObject, DWORD dwKeyState, CPoint point);
         virtual BOOL OnDrop(COleDataObject* pDataObject, DROPEFFECT dropEffect, CPoint point);

         ... ...

}

        2.2. 在建立CView時,一定要呼叫COleDropTarget物件的Register()方法,註冊該視窗,使視窗可以接受拖拽。

int CWindowView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    if (CView::OnCreate(lpCreateStruct) == -1)
        return -1;

    // 註冊,
    m_oleTarget.Register(this);


    return 0;
}

       2.3. 相關的虛擬函式的具體實現

DROPEFFECT CWindowView::OnDragEnter(COleDataObject* pDataObject, DWORD dwKeyState, CPoint point)
{
    // 返回值可能決定圖示顯示效果
    return DROPEFFECT_MOVE;
}


void CGeneralUI_CreateWindowView::OnDragLeave()
{
    CView::OnDragLeave();
}


DROPEFFECT CGeneralUI_CreateWindowView::OnDragOver(COleDataObject* pDataObject, DWORD dwKeyState, CPoint point)
{
    return DROPEFFECT_MOVE;
}


BOOL CGeneralUI_CreateWindowView::OnDrop(COleDataObject* pDataObject, DROPEFFECT dropEffect, CPoint point)
{
    // TODO: 在此新增專用程式碼和/或呼叫基類

    HGLOBAL hgMem = NULL;
    char* htmpMem = NULL;

    CString dataBuf_dataName, dataBuf_dataID;
    int typeIndex= 6;

    if (pDataObject->IsDataAvailable(CF_TEXT))
    {
        hgMem = pDataObject->GetGlobalData(CF_TEXT);

        htmpMem = (char*)GlobalLock(hgMem);

        if (htmpMem != NULL)
        {
            typeIndex = *(int*)htmpMem;
            dataBuf_dataID.Format(L"%s", (TCHAR*)(htmpMem + 4));
            dataBuf_dataName.Format(L"%s", (TCHAR*)(htmpMem + 68));

            CWnd* onCtrlPtr = CheckCurSelCtrl(point);

            // 獲取到了資料,根據自己的要求做你自己的相關操作

           AfxMessageBox(dataBuf_dataName + L" " + dataBuf_dataID);

        }
    }

    GlobalUnlock(hgMem);

    return TRUE;
}      

以上就是拖拽功能實現的全部程式碼,希望可以幫到大家。程式碼的優化還請大家多給點意見。

關於參考資料我就不進行羅列了。