返回值:如果函數(shù)調(diào)用成功,返回非零值;如果函數(shù)調(diào)用失敗,返回值是零。若想獲得更多的錯(cuò)誤信息,請(qǐng)調(diào)用GetLastError函數(shù)。
備注:窗口被重畫來(lái)反映菜單的修改。函數(shù)SetMenu替換原來(lái)的菜單(如果存在),但并不將其銷毀。應(yīng)用程序必須調(diào)用函數(shù)DestroyMenu來(lái)銷毀菜單。
26.BOOL SetMenuItemBitmaps(UINT nPosition,UINT nFlags,const CBitmap* pBmpUnchecked,const CBitmap* pBmpChecked)
當(dāng)點(diǎn)擊某個(gè)菜單項(xiàng)時(shí),框架類先接受菜單消息,框架類在交給視類處理。如果視類不作處理,則交給文檔類處理。文檔類不處理,則交還給視類,視類
一。創(chuàng)建非Popup類型菜單,不使用資源。
(一)創(chuàng)建非下拉菜單。
1。在窗口類的OnCreate函數(shù)里創(chuàng)建CMenu對(duì)象。如果是創(chuàng)建運(yùn)用程序主框架窗口
的話,也可以在InitInstance()函數(shù)里。
2。聲明一個(gè)CMenu對(duì)象:CMenu MyMenu;
3。調(diào)用MyMenu.CreateMenu()或MyMenu.LoadMenu()
4。調(diào)用若干次MyMenu.AppendMenu()或MyMenu.InsertMenu(),每調(diào)用一次創(chuàng)建一
個(gè)菜單項(xiàng)。
5。調(diào)用MyMneu.SetMenu()將菜單Attach到窗口上。
6。調(diào)用MyMenu.Detach()。
int CMyWnd::OnCreate( LPCREATESTRUCT lpCreateStruct )
{
CMenu MyMenu;
MyMenu.CreateMenu();
MyMenu.AppendMenu(MF_STRING,IDM_MENU0,"文件");
MyMenu.InsertMenu(IDM_MENU2,MF_BYCOMMAND,IDM_ITEM0,"有關(guān)");
this->SetMenu(&MyMenu);
MyMenu.Detach();
return 0;
}
用Detach()使菜單和MyMenu對(duì)象脫離關(guān)系,因?yàn)镸yMenu對(duì)象是一個(gè)局部變量,馬上就要超出作用域了,這一步是必須的。
而SetMenu 是CMyWnd的成員函數(shù),是建立看得見(jiàn)的菜單同CMyWnd::m_cmenu的關(guān)系
DestroyMenu則根據(jù)m_cmenu 銷毀看得見(jiàn)的菜單。
備注:一個(gè)應(yīng)用程序在關(guān)閉之前,必須調(diào)用函數(shù)DestroyMenu來(lái)銷毀一個(gè)沒(méi)被分配給窗口的菜單。分配給窗口的菜單,當(dāng)應(yīng)用程序關(guān)閉時(shí),被自動(dòng)銷毀。
(二)創(chuàng)建下拉菜單,不使用資源。
這種菜單當(dāng)鼠標(biāo)移動(dòng)到菜單條目上面點(diǎn)擊時(shí)不是去執(zhí)行某段程序,而是彈出
一個(gè)下拉菜單。這需要用前面的方法創(chuàng)建兩個(gè)菜單。第一個(gè)是鼠標(biāo)未點(diǎn)擊時(shí)看到
的那個(gè)菜單,另一個(gè)就是扮演下拉菜單的菜單。例子:
int CMyWnd::OnCreate( LPCREATESTRUCT lpCreateStruct )
{
CMenu MyMenu0,MyMenu1;
//下面這幾條創(chuàng)建下拉菜單
MyMenu1.CreateMenu();
MyMenu1.AppendMenu(MF_STRING,IDM_ITEM0,"拷貝");
MyMenu1.AppendMenu(MF_STRING,IDM_ITEM1,"剪切");
MyMenu1.AppendMenu(MF_STRING,IDM_ITEM2,"粘貼");
MyMenu1.AppendMenu(MF_SEPARATOR,IDM_ITEM3,"");
MyMenu1.AppendMenu(MF_STRING,IDM_ITEM4,"全選");
MyMenu1.AppendMenu(MF_SEPARATOR,IDM_ITEM5,"");
MyMenu1.AppendMenu(MF_STRING,IDM_ITEM6,"刪除");
//下面這兩條創(chuàng)建鼠標(biāo)未點(diǎn)擊時(shí)看到的那個(gè)菜單
//其中第二句將下拉菜單張貼到第一個(gè)菜單上。
MyMenu0.CreateMenu();
MyMenu0.AppendMenu(MF_POPUP,(UINT)MyMenu1.m_hMenu,"編輯");
this->SetMenu(&MyMenu0);//將菜單張貼到窗口上
MyMenu0.Detach();//必須有
MyMenu1.Detach();//必須有
return 0;
}
二。創(chuàng)建Popup類型的菜單,也不用資源。
很多程序里,只要用鼠標(biāo)右鍵點(diǎn)一下窗口客戶區(qū),就會(huì)在鼠標(biāo)的位置彈出一
個(gè)菜單,這叫右鍵菜單。我們可以用CMenu類來(lái)制作。
制作這種菜單比制作第一類菜單稍微復(fù)雜點(diǎn)。首先要在窗口類里加個(gè)成員變
量:CMenu *MyMenu2;
然后在窗口類的構(gòu)造函數(shù)里(或OnCreate()函數(shù)里)加上創(chuàng)建菜單的語(yǔ)句,再
在析構(gòu)函數(shù)里加上銷毀菜單的語(yǔ)句,最后在OnRButtonDown()函數(shù)里加上顯示菜單
的語(yǔ)句。
創(chuàng)建菜單時(shí),CMenu類對(duì)象應(yīng)該用new來(lái)分配。
例子:
CMyWnd::CMyWnd()
{
//CMyWnd是從CWnd派生來(lái)的。
//先把菜單創(chuàng)建起來(lái)。
MyMenu2=new CMenu;
MyMenu2->CreatePopupMenu();
MyMenu2->AppendMenu(MF_STRING,IDM_ITEM0,"拷貝");
MyMenu2->AppendMenu(MF_STRING,IDM_ITEM1,"剪切");
MyMenu2->AppendMenu(MF_STRING,IDM_ITEM2,"粘貼");
MyMenu2->AppendMenu(MF_SEPARATOR,IDM_ITEM3,"");
MyMenu2->AppendMenu(MF_STRING,IDM_ITEM4,"全選");
MyMenu2->AppendMenu(MF_SEPARATOR,IDM_ITEM3,"");
MyMenu2->AppendMenu(MF_STRING,IDM_ITEM5,"刪除");
}
CMyWnd::~CMyWnd()
{
MyMenu2->DestroyMenu();//銷毀菜單所占用的系統(tǒng)資源
delete MyMenu2;//銷毀菜單類對(duì)象
}
void CMyWnd::OnRButtonDown(UINT nFlags, CPoint point)
{
RECT rect;
GetWindowRect(&rect);
//顯示菜單
MyMenu2->TrackPopupMenu(TPM_RIGHTALIGN,point.x+rect.left,point.y+
rect.top,this,NULL);
}
三。使用資源編輯器做好的菜單,只能做非POPUP類型菜單。
如果使用資源的話,創(chuàng)建菜單確實(shí)非常簡(jiǎn)單了,只須在窗口類的OnCreate()
函數(shù)里加幾句話就行了:
int CMyWnd::OnCreate( LPCREATESTRUCT lpCreateStruct )
{
CMenu MyMenu3;
MyMenu3.LoadMenu(IDR_MENU1);//IDR_MENU1是你的菜單的資源ID。
this->SetMenu(&MyMenu3); // this == CMyWnd*
MyMenu3.Detach();
return 0;
}
MFC類里CMENU類的成員函數(shù)。功能是切斷一個(gè)CWnd對(duì)象和一個(gè)有效窗口的聯(lián)系。 由于WNDCLASS其實(shí)和CWnd根本沒(méi)有什么關(guān)系,它們之間只是通過(guò)CWnd的成
員HWND聯(lián)系起來(lái)的。Detach的作用是切斷一個(gè)CWnd對(duì)象和一個(gè)有效窗口的聯(lián)系。因?yàn)镃Wnd是C++的對(duì)象,C++的對(duì)象有一個(gè)生存期的概念,脫離了該對(duì)象的作用域,這
個(gè)對(duì)象就要被銷毀,但是Windows對(duì)象沒(méi)有這個(gè)特點(diǎn),當(dāng)銷毀CWnd對(duì)象的時(shí)候,我們不一定希望WNDCLASS一起被銷毀,那么在此之前,我們就先要把這個(gè)聯(lián)系切斷。
當(dāng)我們建立了一個(gè)局部的菜單對(duì)象后,比如 在一個(gè)窗口類的函數(shù)里建立了一個(gè)局部菜單對(duì)象,當(dāng)這個(gè)窗口函數(shù)的生命周期結(jié)束時(shí),如果不希望菜單對(duì)象也被銷毀,就要用
detach()函數(shù)把菜單句柄和這個(gè)菜單對(duì)象分離。這樣,當(dāng)局部的菜單對(duì)象被銷毀時(shí),它不會(huì)銷毀一個(gè)它不具備擁有權(quán)的菜單。
m_menu.CreateMenu();
CString c_menustr;
while (! m_pRecord->ADOEOF)
{
c_menustr = m_pRecord->GetCollect("菜單名稱").bstrVal;
//menu.AppendMenu(MF_STRING,-1,c_menustr);
LoadSubMenu(&m_menu,c_menustr);
m_pRecord->MoveNext();
}
SetMenu(&m_menu);
void LoadSubMenu(CMenu* m_menu,CString str)
{
CMenu m_tempmenu;
m_tempmenu.CreateMenu();
while (!m_record->ADOEOF)
{
CString c_menustr = m_record->GetCollect("菜單名稱").bstrVal;
if(IsHaveSubMenu(c_menustr))
{
LoadSubMenu(&m_tempmenu,c_menustr);
}
else
m_tempmenu.AppendMenu(MF_STRING,-1,c_menustr);
m_record->MoveNext();
}
m_menu->AppendMenu(MF_POPUP,(UINT)m_tempmenu.m_hMenu,str);
m_tempmenu.Detach();
}
自定義菜單時(shí),需要
(1)主窗口
m_menu.AttatchMenu(this->GetMenu()->GetSafeHmenu());
m_menu.ChangeMenuItem(&m_menu,TRUE);
或是m_menu.LoadMenu(IDR_POPUPMENU);
m_menu.ChangeMenuItem(&m_menu);
ON_WM_DRAWITEM()
ON_WM_MEASUREITEM()
afx_msg void OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct);
afx_msg void OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct);
void CMainFrame::OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct) //
{
m_menu.DrawItem(lpDrawItemStruct);
// CFrameWnd::OnDrawItem(nIDCtl, lpDrawItemStruct);
}
void CMainFrame::OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct)
{
m_menu.MeasureItem(lpMeasureItemStruct);
// CFrameWnd::OnMeasureItem(nIDCtl, lpMeasureItemStruct);
}
void CMainFrame::OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct)
lpMeasureItemStruct 是指向MEASUREITEMSTRUCT結(jié)構(gòu)體的指針,其成員變量
UINT CtlType; // 要繪制的類型
UINT itemID; // 菜單選項(xiàng)ID
UINT itemWidth; //菜單選項(xiàng)寬度
UINT itemHeight; //菜單選項(xiàng)高度
void CMainFrame::OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct)
lpDrawItemStruct 是指向DRAWITEMSTRUCT結(jié)構(gòu)體的指針,其成員變量
UINT CtlType; // 要繪制的類型
UINT itemID; // 菜單選項(xiàng)ID
UINT itemAction; // 菜單動(dòng)作
UINT itemState; // 菜單選項(xiàng)的當(dāng)前狀態(tài)
HWND hwndItem; // 頂層菜單的句柄
HDC hDC; // 繪制設(shè)備DC
RECT rcItem; // 菜單選項(xiàng)的大小
DWORD itemData; // 附加自定義數(shù)據(jù),由AppendMenu或InsertMenu或ModifyMenu的lpszNewItem指定
WM_DRAWITEM:繪制菜單的樣式
WM_MEASUREITEM:指定要繪制菜單的大小
WM_INITMENU:把框架菜單全部改成帶MF_OWNERDRAW標(biāo)志
(2)class CIconMenu : public CMenu
struct CMenuItemInfo
{
CString m_ItemText;//菜單項(xiàng)文本
int m_IconIndex;//菜單項(xiàng)索引
int m_ItemID;//菜單標(biāo)記 -2頂層菜單,-1彈出式菜單,0分隔條,其他普通菜單
};
CMenuItemInfo m_ItemLists[MAX_MENUCOUNT]; //菜單項(xiàng)信息
BOOL CIconMenu::AttatchMenu(HMENU m_hmenu)
{
this->Attach(m_hmenu);
return TRUE;
}
BOOL CIconMenu::ChangeMenuItem(CMenu* m_menu,BOOL m_Toped)
{
if (m_menu != NULL)
{
int m_itemcount = m_menu->GetMenuItemCount();
for (int i=0;i<m_itemcount;i++)
{
m_menu->GetMenuString(i,m_ItemLists[m_index].m_ItemText,MF_BYPOSITION);
int m_itemID = m_menu->GetMenuItemID(i);
if (m_itemID==-1 && m_Toped)
{
m_itemID = -2;//頂層菜單
};
m_ItemLists[m_index].m_ItemID = m_itemID;
if (m_itemID>0)
{
m_ItemLists[m_index].m_IconIndex= m_iconindex;
m_iconindex+=1;
}
m_menu->ModifyMenu(i,MF_OWNERDRAW|MF_BYPOSITION |MF_STRING,m_ItemLists[m_index].m_ItemID,(LPSTR)&(m_ItemLists
[m_index]));
m_index+=1;
CMenu* m_subMenu = m_menu->GetSubMenu(i);
if (m_subMenu)
{
ChangeMenuItem(m_subMenu);
}
}
}
return TRUE ;
}
void CIconMenu::MeasureItem( LPMEASUREITEMSTRUCT lpStruct )
void CIconMenu::DrawItem( LPDRAWITEMSTRUCT lpStruct )
響應(yīng)系統(tǒng)菜單按鍵
// 相應(yīng)系統(tǒng)的下拉菜單
void CPeculiarMenuDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if (nID == IDI_PECULIARMENU)
{
MessageBox("系統(tǒng)菜單","提示",MB_OKCANCEL|MB_ICONINFORMATION|MB_DEFBUTTON2);
}
else
{
CDialog::OnSysCommand(nID, lParam);
}
}
獲取系統(tǒng)菜單
CMenu *m_pMenu = GetSystemMenu(FALSE); // GetSystemMenu(); 獲取系統(tǒng)菜單
【VC++】在對(duì)話框中使用ON_UPDATE_COMMAND_UI更新菜單
從命令用戶界面處理函數(shù)(Command UI handler)改變菜單狀態(tài)(啟用/禁用,選擇/取消選擇,更改文字)在由對(duì)話框處理時(shí)沒(méi)有正常工作。
原因:在下拉菜單顯示的時(shí)候, WM_INITMENUPOPUP消息被先發(fā)送以顯示菜單項(xiàng)。MFC CFrameWnd::OnInitMenuPopup 函數(shù)遍歷菜單項(xiàng)并為每個(gè)菜單項(xiàng)調(diào)用更新命令處
理函數(shù)(如果有的話)。菜單的外觀被更新以反映它的狀態(tài)(啟用/禁用,選擇/取消選擇)。更新用戶界面機(jī)制在基于對(duì)話框的應(yīng)用程序中不能工作,因?yàn)镃Dialog沒(méi)有
OnInitMenuPopup 處理函數(shù),而使用CWnd's 默認(rèn)處理函數(shù),該函數(shù)沒(méi)有為菜單項(xiàng)調(diào)用更新命令處理函數(shù)。
解決辦法:
class CTestDlg : public CDialog
{
void OnInitMenuPopup(CMenu *pPopupMenu, UINT nIndex,BOOL bSysMenu);
afx_msg void OnUpdateChkckDelete(CCmdUI* pCmdUI);
afx_msg void OnUpdateChkckCut(CCmdUI* pCmdUI);
afx_msg void OnUpdateChkckCopy(CCmdUI* pCmdUI);
afx_msg void OnUpdateChkckSend(CCmdUI* pCmdUI);
afx_msg void OnUpdateChkckPaste(CCmdUI* pCmdUI);
}
1.在消息映射中添加ON_WM_INITMENUPOPUP項(xiàng)
[cpp] view plaincopy
BEGIN_MESSAGE_MAP(CTestDlg, CDialog)
//{{AFX_MSG_MAP(CTestDlg)
.......
//}}AFX_MSG_MAP
ON_WM_INITMENUPOPUP()
ON_UPDATE_COMMAND_UI(ID_CHKCK_DELETE, OnUpdateChkckDelete)
ON_UPDATE_COMMAND_UI(ID_CHKCK_CUT, OnUpdateChkckCut)
ON_UPDATE_COMMAND_UI(ID_CHKCK_COPY, OnUpdateChkckCopy)
ON_UPDATE_COMMAND_UI(ID_CHKCK_SEND, OnUpdateChkckSend)
ON_UPDATE_COMMAND_UI(ID_CHKCK_PASTE, OnUpdateChkckPaste)
END_MESSAGE_MAP()
2.在你的對(duì)話框類中添加OnInitMenuPopup成員函數(shù)且復(fù)制下列代碼到該函數(shù)(注意:代碼基本上是從CFrameWnd::OnInitMenuPopup(在WinFrm.cpp中)復(fù)制過(guò)來(lái)的)。
[cpp] view plaincopy
void CTestDlg::OnInitMenuPopup(CMenu *pPopupMenu, UINT nIndex,BOOL bSysMenu)
{
ASSERT(pPopupMenu != NULL);
CCmdUI state;
state.m_pMenu = pPopupMenu;
ASSERT(state.m_pOther == NULL);
ASSERT(state.m_pParentMenu == NULL);
HMENU hParentMenu;
if (AfxGetThreadState()->m_hTrackingMenu == pPopupMenu->m_hMenu)
state.m_pParentMenu = pPopupMenu;
else if ((hParentMenu = ::GetMenu(m_hWnd)) != NULL)
{
CWnd* pParent = this;
if (pParent != NULL &&
(hParentMenu = ::GetMenu(pParent->m_hWnd)) != NULL)
{
int nIndexMax = ::GetMenuItemCount(hParentMenu);
for (int nIndex = 0; nIndex < nIndexMax; nIndex++)
{
if (::GetSubMenu(hParentMenu, nIndex) == pPopupMenu->m_hMenu)
{
state.m_pParentMenu = CMenu::FromHandle(hParentMenu);
break;
}
}
}
}
state.m_nIndexMax = pPopupMenu->GetMenuItemCount();
for (state.m_nIndex = 0; state.m_nIndex < state.m_nIndexMax;
state.m_nIndex++)
{
state.m_nID = pPopupMenu->GetMenuItemID(state.m_nIndex);
if (state.m_nID == 0)
continue;
ASSERT(state.m_pOther == NULL);
ASSERT(state.m_pMenu != NULL);
if (state.m_nID == (UINT)-1)
{
state.m_pSubMenu = pPopupMenu->GetSubMenu(state.m_nIndex);
if (state.m_pSubMenu == NULL ||
(state.m_nID = state.m_pSubMenu->GetMenuItemID(0)) == 0 ||
state.m_nID == (UINT)-1)
{
continue;
}
state.DoUpdate(this, TRUE);
}
else
{
state.m_pSubMenu = NULL;
state.DoUpdate(this, FALSE);
}
UINT nCount = pPopupMenu->GetMenuItemCount();
if (nCount < state.m_nIndexMax)
{
state.m_nIndex -= (state.m_nIndexMax - nCount);
while (state.m_nIndex < nCount &&
pPopupMenu->GetMenuItemID(state.m_nIndex) == state.m_nID)
{
state.m_nIndex++;
}
}
state.m_nIndexMax = nCount;
}
}
void CChkDlgWnd::OnUpdateChkckPaste(CCmdUI* pCmdUI)
{
CWnd* pWnd = GetFocus();
if(pWnd)
{
if( pWnd->IsKindOf( RUNTIME_CLASS(CChkckTreeView) ) )
{
m_pTreeView->OnUpdateChkckPaste(pCmdUI);
}
}
}