在MFC中,排除調用API函數那種復雜的方法之外,就只有CMenu這個類可以讓我們來控制菜單了。對於這個類,琢磨了兩天,總算有點心得。
對於系統菜單,創建起來比較簡單,直接使用資源編輯器就能生成菜單,再通過ClassWizard創建菜單命令函數。在我的資源中有一個工程,實現了一個右鍵彈出貼圖菜單。結合這個工程,介紹動態創建菜單、創建彈出式菜單和重繪菜單。
首先介紹基礎知識:
一、CMenu類的成員函數:
1. CreateMenu()和CreatePopupMenu(),這兩個函數用來創建一個菜單實例,CreateMenu()創建的是普通的菜單實例,如果想創建彈出式菜單,就要用CreatePopupMenu()函數。
2. AppendMenu()向菜單中添加一個子項,這個函數有兩個主要的參數。第一個UINT nFlags,這個參數表明了該子項的屬性特征,可以這樣說,這個參數規定了菜單的樣式和功能。後面會詳細講這個參數所能使用的值。第二個參數UINT_PTR nIDNewItem,根據nFlags使用不同的設置,該參數將標明菜單的資源ID或在這個菜單中的索引號。第三個參數可以省略,如果不省略,可以傳入一個字符串,這個字符串將顯示在菜單中(因為我准備用突破表示菜單項,所以我的工程中省略了這個參數)。
3. DrawItem(),這是一個虛函數,如果菜單設置成可以自繪類型,則這個函數將在生成菜單、彈出菜單、選中菜單、點擊菜單等時由系統框架調用。因此,這個函數是一個很有用的函數,它可以幫你繪制出各種樣式的菜單。
4. MeasureItem()也是一個虛函數,當菜單被創建的時候由系統框架調用。這個函數用來設置菜單的大小。
二、nFlags說明:
OLOR: black">只有當nFlags設置成MF_OWNERDRAW的時候,系統框架才會重繪菜單。
MF_CHECKED:命令旁顯示默認復選標志
MF_UNCHECKED:清除命令旁的復選標志
MF_DISABLED:禁止此菜單命令,但是不變灰顯示
MF_ENABLED:允許此菜單命令,恢復到正常狀態
MF_GRAYED:禁止此菜單命令,變灰顯示
MF_MENUBARBREAK:對於靜態菜單,放到新行;對於彈出菜單,放到新欄 中,欄間有分隔線
MF_MENUBREAK:對於靜態菜單,放到新行;對於彈出菜單,放到新欄,欄間霧分隔線
MF_OWNERDRAW:指定該命令是自畫式菜單命令
MF_POPUP:指定該菜單命令有一個關聯的彈出式菜單
MF_SEPARATOR:畫一條水平分隔線,只用於彈出式菜單
MF_STRING:指定此菜單命令是一個字符串
三、開始工程:
1. 創建一個基於對話框的工程。然後添加一個新類,這個新類從CMenu繼承。這個地方值得注意:如果使用ClassWizard添加新類的話,你會發現在基類的列表中找不到CMenu。所以,只能自己創建兩個文件,然後自己寫代碼了。
2. 我們給新類取名CBmpMenu,重載CMenu的DrawItem()和MeasureItem()這兩個虛函數。
頭文件:
class CBmpMenu : public CMenu
{
public:
CBmpMenu();
virtual ~CBmpMenu();
virtual void DrawItem(LPDRAWITEMSTRUCT lpDIS);
virtual void MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct);
};
3.
void CBmpMenu::MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct)
{
lpMeasureItemStruct->item
Width=50; lpMeasureItemStruct->itemHeight=18;}可以看的很清楚,lpMeasureItemStruct->itemWidth表示菜單的寬度, itemHeight表示菜單的高度,重新設置你想設置的值。
4.工程添加了兩組圖片資源,每一組五張,一組表示普通模式下的圖片,一種表示當選中菜單時顯示的圖片。要使菜單彈出時顯示圖片,則重載DrawItem函數,並在DrawItem函數中繪制圖片。
void CBmpMenu::DrawItem(LPDRAWITEMSTRUCT lpDIS)
{
CDC* pDC=CDC::FromHandle(lpDIS->hDC);
CDC dcmem;
dcmem.CreateCompatibleDC(pDC);
CBitmap * bt=new CBitmap;
if((lpDIS->itemState & ODS_SELECTED) &&
(lpDIS->iteMaction & (ODA_SELECT | ODA_DRAWENTIRE)))
{
switch(lpDIS->itemID)
{
case IDR_MENU:
bt->LoadBitmap(MAKEINTRESOURCE(IDB_HOT_50));
break;
case IDR_MENU+1:
bt->LoadBitmap(MAKEINTRESOURCE(IDB_HOT_100));
break;
case IDR_MENU+2:
bt->LoadBitmap(MAKEINTRESOURCE(IDB_HOT_150));
break;
case IDR_MENU+3:
bt->LoadBitmap(MAKEINTRESOURCE(IDB_HOT_200));
break;
case IDR_MENU+4:
bt->LoadBitmap(MAKEINTRESOURCE(IDB_HOT_FULL));
break;
}
}
else
{<
BR> switch(lpDIS->itemID)
{
case IDR_MENU:
bt->LoadBitmap(MAKEINTRESOURCE(IDB_NORMAL_50));
break;
case IDR_MENU+1:
bt->LoadBitmap(MAKEINTRESOURCE(IDB_NORMAL_100));
break;
case IDR_MENU+2:
bt->LoadBitmap(MAKEINTRESOURCE(IDB_NORMAL_150));
break;
case IDR_MENU+3:
bt->LoadBitmap(MAKEINTRESOURCE(IDB_NORMAL_200));
break;
le="COLOR: black"> case IDR_MENU+4:
bt->LoadBitmap(MAKEINTRESOURCE(IDB_NORMAL_FULL));
break;
}
}
dcmem.SelectObject(bt);
pDC->BitBlt(lpDIS->rcItem.left,lpDIS->rcItem.top,61,18,&dcmem,0,0,SRCCOPY);
}
LPDRAWITEMSTRUCT類型的參數包含9個域:
typedef struct tagDRAWITEMSTRUCT {
UINT CtlType;
UINT CtlID;
UINT itemID;
UINT iteMaction;
UINT itemState;
HWND hwndItem;
HDC hDC
an>;
RECT rcItem;
DWord itemData;
} DRAWITEMSTRUCT;
CtlType:表示重繪的對象,因為不光菜單可以重繪,其他的一些資源也可以。ODT_BUTTON、ODT_COMBOBOX 、ODT_LISTBOX 、ODT_MENU 、ODT_LISTVIEW 、ODT_STATIC 、ODT_TAB 。
CtlID:控件資源的ID號,如果對菜單重繪,那麼這個域不使用。
itemID:在這裡表示菜單項的ID。
iteMaction:表示發生什麼動作導致重繪。
itemState:表示當前重繪資源所處的狀態,例如被點擊,被選中。
各種動作和狀態MSDN中做了詳細說明,可在MSDN2003:Visual Studio . Net\Visual C++\ Visual C++參考\ Visual C++ librarIEs\MFC Reference\Structures…\Structures by MFC\ DRAWITEMSTRUCT Structure中找到。
hwndItem:控件窗體的句柄,如果是菜單,則表示菜單的句柄。
hDC:要重繪的區域的設備內容,重繪的所有動作都靠它。
rcItem:重繪區域的坐標。
itemData:添加菜單時設定的值,由於我們省略了AppendMenu()的第三個參數,所以這裡不用關心它。
看完這些介紹,我們大致可以明白如何重新繪制菜單了,也就是使用hDC在rcItem區域繪圖,至於繪制什麼圖,可以根據itemID表示的不同的子項,iteMaction表示的不同的動作,itemState表示子項的不同的狀態來繪圖。
此工程就是根據是否選中菜單『if((lpDIS->itemState & ODS_SELECTED) &&(lpDIS->iteMaction & (ODA_SELECT | ODA_DRAWENTIRE)))』繪制不同狀態的圖。
同時判斷一下ID,switch(lpDIS->itemID)。