1. 程式人生 > >如何實現選單的拖拽

如何實現選單的拖拽

  為了讓選單支援拖拽,需要做: 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” ) ; 這裡比較麻煩的是CMenuDataObjectCmenuDropSource實現,其不 在本文的討論範圍,請參考相關資料,
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,這一切將變得很簡單,IDataObjectIDataSource都由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實現不在本文討論範圍,請參考相關資料。