(孫鑫)菜單的原理及編寫
孫鑫VC視頻筆記
新建一個工程->MFCAppWizard[exe]->工程名Menu->單文檔應用程序
彈出菜單不能用來命令響應
在資源裏面,增加一個菜單項,爲它改了一個新id:IDM_MYMENU,並做消息響應。
在左邊object ids選擇IDM_MYMENU,在消息上面選COMMAND,在CMainFrame這個類增加消息響應函數。
===
消息的分類
對一個消息來說,我們將它們分成3類:標準消息,命令消息,通告消息。
[標準消息]:除WM_COMMAND之外,所有以WM_開頭的消息。
凡是從CWnd派生的類,纔可以接收[標準消息]。例如:WM_CHAR,WM_CREATE,WM_PAINT。
當然也能接受[命令消息]和[通告消息]。
[命令消息]:來自菜單、加速鍵或工具欄按鈕的消息。這類消息都以WM_COMMAND呈現。
在MFC中,通過菜單項的標識(ID)來區分不同的命令消息;
在SDK中,通過消息的wParam參數識別。
凡是從CCmdTarget派生的類,都可以接收[命令消息],[通告消息]。
[通告消息]:由控件產生的消息,例如,按鈕的單擊.這類消息也是以WM_COMMAND形式呈現.
所以在程序中App類和Doc類,都是從CCmdTarget派生的,所以可以接受菜單的[命令消息]。
但是這兩個類不能接受[標準消息]。
===
創建標記菜單
如果要在“文件”->“新建”的菜單項上創建標記,那麼我們可以選擇在CMainFrame::OnCreate下進行。
像[文件],[編輯],[查看],[幫助],我們叫它們爲“子菜單”。
如果要對菜單編程,就好像我們找一個房間一樣,我們首先找到整棟樓,然後再確定它在的樓層(它在多小
樓),最後找到它的房間。
如果要在“文件”->“新建”的菜單項上創建標記,首先需要找到菜單欄,然後找到它的子菜單(文件),
最後找到它的菜單項(新建)。
---
首先要獲取菜單欄,那麼要獲取一個指向菜單欄對象的指針,也就是框架窗口的,因爲菜單也是屬於框架窗
口,可以在框架窗口的利用一個方法CMenu* GetMenu( ) const;獲取菜單欄的指針。
利用這個函數GetMenu;就可以返回,得到一個指向CMenu對象的指針。
在CMenu的一個函數GetSubMenu,能夠讓我們獲取子菜單。CMenu* GetSubMenu( int nPos ) const;
當我們獲取到子菜單(找到它的樓層),之後就要對(它的房間)操作了。要做標記的菜單,
可以用CMenu::CheckMenuItem。(放置,或移走一個標記,在菜單項上)
UINT CheckMenuItem( UINT nIDCheckItem, UINT nCheck );
第一個參數取值的含義,由第二個參數決定。當第二個參數取數值爲MF_BYCOMMAND的時候,就標識第一個參
數取菜單的ID號;取值爲MF_BYPOSITION的時候,第一個參數就要取菜單的索引號。
MF_CHECKED(標記) or MF_UNCHECKED(不標記) with MF_BYPOSITION(索引號) or MF_BYCOMMAND(菜
單的ID號)。可以進行不同的組合(MF_CHECKED | MF_BYPOSITION;MF_CHECKED | MF_BYCOMMAND)。
---
給菜單項打上標記
在CMainFrame::OnCreate輸入以下代碼
GetMenu()->GetSubMenu(0)->CheckMenuItem(0,MF_BYPOSITION | MF_CHECKED);
//GetMenu是CWnd的成員函數,可以直接調用,它返回整個菜單欄的指針。
//GetSubMenu獲取子菜單“新建”。CheckMenuItem爲菜單項“新建”打上標記。
GetMenu()->GetSubMenu(0)->CheckMenuItem(ID_FILE_NEW,MF_BYCOMMAND | MF_CHECKED);
//作用同上,只不過用“新建”菜單項的ID號訪問。
===
設置缺省菜單項(字體會變粗)
利用CMenu的成員函數SetDefaultItem。
BOOL SetDefaultItem( UINT uItem, BOOL fByPos = FALSE );
UINT uItem,也是兩種訪問方式,用索引號訪問,第二個參數就設置TRUE;如果用ID號訪問,則爲FALSE。
在CMainFrame::OnCreate輸入以下代碼
GetMenu()->GetSubMenu(0)->SetDefaultItem(1,TRUE);//兩種訪問方式,作用一樣,這個爲索引號訪問
GetMenu()->GetSubMenu(0)->SetDefaultItem(ID_FILE_OPEN);
//這個爲ID號訪問,第二個參數FALSE爲缺省值,可以不寫。
===
圖形標記菜單
利用CMenu的成員函數SetMenuItemBitmaps。
BOOL SetMenuItemBitmaps( UINT nPosition, UINT nFlags, const CBitmap* pBmpUnchecked, const
CBitmap* pBmpChecked );
第二個參數選擇MF_BYCOMMAND,第一個參數要填ID號;第二個參數選擇MF_BYPOSITION,第一個參數要填索
引號。後面兩個參數都是CBitmap的指針
在CMainFrame::OnCreate輸入以下代碼(自己先創建好新的位圖IDB_BITMAP1,注意背景色不要是白色的)
m_bitmap.LoadBitmap(IDB_BITMAP1);//加載位圖,m_bitmap爲CMainFrame成員函數,權限爲private。
GetMenu()->GetSubMenu(0)->SetMenuItemBitmaps(0,MF_BYPOSITION,&m_bitmap,&m_bitmap);
===
屏蔽菜單的功能,讓它不能用(屏蔽“打開”菜單項的功能,讓它不顯示“打開”對話框)
可以利用一個函數EnableMenuItem。
UINT EnableMenuItem( UINT nIDEnableItem, UINT nEnable );
首先需要在CMainFrame的構造函數中,設置m_bAutoMenuEnable=FALSE;
(因爲msdn說明: NOTE: m_bAutoMenuEnable is set to FALSE in the constructor of CMainFrame)
這句話又是什麼意思?在MFC中,一個菜單能使用和不能使用,MFC給我們提供了命令更新的機制。
所有菜單的更新,都是由這種機制完成的。
在CMainFrame::OnCreate輸入以下代碼:
GetMenu()->GetSubMenu(0)->EnableMenuItem(1,MF_BYPOSITION | MF_DISABLED | MF_GRAYED);
//MF_DISABLED屏蔽菜單項的功能,MF_GRAYED讓菜單項變灰
===
取消和加載菜單(可以方便的設置,在資源裏面自己定義好的菜單,讓程序沒有菜單或設置新的菜單)
可以利用一個函數SetMenu(CWnd的)
BOOL SetMenu( CMenu* pMenu );
pMenu
Identifies the new menu. If this parameter is NULL, the current menu is removed
標識一個新的菜單,如果參數爲NULL,當前菜單被移走。
在CMainFrame::OnCreate輸入以下代碼:
SetMenu(NULL);//移走當前所有菜單
CMenu menu;
menu.LoadMenu(IDR_MAINFRAME);//加載自己定義的菜單,要先在資源裏面創建自定義的菜單
SetMenu(&menu);
menu.Detach();//將菜單句柄和c++對象斷開,這樣CMenu定義的局部變量在析構的時候,它就不會銷燬菜單
===
***MFC對菜單項採用的“命令更新機制”(菜單項的ID在消息響應裏面,捕獲UPDATE_COMMAND_UI)
這個機制,可以方便,對菜單項進行修改。(屏蔽菜單項,讓它能使用或不能使用)
菜單項狀態的維護是依賴於UPDATE_COMMAND_UI消息,誰(菜單項的ID號)捕獲UPDATE_COMMAND_UI消息,
MFC就在其中創建一個CCmdUI對象。
在某個菜單項ID號捕獲的消息響應函數中(如:void CMainFrame::OnUpdateEditCut(CCmdUI* pCmdUI) )
寫入如下代碼:
pCmdUI->Enable();//缺省值爲TRUE,可以不填,如爲FALSE,那個菜單項就不能使用。
===
增加右鍵彈出菜單
第一種方法:讓MFC幫我們加
選工程(Project)->增加到工程(Add to Project)->組件和控件(Components and Controls)
->visual c++ components->添加pop-up menu->增加pop-up menu到CMenuView類中。
第二種方法:手動自己創建(在view類)
1 首先在資源裏編輯一個新的菜單。
2 既然你是右鍵點擊之後彈出菜單,所以第一步就要捕獲右鍵點擊的消息WM_RBUTTONDOWN(在view類)。
3 在右鍵點擊的消息響應函數OnRButtonDown中添加如下代碼:
CMenu menu;
menu.LoadMenu(IDR_MENU1);//加載你新建的菜單資源
CMenu *pPopup=menu.GetSubMenu(0);//獲取子菜單,當然對於彈出菜單,它只有一個菜單項
ClientToScreen(&point);//客服區座標轉換成屏幕座標
pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y,this);
//創建右鍵彈出菜單
---
給彈出菜單的菜單項,做消息響應
點擊菜單項的ID->建立類嚮導(Classwizard),之後會用對話框的一些選項,別理它,選取消。
->在view類裏面,增加COMMAND
===
***如何動態添加/刪除/插入菜單
把工程關了。創建一個新的工程->MFCAppWizard[exe]->工程名Menu2->單文檔應用程序。
之前我們添加菜單,是通過資源編輯器,自己手動添加的菜單。現在我們要通過代碼,動態的添加菜單。
可以在CMainFrame::OnCreate中,動態增加菜單項。
要動態增加菜單或菜單項,可以利用CMenu的成員函數AppendMenu。
BOOL AppendMenu( UINT nFlags, UINT nIDNewItem = 0, LPCTSTR lpszNewItem = NULL );
第一個參數添加菜單的類型,第二個參數指示新的菜單項的命令ID,第三個參數爲菜單名稱。
(詳細看MSDN)
---
添加彈出菜單(子菜單)
可以利用CMenu的成員函數CreatePopupMenu。
在CMainFrame::OnCreate中添加以下代碼:
CMenu menu;
menu.CreatePopupMenu();//創建一個空的彈出菜單
GetMenu()->AppendMenu(MF_POPUP,(UINT)menu.m_hMenu,"WinSun");
//獲取菜單欄指針,然後將彈出菜單增加到,現有菜單的後面
menu.Detach();//將菜單句柄和c++對象斷開,這樣CMenu定義的局部變量在析構的時候,它就不會銷燬菜單
===
插入彈出菜單
@在彈出菜單裏面,增加菜單項。(同樣可以採用AppendMenu)
如果想在“編輯”和“查看”的兩個彈出菜單,插入新的彈出菜單。
可以利用CMenu的成員函數InsertMenu。
BOOL InsertMenu( UINT nPosition, UINT nFlags, UINT nIDNewItem = 0, LPCTSTR lpszNewItem = NULL
);
第一個參數:插入之後菜單項所在的ID號或索引號,由第二個參數決定訪問方式;
第二個參數:菜單的訪問方式,ID號或索引號;還有要要插入的菜單類型
第三個參數:如果nFlags參數設置爲MF_POPUP, the menu handle (HMENU) of the pop-up menu.
第四個參數:菜單的名稱
在CMainFrame::OnCreate中添加以下代碼:
CMenu menu;
menu.CreatePopupMenu();//創建一個空的彈出菜單
GetMenu()->InsertMenu(2,MF_BYPOSITION | MF_POPUP,(UINT)menu.m_hMenu,"WinSun");
menu.AppendMenu(MF_STRING,111,"Hello");
//第一個參數指明,要增加的類型是菜單項;第二個參數爲菜單項ID,你可以隨便寫;第三個參數爲它名稱
menu.AppendMenu(MF_STRING,112,"Weixin");//增加菜單項
menu.AppendMenu(MF_STRING,113,"Mybole");//同上
menu.Detach();//將菜單句柄和c++對象斷開,這樣CMenu定義的局部變量在析構的時候,它就不會銷燬菜單
//如果要在其他的彈出菜單,怎樣增加菜單項?
GetMenu()->GetSubMenu(你要增加菜單項的那個菜單,的索引號)->AppendMenu(MF_STRING,114,"Mybole");
//如果要在其他的彈出菜單,它的菜單項之間,怎樣增加菜單項?
GetMenu()->GetSubMenu(0)->InsertMenu(ID_FILE_OPEN,MF_STRING | MF_BYCOMMAND,115,"Mybole123");
===
刪除子菜單(“編輯”)
可以利用CMenu的成員函數DeleteMenu.刪除指定菜單項,或者彈出菜單。
就看你用什麼,調用DeleteMenu。如果用菜單欄的指針GetMenu()調用DeleteMenu,刪除的是彈出菜單,如
果你用子菜單GetSubMenu的指針,調用DeleteMenu,刪除的是菜單項。
BOOL DeleteMenu( UINT nPosition, UINT nFlags );
參數:菜單項或彈出菜單,所在的ID號或索引號,由第二個參數決定訪問方式;
在CMainFrame::OnCreate中添加以下代碼:
GetMenu()->DeleteMenu(1,MF_BYPOSITION);//刪除彈出菜單
GetMenu()->GetSubMenu(1)->DeleteMenu(ID_FILE_SAVE,MF_BYCOMMAND);//刪除指定菜單項
===
如何爲動態增加的菜單,或菜單項做命令響應
我們在資源編輯器,增加的菜單可以通過類嚮導,做命令響應。那麼動態增加的菜單怎麼辦了?
當然,我們可以通過手動添加。
首先創建菜單的資源ID,在File View頭文件Resource.h,如下格式:
#define IDM_... 111
#define IDM_... 112
做一個菜單命令的響應函數,它的添加和消息映射是一樣的。
3個步驟:
1 在類的頭文件(這裏是CMainFrm.h),做命令函數的原型。
DECLARE_MESSAGE_MAP()之前,//{{AFX_MSG(CMainFrame)和//}}AFX_MSG之後:afx_msg void OnHello();
2 消息原型寫完,接下來就是消息映射。在源文件(.cpp)當中,在BEGIN_MESSAGE_MAP和END_MESSAGE_MAP()
之間,加入:(對於命令消息,我們通過ON_COMMAND這個宏來完成)ON_COMMAND(IDM_HELLO,OnHello)
3 消息響應函數的實現部分:在最後面寫(#endif //_DEBUG的後面)
void CMainFrame::OnHello
{
//響應代碼......
}
===
編寫一個電話本程序
我們想要完成的功能:鍵盤輸入人名,空格之後+上電話號碼,回車之後,把人名添加到菜單項。當點擊彈
出菜單時PhoneBook,把人名和電話號碼顯示在屏幕。
1 首先在view類捕獲WM_CHAR消息。
2 在view類,增加4個成員變量:第一個:類型int,名稱m_nIndex,權限private;
第二個:類型CMenu,名稱m_menu,權限private;
第三個:類型CString,名稱m_strLine,權限private;//存儲輸入的字符串
第四個:類型CStringArray,名稱m_strArray,權限,public。
3 在view類的構造函數賦初值,m_nIndex=-1;m_strLine="";(將字符串初始化爲空)
4 在View::OnChar中添加如下代碼。(所有的鍵盤輸入,都會通過OnChar這個消息來捕獲)
CClientDC dc(this);
if(0x0d==nChar)//如果回車等於nChar
{
if(0==++m_nIndex)//如果是第一次,就創建菜單
{
m_menu.CreatePopupMenu();//創建彈出菜單
GetParent()->GetMenu()->AppendMenu(MF_POPUP,
(UINT)m_menu.m_hMenu,"PhoneBook");
//將彈出菜單,添加到菜單欄上,注意要先獲取框架類的指針
GetParent()->DrawMenuBar();//重繪改變的菜單欄,否則新增的彈出菜單有問題
}
m_menu.AppendMenu(MF_STRING,IDM_PHONE1+m_nIndex,
m_strLine.Left(m_strLine.Find(' ')));
//增加菜單項,以輸入的人名爲菜單項名字,m_strLine.Find查找空格,m_strLine.Left是截取空格左邊的
字符串(即人名)
m_strArray.Add(m_strLine);//用CStrArray保存CString對象,這樣人名和號碼都保存在
這個集合類上面
m_strLine.Empty();//清空上次輸入的字符串
Invalidate();//擦除先前的文字,防止重疊顯示。這個函數讓整個客服端區域背景無效
}
else
{
m_strLine+=nChar;
dc.TextOut(0,0,m_strLine);//輸出字符串
}
5 你點菜單項的時候,要顯示人名和電話號碼,所以要對菜單項進行響應
(一)先在資源編輯器上添加一個菜單,其彈出的菜單項ID號分別爲IDM_PHONE1, IDM_PHONE2, IDM_PHONE3
(二)在Rsource.h中添加
#define IDM_PHONE1 201
#define IDM_PHONE2 202
#define IDM_PHONE3 203
(三)回到菜單上面的資源編輯器,利用ClassWizard給菜單項添加響應函數,並編輯函數。
在view類,增加的命令響應函數COMMAND。
(四)最後回到菜單的資源編輯器,把先前增加的菜單刪除。
(五)把剛纔加的,在view類構造函數的消息響應函數ON_COMMAND(IDM_PHONE3, OnPhone3),拿到2個註釋
宏的外面,即END_MESSAGE_MAP()的前面,否則會有一些不必要的麻煩。
6 在新增加的響應函數(第一種方法),寫上以下代碼:
第一個OnPhone1:
CClientDC dc(this);
dc.TextOut(0,0,m_strArray.GetAt(1);
第二個OnPhone2:
CClientDC dc(this);
dc.TextOut(0,0,m_strArray.GetAt(2);
第二個OnPhone3:
CClientDC dc(this);
dc.TextOut(0,0,m_strArray.GetAt(3);
由框架類捕獲菜單響應(第二種方法),而不是由View處理,可以節約代碼,而且更聰明
首先在框架類,增加虛函數OnCommand。
在其中添加如下代碼:
int MenuCmdId=LOWORD(wParam);
CMenu2View *pView=(CMenu2View*)GetActiveView();//獲取視類指針GetActiveView。
if(MenuCmdId>=IDM_PHONE1 && MenuCmdId<IDM_PHONE1+pView->m_strArray.GetSize())
{
CClientDC dc(pView);
dc.TextOut(0,0,pView->m_strArray.GetAt(MenuCmdId-IDM_PHONE1));
return TRUE;
}
備註:記得在MainFrm.cpp添加頭文件:#include "Menu2Doc.h";#include "Menu2View"
新建一個工程->MFCAppWizard[exe]->工程名Menu->單文檔應用程序
彈出菜單不能用來命令響應
在資源裏面,增加一個菜單項,爲它改了一個新id:IDM_MYMENU,並做消息響應。
在左邊object ids選擇IDM_MYMENU,在消息上面選COMMAND,在CMainFrame這個類增加消息響應函數。
===
消息的分類
對一個消息來說,我們將它們分成3類:標準消息,命令消息,通告消息。
[標準消息]:除WM_COMMAND之外,所有以WM_開頭的消息。
凡是從CWnd派生的類,纔可以接收[標準消息]。例如:WM_CHAR,WM_CREATE,WM_PAINT。
當然也能接受[命令消息]和[通告消息]。
[命令消息]:來自菜單、加速鍵或工具欄按鈕的消息。這類消息都以WM_COMMAND呈現。
在MFC中,通過菜單項的標識(ID)來區分不同的命令消息;
在SDK中,通過消息的wParam參數識別。
凡是從CCmdTarget派生的類,都可以接收[命令消息],[通告消息]。
[通告消息]:由控件產生的消息,例如,按鈕的單擊.這類消息也是以WM_COMMAND形式呈現.
所以在程序中App類和Doc類,都是從CCmdTarget派生的,所以可以接受菜單的[命令消息]。
但是這兩個類不能接受[標準消息]。
===
創建標記菜單
如果要在“文件”->“新建”的菜單項上創建標記,那麼我們可以選擇在CMainFrame::OnCreate下進行。
像[文件],[編輯],[查看],[幫助],我們叫它們爲“子菜單”。
如果要對菜單編程,就好像我們找一個房間一樣,我們首先找到整棟樓,然後再確定它在的樓層(它在多小
樓),最後找到它的房間。
如果要在“文件”->“新建”的菜單項上創建標記,首先需要找到菜單欄,然後找到它的子菜單(文件),
最後找到它的菜單項(新建)。
---
首先要獲取菜單欄,那麼要獲取一個指向菜單欄對象的指針,也就是框架窗口的,因爲菜單也是屬於框架窗
口,可以在框架窗口的利用一個方法CMenu* GetMenu( ) const;獲取菜單欄的指針。
利用這個函數GetMenu;就可以返回,得到一個指向CMenu對象的指針。
在CMenu的一個函數GetSubMenu,能夠讓我們獲取子菜單。CMenu* GetSubMenu( int nPos ) const;
當我們獲取到子菜單(找到它的樓層),之後就要對(它的房間)操作了。要做標記的菜單,
可以用CMenu::CheckMenuItem。(放置,或移走一個標記,在菜單項上)
UINT CheckMenuItem( UINT nIDCheckItem, UINT nCheck );
第一個參數取值的含義,由第二個參數決定。當第二個參數取數值爲MF_BYCOMMAND的時候,就標識第一個參
數取菜單的ID號;取值爲MF_BYPOSITION的時候,第一個參數就要取菜單的索引號。
MF_CHECKED(標記) or MF_UNCHECKED(不標記) with MF_BYPOSITION(索引號) or MF_BYCOMMAND(菜
單的ID號)。可以進行不同的組合(MF_CHECKED | MF_BYPOSITION;MF_CHECKED | MF_BYCOMMAND)。
---
給菜單項打上標記
在CMainFrame::OnCreate輸入以下代碼
GetMenu()->GetSubMenu(0)->CheckMenuItem(0,MF_BYPOSITION | MF_CHECKED);
//GetMenu是CWnd的成員函數,可以直接調用,它返回整個菜單欄的指針。
//GetSubMenu獲取子菜單“新建”。CheckMenuItem爲菜單項“新建”打上標記。
GetMenu()->GetSubMenu(0)->CheckMenuItem(ID_FILE_NEW,MF_BYCOMMAND | MF_CHECKED);
//作用同上,只不過用“新建”菜單項的ID號訪問。
===
設置缺省菜單項(字體會變粗)
利用CMenu的成員函數SetDefaultItem。
BOOL SetDefaultItem( UINT uItem, BOOL fByPos = FALSE );
UINT uItem,也是兩種訪問方式,用索引號訪問,第二個參數就設置TRUE;如果用ID號訪問,則爲FALSE。
在CMainFrame::OnCreate輸入以下代碼
GetMenu()->GetSubMenu(0)->SetDefaultItem(1,TRUE);//兩種訪問方式,作用一樣,這個爲索引號訪問
GetMenu()->GetSubMenu(0)->SetDefaultItem(ID_FILE_OPEN);
//這個爲ID號訪問,第二個參數FALSE爲缺省值,可以不寫。
===
圖形標記菜單
利用CMenu的成員函數SetMenuItemBitmaps。
BOOL SetMenuItemBitmaps( UINT nPosition, UINT nFlags, const CBitmap* pBmpUnchecked, const
CBitmap* pBmpChecked );
第二個參數選擇MF_BYCOMMAND,第一個參數要填ID號;第二個參數選擇MF_BYPOSITION,第一個參數要填索
引號。後面兩個參數都是CBitmap的指針
在CMainFrame::OnCreate輸入以下代碼(自己先創建好新的位圖IDB_BITMAP1,注意背景色不要是白色的)
m_bitmap.LoadBitmap(IDB_BITMAP1);//加載位圖,m_bitmap爲CMainFrame成員函數,權限爲private。
GetMenu()->GetSubMenu(0)->SetMenuItemBitmaps(0,MF_BYPOSITION,&m_bitmap,&m_bitmap);
===
屏蔽菜單的功能,讓它不能用(屏蔽“打開”菜單項的功能,讓它不顯示“打開”對話框)
可以利用一個函數EnableMenuItem。
UINT EnableMenuItem( UINT nIDEnableItem, UINT nEnable );
首先需要在CMainFrame的構造函數中,設置m_bAutoMenuEnable=FALSE;
(因爲msdn說明: NOTE: m_bAutoMenuEnable is set to FALSE in the constructor of CMainFrame)
這句話又是什麼意思?在MFC中,一個菜單能使用和不能使用,MFC給我們提供了命令更新的機制。
所有菜單的更新,都是由這種機制完成的。
在CMainFrame::OnCreate輸入以下代碼:
GetMenu()->GetSubMenu(0)->EnableMenuItem(1,MF_BYPOSITION | MF_DISABLED | MF_GRAYED);
//MF_DISABLED屏蔽菜單項的功能,MF_GRAYED讓菜單項變灰
===
取消和加載菜單(可以方便的設置,在資源裏面自己定義好的菜單,讓程序沒有菜單或設置新的菜單)
可以利用一個函數SetMenu(CWnd的)
BOOL SetMenu( CMenu* pMenu );
pMenu
Identifies the new menu. If this parameter is NULL, the current menu is removed
標識一個新的菜單,如果參數爲NULL,當前菜單被移走。
在CMainFrame::OnCreate輸入以下代碼:
SetMenu(NULL);//移走當前所有菜單
CMenu menu;
menu.LoadMenu(IDR_MAINFRAME);//加載自己定義的菜單,要先在資源裏面創建自定義的菜單
SetMenu(&menu);
menu.Detach();//將菜單句柄和c++對象斷開,這樣CMenu定義的局部變量在析構的時候,它就不會銷燬菜單
===
***MFC對菜單項採用的“命令更新機制”(菜單項的ID在消息響應裏面,捕獲UPDATE_COMMAND_UI)
這個機制,可以方便,對菜單項進行修改。(屏蔽菜單項,讓它能使用或不能使用)
菜單項狀態的維護是依賴於UPDATE_COMMAND_UI消息,誰(菜單項的ID號)捕獲UPDATE_COMMAND_UI消息,
MFC就在其中創建一個CCmdUI對象。
在某個菜單項ID號捕獲的消息響應函數中(如:void CMainFrame::OnUpdateEditCut(CCmdUI* pCmdUI) )
寫入如下代碼:
pCmdUI->Enable();//缺省值爲TRUE,可以不填,如爲FALSE,那個菜單項就不能使用。
===
增加右鍵彈出菜單
第一種方法:讓MFC幫我們加
選工程(Project)->增加到工程(Add to Project)->組件和控件(Components and Controls)
->visual c++ components->添加pop-up menu->增加pop-up menu到CMenuView類中。
第二種方法:手動自己創建(在view類)
1 首先在資源裏編輯一個新的菜單。
2 既然你是右鍵點擊之後彈出菜單,所以第一步就要捕獲右鍵點擊的消息WM_RBUTTONDOWN(在view類)。
3 在右鍵點擊的消息響應函數OnRButtonDown中添加如下代碼:
CMenu menu;
menu.LoadMenu(IDR_MENU1);//加載你新建的菜單資源
CMenu *pPopup=menu.GetSubMenu(0);//獲取子菜單,當然對於彈出菜單,它只有一個菜單項
ClientToScreen(&point);//客服區座標轉換成屏幕座標
pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y,this);
//創建右鍵彈出菜單
---
給彈出菜單的菜單項,做消息響應
點擊菜單項的ID->建立類嚮導(Classwizard),之後會用對話框的一些選項,別理它,選取消。
->在view類裏面,增加COMMAND
===
***如何動態添加/刪除/插入菜單
把工程關了。創建一個新的工程->MFCAppWizard[exe]->工程名Menu2->單文檔應用程序。
之前我們添加菜單,是通過資源編輯器,自己手動添加的菜單。現在我們要通過代碼,動態的添加菜單。
可以在CMainFrame::OnCreate中,動態增加菜單項。
要動態增加菜單或菜單項,可以利用CMenu的成員函數AppendMenu。
BOOL AppendMenu( UINT nFlags, UINT nIDNewItem = 0, LPCTSTR lpszNewItem = NULL );
第一個參數添加菜單的類型,第二個參數指示新的菜單項的命令ID,第三個參數爲菜單名稱。
(詳細看MSDN)
---
添加彈出菜單(子菜單)
可以利用CMenu的成員函數CreatePopupMenu。
在CMainFrame::OnCreate中添加以下代碼:
CMenu menu;
menu.CreatePopupMenu();//創建一個空的彈出菜單
GetMenu()->AppendMenu(MF_POPUP,(UINT)menu.m_hMenu,"WinSun");
//獲取菜單欄指針,然後將彈出菜單增加到,現有菜單的後面
menu.Detach();//將菜單句柄和c++對象斷開,這樣CMenu定義的局部變量在析構的時候,它就不會銷燬菜單
===
插入彈出菜單
@在彈出菜單裏面,增加菜單項。(同樣可以採用AppendMenu)
如果想在“編輯”和“查看”的兩個彈出菜單,插入新的彈出菜單。
可以利用CMenu的成員函數InsertMenu。
BOOL InsertMenu( UINT nPosition, UINT nFlags, UINT nIDNewItem = 0, LPCTSTR lpszNewItem = NULL
);
第一個參數:插入之後菜單項所在的ID號或索引號,由第二個參數決定訪問方式;
第二個參數:菜單的訪問方式,ID號或索引號;還有要要插入的菜單類型
第三個參數:如果nFlags參數設置爲MF_POPUP, the menu handle (HMENU) of the pop-up menu.
第四個參數:菜單的名稱
在CMainFrame::OnCreate中添加以下代碼:
CMenu menu;
menu.CreatePopupMenu();//創建一個空的彈出菜單
GetMenu()->InsertMenu(2,MF_BYPOSITION | MF_POPUP,(UINT)menu.m_hMenu,"WinSun");
menu.AppendMenu(MF_STRING,111,"Hello");
//第一個參數指明,要增加的類型是菜單項;第二個參數爲菜單項ID,你可以隨便寫;第三個參數爲它名稱
menu.AppendMenu(MF_STRING,112,"Weixin");//增加菜單項
menu.AppendMenu(MF_STRING,113,"Mybole");//同上
menu.Detach();//將菜單句柄和c++對象斷開,這樣CMenu定義的局部變量在析構的時候,它就不會銷燬菜單
//如果要在其他的彈出菜單,怎樣增加菜單項?
GetMenu()->GetSubMenu(你要增加菜單項的那個菜單,的索引號)->AppendMenu(MF_STRING,114,"Mybole");
//如果要在其他的彈出菜單,它的菜單項之間,怎樣增加菜單項?
GetMenu()->GetSubMenu(0)->InsertMenu(ID_FILE_OPEN,MF_STRING | MF_BYCOMMAND,115,"Mybole123");
===
刪除子菜單(“編輯”)
可以利用CMenu的成員函數DeleteMenu.刪除指定菜單項,或者彈出菜單。
就看你用什麼,調用DeleteMenu。如果用菜單欄的指針GetMenu()調用DeleteMenu,刪除的是彈出菜單,如
果你用子菜單GetSubMenu的指針,調用DeleteMenu,刪除的是菜單項。
BOOL DeleteMenu( UINT nPosition, UINT nFlags );
參數:菜單項或彈出菜單,所在的ID號或索引號,由第二個參數決定訪問方式;
在CMainFrame::OnCreate中添加以下代碼:
GetMenu()->DeleteMenu(1,MF_BYPOSITION);//刪除彈出菜單
GetMenu()->GetSubMenu(1)->DeleteMenu(ID_FILE_SAVE,MF_BYCOMMAND);//刪除指定菜單項
===
如何爲動態增加的菜單,或菜單項做命令響應
我們在資源編輯器,增加的菜單可以通過類嚮導,做命令響應。那麼動態增加的菜單怎麼辦了?
當然,我們可以通過手動添加。
首先創建菜單的資源ID,在File View頭文件Resource.h,如下格式:
#define IDM_... 111
#define IDM_... 112
做一個菜單命令的響應函數,它的添加和消息映射是一樣的。
3個步驟:
1 在類的頭文件(這裏是CMainFrm.h),做命令函數的原型。
DECLARE_MESSAGE_MAP()之前,//{{AFX_MSG(CMainFrame)和//}}AFX_MSG之後:afx_msg void OnHello();
2 消息原型寫完,接下來就是消息映射。在源文件(.cpp)當中,在BEGIN_MESSAGE_MAP和END_MESSAGE_MAP()
之間,加入:(對於命令消息,我們通過ON_COMMAND這個宏來完成)ON_COMMAND(IDM_HELLO,OnHello)
3 消息響應函數的實現部分:在最後面寫(#endif //_DEBUG的後面)
void CMainFrame::OnHello
{
//響應代碼......
}
===
編寫一個電話本程序
我們想要完成的功能:鍵盤輸入人名,空格之後+上電話號碼,回車之後,把人名添加到菜單項。當點擊彈
出菜單時PhoneBook,把人名和電話號碼顯示在屏幕。
1 首先在view類捕獲WM_CHAR消息。
2 在view類,增加4個成員變量:第一個:類型int,名稱m_nIndex,權限private;
第二個:類型CMenu,名稱m_menu,權限private;
第三個:類型CString,名稱m_strLine,權限private;//存儲輸入的字符串
第四個:類型CStringArray,名稱m_strArray,權限,public。
3 在view類的構造函數賦初值,m_nIndex=-1;m_strLine="";(將字符串初始化爲空)
4 在View::OnChar中添加如下代碼。(所有的鍵盤輸入,都會通過OnChar這個消息來捕獲)
CClientDC dc(this);
if(0x0d==nChar)//如果回車等於nChar
{
if(0==++m_nIndex)//如果是第一次,就創建菜單
{
m_menu.CreatePopupMenu();//創建彈出菜單
GetParent()->GetMenu()->AppendMenu(MF_POPUP,
(UINT)m_menu.m_hMenu,"PhoneBook");
//將彈出菜單,添加到菜單欄上,注意要先獲取框架類的指針
GetParent()->DrawMenuBar();//重繪改變的菜單欄,否則新增的彈出菜單有問題
}
m_menu.AppendMenu(MF_STRING,IDM_PHONE1+m_nIndex,
m_strLine.Left(m_strLine.Find(' ')));
//增加菜單項,以輸入的人名爲菜單項名字,m_strLine.Find查找空格,m_strLine.Left是截取空格左邊的
字符串(即人名)
m_strArray.Add(m_strLine);//用CStrArray保存CString對象,這樣人名和號碼都保存在
這個集合類上面
m_strLine.Empty();//清空上次輸入的字符串
Invalidate();//擦除先前的文字,防止重疊顯示。這個函數讓整個客服端區域背景無效
}
else
{
m_strLine+=nChar;
dc.TextOut(0,0,m_strLine);//輸出字符串
}
5 你點菜單項的時候,要顯示人名和電話號碼,所以要對菜單項進行響應
(一)先在資源編輯器上添加一個菜單,其彈出的菜單項ID號分別爲IDM_PHONE1, IDM_PHONE2, IDM_PHONE3
(二)在Rsource.h中添加
#define IDM_PHONE1 201
#define IDM_PHONE2 202
#define IDM_PHONE3 203
(三)回到菜單上面的資源編輯器,利用ClassWizard給菜單項添加響應函數,並編輯函數。
在view類,增加的命令響應函數COMMAND。
(四)最後回到菜單的資源編輯器,把先前增加的菜單刪除。
(五)把剛纔加的,在view類構造函數的消息響應函數ON_COMMAND(IDM_PHONE3, OnPhone3),拿到2個註釋
宏的外面,即END_MESSAGE_MAP()的前面,否則會有一些不必要的麻煩。
6 在新增加的響應函數(第一種方法),寫上以下代碼:
第一個OnPhone1:
CClientDC dc(this);
dc.TextOut(0,0,m_strArray.GetAt(1);
第二個OnPhone2:
CClientDC dc(this);
dc.TextOut(0,0,m_strArray.GetAt(2);
第二個OnPhone3:
CClientDC dc(this);
dc.TextOut(0,0,m_strArray.GetAt(3);
由框架類捕獲菜單響應(第二種方法),而不是由View處理,可以節約代碼,而且更聰明
首先在框架類,增加虛函數OnCommand。
在其中添加如下代碼:
int MenuCmdId=LOWORD(wParam);
CMenu2View *pView=(CMenu2View*)GetActiveView();//獲取視類指針GetActiveView。
if(MenuCmdId>=IDM_PHONE1 && MenuCmdId<IDM_PHONE1+pView->m_strArray.GetSize())
{
CClientDC dc(pView);
dc.TextOut(0,0,pView->m_strArray.GetAt(MenuCmdId-IDM_PHONE1));
return TRUE;
}
備註:記得在MainFrm.cpp添加頭文件:#include "Menu2Doc.h";#include "Menu2View"
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.