1. 程式人生 > >基於單文件MFC的選單的操作

基於單文件MFC的選單的操作

基於單文件的MFC標準的程式

選單命令的訊息路由:

由快到慢
檢視 (最快) > 文件 > 框架類 > 應用程式類
如果同一個選單命令 在上面四個中都有處理函式,則只相應 檢視,如果將檢視中處理函式刪掉,則只相應 文件類的,以此類推。
**-
摘自孫鑫VC++

關於選單的幾個操作

在MainFrm.cpp的OnCreate函式下:

思路:獲取主選單–》獲取子選單-》給第3和5個選單項做標記√–》給第5個子選單 設為預設項–》給第5個變灰

【注意】分隔符也佔一個位置啊,列印是第6個,但引數裡面是5哦,從零開始數。

//獲取主選單
	CMenu *menu = GetMenu();//獲取主選單,
	
	//獲取子選單
	CMenu *Filemenu = menu->GetSubMenu(0);//獲取子選單

	//標記√  兩種方式
	Filemenu->CheckMenuItem(2,MF_BYPOSITION | MF_CHECKED);
	Filemenu->CheckMenuItem(ID_FILE_PRINT_SETUP,MF_BYCOMMAND | MF_CHECKED);

	//設定預設項 一個菜單隻有一個預設項   FALSE為 ID ;  true 為位置
	//Filemenu->SetDefaultItem(ID_FILE_PRINT,FALSE);//列印(P)...
	Filemenu->SetDefaultItem(3,true);////列印(P)...注意分隔符也佔一個位置啊

	//變灰  CFrameWnd:: m_bAutoMenuEnable = false;
	//注意變灰這個,需要在建構函式中 新增m_bAutoMenuEnable = false;!!!!
	Filemenu->EnableMenuItem(5,MF_BYPOSITION | MF_DISABLED);

	//分隔符設定問題--》》》》》  右擊選單 屬性  separator  改為false即可

載入新選單

**思路:**如果有舊的,可以先移除,載入新選單,設定選單

//移除選單//為NULL時全部移除
SetMenu(NULL);//CMenu::RemoveMenu()必須給指定的刪除哪一個子項

//載入選單
	CMenu menuNew;
	menuNew.LoadMenu(IDR_NEW_MENU);
	SetMenu(&menuNew);
	menuNew.Detach();//這個必須要有,沒有程式崩潰

如果只有上面前三行程式則會程式崩潰,因為menuNew是一個區域性變數。
解決辦法:
一: CMenu menuNew; 設定為全域性變數
二: 在上面程式碼中新增 menuNew.Detach();即可

幾個重要函式:

  • SetMenu()

原型BOOL SetMenu(HWND hWnd,HMENU hMenu);
功能:該函式分配一個新選單到指定視窗
引數:
hWnd:選單被分配到其中的視窗的控制代碼。
HMenu:新選單的控制代碼。如果選單引數為NULL,則視窗的當前選單被刪除。 函式SetMenu替換原來的選單(如果存在),但並不將其銷燬。應用程式必須呼叫函式DestroyMenu來銷燬選單。

  • TrackPopupMenu
    功能:彈出子選單
    BOOL TrackPopupMenu( UINT nFlags, int x, int y, CWnd* pWnd,LPCRECT lpRect = NULL );
    引數: x,y是指螢幕左邊。如果是View中新增WM_LBUTTONDOWM 獲得Point是客戶區座標,需要用ClientToScreen(&point)來轉換
  • 子選單欄的有兩種訊息
    一:ON_UPDATE_COMMAND_UI 處理使用者介面顯示狀態:變灰,√
    二:COMMAND :點選按鍵,設定變數m_bIsUpdate來控制上面的情況
    訊息:ON_UPDATE_COMMAND_UI 選中子選單-> 右擊 新增事件處理程式–選擇 CMainFrame。這個訊息可以處理當前子選單的狀態,比如何時變灰啊,加√啊,加文字,加單選啊,用其他函式下的變數【標誌位】來控制這個何時需要何種變化狀態。
    用這種方式比 在OnCreate中( 先獲得主選單,再獲得子選單,在操作子選單變灰 )這種要方便,因為選單是內部自動更新,會有重繪等操作,會相應這個訊息的。

選單更新機制

ON_UPDATE_COMMAND_UI 這個訊息來,選單有內部自動更新功能,通過標誌位來更新選單。

子選單的介面更新

重點】用一個標誌變數去控制子選單的更新,在框架類中實現的


    //ON_UPDATE_COMMAND_UI 訊息的作用:
//功能:點選B 使A變灰或者不變灰//選單欄是自動內部更新的,重新呼叫繪製選單欄
//選單是內部自動更新
//A的子選單的更新
void CMainFrame::OnUpdateTest(CCmdUI *pCmdUI)
{
	// TODO: 在此新增命令更新使用者介面處理程式程式碼
	if(true == m_bIsUpdata)
	{
		pCmdUI->Enable(TRUE);
//		pCmdUI->SetCheck();
		pCmdUI->SetRadio();
	}
	else
	{		
		pCmdUI->Enable(FALSE);
		pCmdUI->SetCheck(false);
	}
}

