如何實現選單的拖拽
阿新 • • 發佈:2019-02-13
為了讓選單支援拖拽,需要做:
1、為選單新增MNS_DRAGDROP風格
MENUINFO Info ;
Info.cbSize = sizeof(MENUINFO) ;
Info.fMask = MIM_STYLE ;
Info.dwStyle= MNS_DRAGDROP ;
SetMenuInfo( hMenu , &Info ) ;
注:當WINVER>=0x500,才能找到NMS_DRAGDROP和下面相關常數的定義
2、處理WM_MENUDRAG訊息
其中, wParam 指定被拖拽選單項的位置,lParam為選單的控制代碼。
返回值如果是MND_CONTINUE,則選單仍然啟用,如果滑鼠被釋放,它將被忽略!
就是說不實現拖拽功能,使用預設的處理方式! 如果是MND_ENDMENU,則選單將關閉。要實現拖拽,要返回該值! 重點在於,在WM_MENUDRAG內要啟動拖操作,這裡我使用了OLE拖-放傳輸協議, 即呼叫 DoDragDrop : -分配記憶體,大小是256位元組。空間的大小要根據實際情況來定!
HGLOBAL hGlobal = ::GlobalAlloc( GMEM_SHARE | GMEM_ZEROINIT , 256 ) ;
-將需的資料儲存到分配的記憶體中,這裡我將儲存選單的文字
LPVOID pvoid = ::GlobalLock( hGlobal ) ; LPTSTR lpstr = (LPTSTR)pvoid ; ::GetMenuString( hMenu , pos , lpstr , MAX_PATH - 1 , MF_BYPOSITION ) ; ::GlobalUnlock( hGlobal ) ;
注:hMenu 來自 lParam , pos 來自 wParam -準備IDataObject、IDropSource物件
STGMEDIUM stgMedium ; memset( &stgMedium , 0 , sizeof(STGMEDIUM) ) ; stgMedium.tymed = TYMED_HGLOBAL ; stgMedium.hGlobal = hGlobal ; stgMedium.pUnkForRelease = NULL ; CMenuDataObject *pDataObj = new CMenuDataObject (&stgMedium, m_uClipboardFormat); CMenuDropSource *pDropSource = new CMenuDropSource ;
pDropSource->AddRef() ;
pDataObj->AddRef() ;
注:CMenuDataObject 派生自 IDataObject ;
CMenuDropSource 派生自 IDropSource
m_uClipboardFormat 是自定義的資料格式,為了能區分出自已的資料。
m_uClipboardFormat= RegisterClipboardFormat( “MyFormat” ) ;
這裡比較麻煩的是CMenuDataObject和CmenuDropSource實現,其不
在本文的討論範圍,請參考相關資料, MSDN的例子等。
-呼叫DoDragDrop 啟動拖操作
DWORD dwEffect = 0 ;
HRESULT ret = ::DoDragDrop( pDataObj , pDropSource ,
DROPEFFECT_MOVE , &dwEffect );
pDropSource->Release() ;
pDataObj->Release() ;
- 檢查返回值,釋放資源
if( ret == DROPEFFECT_MOVE )
{
::GlobalFree( hGlobal ) ;
return MND_ENDMENU ;
}
注:如果你用 MFC,這一切將變得很簡單,IDataObject和IDataSource都由MFC實現:
COleDataSource *pSource = new COleDataSource ;
pSource->CacheData( (CLIPFORMAT)m_uClipboardFormat , &stgMedium ) ;
DROPEFFECT ret = DROPEFFECT_NONE ;
ret = pSource->DoDragDrop( DROPEFFECT_MOVE ) ;
3、實現接收功能
為了接收資料物件即支援“放”特性,它必須提供一個“放目標”物件。“放目標”
物件實現了介面IDropTarget,並且目標程式還必須把“放目標”物件與一個視窗聯絡在一起。因此,應用程式為了支援“放”特性,要呼叫OLE提供的API函式: RegisterDragDrop,當程式不再支援“放”特性,則可以呼叫RevokeDrapDrop函式取消。 OnCreate { … 視窗初始化 RegisterDragDrop( m_hWnd , &m_DropTarget ) ;
} class CMyDropTarget : public IDropTarget ; CMyDropTarget m_DropTarget ; 因此,工作重點,就是實現 IDropTarget 。如果用MFC,可以讓CMyDropTarget 派生自COleDropTarget 。CMyDropTarget實現不在本文討論範圍,請參考相關資料。
就是說不實現拖拽功能,使用預設的處理方式! 如果是MND_ENDMENU,則選單將關閉。要實現拖拽,要返回該值! 重點在於,在WM_MENUDRAG內要啟動拖操作,這裡我使用了OLE拖-放傳輸協議, 即呼叫 DoDragDrop : -分配記憶體,大小是256位元組。空間的大小要根據實際情況來定!
HGLOBAL hGlobal = ::GlobalAlloc( GMEM_SHARE | GMEM_ZEROINIT , 256 ) ;
-將需的資料儲存到分配的記憶體中,這裡我將儲存選單的文字
LPVOID pvoid = ::GlobalLock( hGlobal ) ; LPTSTR lpstr = (LPTSTR)pvoid ; ::GetMenuString( hMenu , pos , lpstr , MAX_PATH - 1 , MF_BYPOSITION ) ; ::GlobalUnlock( hGlobal ) ;
注:hMenu 來自 lParam , pos 來自 wParam -準備IDataObject、IDropSource物件
STGMEDIUM stgMedium ; memset( &stgMedium , 0 , sizeof(STGMEDIUM) ) ; stgMedium.tymed = TYMED_HGLOBAL ; stgMedium.hGlobal = hGlobal ; stgMedium.pUnkForRelease = NULL ; CMenuDataObject *pDataObj = new CMenuDataObject (&stgMedium, m_uClipboardFormat); CMenuDropSource *pDropSource = new CMenuDropSource ;
物件實現了介面IDropTarget,並且目標程式還必須把“放目標”物件與一個視窗聯絡在一起。因此,應用程式為了支援“放”特性,要呼叫OLE提供的API函式: RegisterDragDrop,當程式不再支援“放”特性,則可以呼叫RevokeDrapDrop函式取消。 OnCreate { … 視窗初始化 RegisterDragDrop( m_hWnd , &m_DropTarget ) ;
} class CMyDropTarget : public IDropTarget ; CMyDropTarget m_DropTarget ; 因此,工作重點,就是實現 IDropTarget 。如果用MFC,可以讓CMyDropTarget 派生自COleDropTarget 。CMyDropTarget實現不在本文討論範圍,請參考相關資料。