//m_bIsUpdata 全域性私有變數 
//點選B選單項時處理函式  命令處理函式
void CMainFrame::OnTestB()//B的訊息處理函式
{
	// TODO: 在此新增命令處理程式程式碼
	m_bIsUpdata = !m_bIsUpdata;//控制A中選單更新
}

【關於基於對話方塊的選單欄沒有更新的問題】
參考https://blog.csdn.net/xuanyin235/article/details/80905655

檢視中操作選單:左鍵單擊彈出子選單項

  • 在客戶區點選滑鼠左鍵時,彈出子選單視窗
    由於客戶區的話,則在檢視視窗新增WM_LBUTTONDOWM訊息。
    思路:
    【報錯】獲取主選單-> 獲取子選單->操作就可以
    【正確】載入選單->獲取子選單-》座標轉換-》彈出子選單
    但CMenu *menu = GetMenu();//成功
    CMenu *SubMenu = menu->GetSubMenu(0);//不成功
    這個獲取就不成功呢,是因為選單是屬於框架類的,如果在檢視中操作時,需要先載入選單項,然後在處理而且,彈出的子選單項再點選時不會相應像mainfrm.cpp中訊息
//在客戶區點選滑鼠左鍵時,彈出子選單項
void CMy05_MenueView::OnLButtonDown(UINT nFlags, CPoint point)
{
	// TODO: 在此新增訊息處理程式程式碼和/或呼叫預設值
	//選單是屬於框架類的,如果在檢視中操作時,需要先載入選單項,然後在處理。//而且,彈出的子選單項再點選時不會相應像mainfrm.cpp中訊息
	 CMenu mee;
	 mee.LoadMenuW(IDR_NEW_MENU);
	 CMenu *SubMenu = mee.GetSubMenu(0);
	 ClientToScreen(&point);
	SubMenu->TrackPopupMenu(TPM_CENTERALIGN | TPM_LEFTBUTTON,point.x,point.y,this);
	CView::OnLButtonDown(nFlags, point);
}

點選選單第2個子選單,使第3個或者其他子選單項更新狀態

載入動態圖示

標題是屬於框架類的,不屬於檢視,,框架包含選單欄,客戶區等。檢視只要客戶區啊。。。

  • 新增資源–匯入圖示資源
  • 在MainFrame.h中定義圖示的控制代碼,如果多個用陣列:HICON hICon[4];
  • 在CMainFrame::CMainFrame()建構函式中 給圖示控制代碼初始化
    hICon[0] = AfxGetApp()->LoadIcon(IDI_ICON1);
	hICon[1] = AfxGetApp()->LoadIcon(IDI_ICON2);
	hICon[2] = AfxGetApp()->LoadIcon(IDI_ICON3);
	hICon[3] = AfxGetApp()->LoadIcon(IDI_ICON4);
  • 在OnCreate中 開啟定時器 SetTimer(12,500,NULL);
  • 在定時器中修改標題: SetClassLong函式非常重要
if(12 == nIDEvent)
	{
		static int i = 0;
		SetClassLong(m_hWnd,GCL_HICON,(LONG)hICon[i]);
		i++;
		if(4 == i)
		{
			i = 0;
		}
	}

函式SetClassLong

功能:設定或者修改視窗類的某些引數
原型:
DWORD SetClassLong(HWND hWnd,int nlndex,LONG dwNewLong)

hWnd:視窗控制代碼及間接給出的視窗所屬的類。
nlndex:指定將被替換的32位值
dwNewLong:指定的替換值。
返回值:
返回值的型別:DWORD

如果要設定WNDCLASSEX結構中的任何值,需要指定下面索引之一:

GCL_CBCLSEXTRA:設定與類相關的尺寸的位元組大小。設定該值不改變己分配的額外位元組數。
GCL_CBWNDEXTRA:設定與類中的每一個視窗相關的尺寸的位元組大小。設定該值不改變已分配額外位元組數。檢視如何進入該記憶體,參看SetWindowLOng。
GCL_HBRBACKGROUND:替換與類有關的背景刷子的控制代碼。
GCL_HCURSOR:替換與類有關的游標的控制代碼。
GCL_HICON:替換與類有關的圖示的控制代碼。
GCL_HMODULE:替換註冊類的模組的控制代碼。
GCL_STYLE:替換視窗類的風格位。
GCL_MENUNAME :替換選單名字串的地址。該字串標識與類有關的選單資源。
GCL_WNDPROC :替換與視窗類有關的視窗過程的地址。

函式 GetClassLong

功能:獲取現有型別
原型DWORD GetClassLong(HWND hWnd,int nlndex);

DWORD SetClassLong 替換指定視窗所屬類的WNDCLASSEX結構,對視窗所屬類的視窗進行修改,

LONG SetWindowLong 該函式改變指定視窗的屬性,對具體的視窗的樣式尺寸等進行修改
區別:一個是操作所屬類,一個操作具體視窗,而且返回值不太一樣

在孫鑫寫的那本書上的例子:

SetWindowLong(m_hWnd, GWL_STYLE,   GetWindowLong(m_hWnd,GWL_STYLE));

【備註】單文件程式中
A 檢視 CView :: CWnd
B 框架 CFrame::CWnd
C 文件 Cdocument :: CCmdTarget
D 應用程式類 CWinApp:: CCmdTarget
AB可以用CWnd::MessagBox CD只能用AfxMessageBox 因為CD不是繼承與CWnd