天天看點

Windows程式設計(2)|Win32建立菜單

()在Win32程式設計下建立菜單有兩種,一種是直接加載資源裡的菜單,另一種是動态建立

一.直接加載資源資源菜單,有兩種方法,要包含頭檔案resource.h

    在"資源檔案"處右鍵"添加"中選擇"資源",在彈出的資源框中選擇"Menu",然後對菜單編輯,我這裡的菜單的ID都沒有改變

   1. 在設計視窗類時将WNDCLASS結構體中的lpszMenuName成員關聯菜單ID,使用MAKEINTRESOURCE()這個宏,将資源

 ID轉換為所對應的資源名,我的視窗類如下

//設計視窗類,該視窗類并不是C++中的類,隻是表示視窗特征的結構體

	WNDCLASS MyWndClass; //WNDCLASS是個結構體,該結構體中的成員是指定視窗特征的資料
   
	//WNDCLASS結構體
	/*

	typedef struct _WNDCLASS { 
	UINT       style; 
	WNDPROC    lpfnWndProc; 
	int        cbClsExtra; 
	int        cbWndExtra; 
	HINSTANCE  hInstance; 
	HICON      hIcon; 
	HCURSOR    hCursor; 
	HBRUSH     hbrBackground; 
	LPCTSTR    lpszMenuName; 
	LPCTSTR    lpszClassName; 
	} WNDCLASS, *PWNDCLASS; 
	Members

	*/

	//這兩個變量允許使用者請求Windows内部提供額外的空間以便額外資料與視窗
	//執行個體發生聯系,通常不配置設定空間
	MyWndClass.cbClsExtra = NULL;
	MyWndClass.cbWndExtra = NULL;
    
	//背景顔色,這裡有兩種方法
	//GetStockObject()傳回的句柄是HGDIOBJ類型,需要轉換,
	//可以加載畫刷,也可以加載畫筆,
	//GetStockObject()參數的值有(看MSDN)
	//BLACK_BRUSH     黑色畫刷
	//DKGRAY_BRUSH    深灰色畫刷
	//GRAY_BRUSH      灰色畫刷
	//WHITE_BRUSH     白色畫刷
	//MyWndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	MyWndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
	
	//視窗的圖示,如果為NULL,系統将提供一個預設的圖示
	//LoadIcon()加載圖示資源,傳回圖示句柄,
	//如果加載系統标準的圖示第一個參數必須為NULL,第二個參數值為
	//IDI_ERROR        錯誤圖示
	//IDI_APPLICATION  預設應用程式圖示
	//IDI_QUESTION     問号圖示 
	//IDI_EXCLAMATION  感歎号圖示 
	//IDI_ASTERISK     星号圖示
	//IDI_WARNING      警告圖示
	//IDI_WINLOGO      Windows圖示
	//IDI_HAND         與IDI_ERROR相同
	//IDI_INFORMATION  消息圖示
	//還有很多.....
	MyWndClass.hIcon   = LoadIcon(NULL,IDI_WINLOGO); 
    
        //光标,LoadCursor()的使用與LoadIcon()相同
        MyWndClass.hCursor = LoadCursor(NULL,IDC_APPSTARTING);
	
	MyWndClass.hInstance = hInstance; //目前執行個體的句柄
        MyWndClass.lpfnWndProc = MyWindowProc; //視窗函數(消息處理函數),lpfnWndProc是個函數指針
	MyWndClass.lpszClassName = szWindowClass; //視窗類名
        //MyWndClass.lpszMenuName  = NULL ;// 菜單,指定菜單資源的名字,NULL是表示沒有菜單,
	MyWndClass.lpszMenuName  = MAKEINTRESOURCE(IDR_MENU1);//一種,MAKEINTRESOURCE()加載菜單
        MyWndClass.style = CS_HREDRAW|CS_VREDRAW; //使用|把多種視窗樣式連接配接在一起
	//視窗的樣式
        //CS_HREDRAW  表示當水準方向寬度發生變化時重繪整個視窗
        //CS_VREDRAW  表示當垂直方向高度發生變化時重繪整個視窗
	//CS_NOCLOSE  禁用系統菜單中的Close指令,既是沒有關閉按鈕
	//CS_DBLCLKS  當使用者輕按兩下滑鼠時向視窗過程函數發送滑鼠輕按兩下消息
    
           

        而在建立視窗函數中菜單參數可以指定為NULL,如下:

//建立視窗,傳回視窗的句柄
    HWND hWnd = CreateWindow(
		        szWindowClass, //視窗類名稱
			szWindowTitle, //視窗标題
			WS_OVERLAPPEDWINDOW,  //視窗樣式,多種樣式
			//這個樣式要與WNDCLASS的樣式差別開,這個是指定某個具體視窗的樣式
			//而WNDCLASS的樣式是指基于該視窗類的所有視窗都具有的樣式
			//WS_OVERLAPPED  一個可層疊視窗
                        //WS_CAPTION     有标題欄
			//WS_SYSMENU     在标題欄帶有系統菜單,WS_CAPTION一起使用
			//WS_THICKFRAME  具有可調邊框視窗
			//WS_MINIMIZEBOX 有最小按鈕,必須設定WS_SYSMENU
			//WS_MAXIMIZEBOX 有最大按鈕,必須設定WS_SYSMENU

			CW_USEDEFAULT,  //x坐标,預設
			//CW_USEDEFAULT僅适用于WS_OVERLAPPED樣式視窗
			CW_USEDEFAULT,  //y坐标,預設
			CW_USEDEFAULT,  //寬
			CW_USEDEFAULT,  //高
			NULL,           //父視窗
			NULL,           //第一種加載菜單
			//LoadMenu(hInstance,MAKEINTRESOURCE(IDR_MENU1)), //第二種加載菜單
			//hMenu, //動态菜單
			hInstance,      //視窗執行個體标記
			NULL            //視窗建立時傳入的資料指針,
				        //多文檔時必須指向CLIENTCREATESTRUCT
		         );
           

     2. 在設計視窗類時将WNDCLASS結構體中的lpszMenuName成員指定為NULL,直接在視窗的建立函數中加載

代碼如上面注釋的第二種加載菜單方法,使用LoadMenu()加載,第一個參數為視窗的執行個體,第二個參數為菜單資源,

一樣要使用MAKEINTRESOURCE()宏來将ID轉為資源名,

二.  動态菜單,不需要包含頭檔案resource.h,但需要定義一些宏,用于相應菜單的消息

//動态菜單
#define IDM_FILE_NEW  40001
#define IDM_FILE_OPEN 40002
#define IDM_EDIT_COPY 40003
#define IDM_EDIT_CUT  40004
           

我不清楚為什麼要從40001開始

建立菜單

//動态菜單
	HMENU hMenu = CreateMenu(); //主菜單,水準

	HMENU hMenuPop = CreateMenu(); //下拉的菜單,垂直
	AppendMenu(hMenuPop,MF_STRING,IDM_FILE_NEW,_T("New"));
	AppendMenu(hMenuPop,MF_STRING,IDM_FILE_OPEN,_T("Open"));
	AppendMenu(hMenu,MF_POPUP,(unsigned int)hMenuPop,_T("File"));
	//把下拉的菜單加載到主菜單第一個菜單,說的有點形象,
	//自己運作程式看效果

	hMenuPop = CreateMenu();
	AppendMenu(hMenuPop,MF_STRING,IDM_EDIT_COPY,_T("Copy"));
	AppendMenu(hMenuPop,MF_STRING,IDM_EDIT_CUT,_T("Cut"));
	AppendMenu(hMenu,MF_POPUP,(unsigned int)hMenuPop,_T("Edit"));
           

 加載菜單,直接在建立視窗函數中把動态菜單的主菜單句柄hMenu給函數中的菜單參數,代碼如上已經注釋的代碼

下面代碼執行的結果是加載動态菜單的結果,直接加載資源裡的菜單已經注釋起來了,代碼中有對菜單消息的相應,

看消息處理函數,代碼是基于Windows程式設計|SDK我的這篇文章裡的代碼修改的

//開發工具:VS2008
//使用 Unicode 字元集

#include <Windows.h>
#include <tchar.h> //_T或_TEXT需要的頭檔案
//#include "resource.h"

//設計一個視窗類;
//注冊視窗類;
//建立視窗;
//顯示及更新視窗。
//消息循環
//視窗函數

//動态菜單
#define IDM_FILE_NEW  40001
#define IDM_FILE_OPEN 40002
#define IDM_EDIT_COPY 40003
#define IDM_EDIT_CUT  40004
//全局變量
TCHAR szWindowClass[] = _T("示範程式");
TCHAR szWindowTitle[] = _T("主視窗标題");

//定義的視窗過程函數,是個回調函數,意思是該函數不是在程式中直接調用
//而是在特定的事件或條件發生時由Windows系統調用,對事件或條件的響應
LRESULT CALLBACK MyWindowProc(
			HWND hwnd,      // handle to window 視窗句柄
			UINT uMsg,      // message identifier	消息辨別
			WPARAM wParam,  // first message parameter
			LPARAM lParam   // second message parameter
			 );
//程式的入口函數
int WINAPI  WinMain( 
		HINSTANCE hInstance,  //記錄程式目前運作的執行個體的句柄
		HINSTANCE hPrevInstance, //已經失去了意義,總為NULL
		LPSTR lpCmdLine, //指令行參數,記錄目前運作程式的路徑
		int nShowCmd     //指定程式視窗應該如何顯示,通常不檢查這個參數的值
		)
{

	//設計視窗類,該視窗類并不是C++中的類,隻是表示視窗特征的結構體

	WNDCLASS MyWndClass; //WNDCLASS是個結構體,該結構體中的成員是指定視窗特征的資料
   
	//WNDCLASS結構體
	/*

	typedef struct _WNDCLASS { 
	UINT       style; 
	WNDPROC    lpfnWndProc; 
	int        cbClsExtra; 
	int        cbWndExtra; 
	HINSTANCE  hInstance; 
	HICON      hIcon; 
	HCURSOR    hCursor; 
	HBRUSH     hbrBackground; 
	LPCTSTR    lpszMenuName; 
	LPCTSTR    lpszClassName; 
	} WNDCLASS, *PWNDCLASS; 
	Members

	*/

	//這兩個變量允許使用者請求Windows内部提供額外的空間以便額外資料與視窗
	//執行個體發生聯系,通常不配置設定空間
	MyWndClass.cbClsExtra = NULL;
	MyWndClass.cbWndExtra = NULL;
    
	//背景顔色,這裡有兩種方法
	//GetStockObject()傳回的句柄是HGDIOBJ類型,需要轉換,
	//可以加載畫刷,也可以加載畫筆,
	//GetStockObject()參數的值有(看MSDN)
	//BLACK_BRUSH     黑色畫刷
	//DKGRAY_BRUSH    深灰色畫刷
	//GRAY_BRUSH      灰色畫刷
	//WHITE_BRUSH     白色畫刷
	//MyWndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	MyWndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
	
	//視窗的圖示,如果為NULL,系統将提供一個預設的圖示
	//LoadIcon()加載圖示資源,傳回圖示句柄,
	//如果加載系統标準的圖示第一個參數必須為NULL,第二個參數值為
	//IDI_ERROR        錯誤圖示
	//IDI_APPLICATION  預設應用程式圖示
	//IDI_QUESTION     問号圖示 
	//IDI_EXCLAMATION  感歎号圖示 
	//IDI_ASTERISK     星号圖示
	//IDI_WARNING      警告圖示
	//IDI_WINLOGO      Windows圖示
	//IDI_HAND         與IDI_ERROR相同
	//IDI_INFORMATION  消息圖示
	//還有很多.....
	MyWndClass.hIcon   = LoadIcon(NULL,IDI_WINLOGO); 
    
        //光标,LoadCursor()的使用與LoadIcon()相同
        MyWndClass.hCursor = LoadCursor(NULL,IDC_APPSTARTING);
	
	MyWndClass.hInstance = hInstance; //目前執行個體的句柄
        MyWndClass.lpfnWndProc = MyWindowProc; //視窗函數(消息處理函數),lpfnWndProc是個函數指針
	MyWndClass.lpszClassName = szWindowClass; //視窗類名
        MyWndClass.lpszMenuName  = NULL ;// 菜單,指定菜單資源的名字,NULL是表示沒有菜單,
	//MyWndClass.lpszMenuName  = MAKEINTRESOURCE(IDR_MENU1);//一種,MAKEINTRESOURCE()加載菜單
        MyWndClass.style         = CS_HREDRAW|CS_VREDRAW; //使用|把多種視窗樣式連接配接在一起
	//視窗的樣式
        //CS_HREDRAW  表示當水準方向寬度發生變化時重繪整個視窗
        //CS_VREDRAW  表示當垂直方向高度發生變化時重繪整個視窗
	//CS_NOCLOSE  禁用系統菜單中的Close指令,既是沒有關閉按鈕
	//CS_DBLCLKS  當使用者輕按兩下滑鼠時向視窗過程函數發送滑鼠輕按兩下消息
    
	//注冊視窗類,告訴Windows系統視窗類設計好了
	RegisterClass(&MyWndClass);

	//動态菜單
	HMENU hMenu = CreateMenu(); //主菜單,水準

	HMENU hMenuPop = CreateMenu(); //下拉的菜單,垂直
	AppendMenu(hMenuPop,MF_STRING,IDM_FILE_NEW,_T("New"));
	AppendMenu(hMenuPop,MF_STRING,IDM_FILE_OPEN,_T("Open"));
	AppendMenu(hMenu,MF_POPUP,(unsigned int)hMenuPop,_T("File"));
	//把下拉的菜單加載到主菜單第一個菜單,說的有點形象,
	//自己運作程式看效果

	hMenuPop = CreateMenu();
	AppendMenu(hMenuPop,MF_STRING,IDM_EDIT_COPY,_T("Copy"));
	AppendMenu(hMenuPop,MF_STRING,IDM_EDIT_CUT,_T("Cut"));
	AppendMenu(hMenu,MF_POPUP,(unsigned int)hMenuPop,_T("Edit"));

	//建立視窗,傳回視窗的句柄
        HWND hWnd = CreateWindow(
		        szWindowClass, //視窗類名稱
			szWindowTitle, //視窗标題
			WS_OVERLAPPEDWINDOW,  //視窗樣式,多種樣式
			//這個樣式要與WNDCLASS的樣式差別開,這個是指定某個具體視窗的樣式
			//而WNDCLASS的樣式是指基于該視窗類的所有視窗都具有的樣式
			//WS_OVERLAPPED  一個可層疊視窗
                        //WS_CAPTION     有标題欄
			//WS_SYSMENU     在标題欄帶有系統菜單,WS_CAPTION一起使用
			//WS_THICKFRAME  具有可調邊框視窗
			//WS_MINIMIZEBOX 有最小按鈕,必須設定WS_SYSMENU
			//WS_MAXIMIZEBOX 有最大按鈕,必須設定WS_SYSMENU

			CW_USEDEFAULT,  //x坐标,預設
			//CW_USEDEFAULT僅适用于WS_OVERLAPPED樣式視窗
			CW_USEDEFAULT,  //y坐标,預設
			CW_USEDEFAULT,  //寬
			CW_USEDEFAULT,  //高
			NULL,           //父視窗
			NULL,           //第一種加載菜單
			//LoadMenu(hInstance,MAKEINTRESOURCE(IDR_MENU1)), //第二種加載菜單
			//hMenu, //動态菜單
			hInstance,      //視窗執行個體标記
			NULL            //視窗建立時傳入的資料指針,
				        //多文檔時必須指向CLIENTCREATESTRUCT
		         );

     //如果視窗建立失敗
	if(!hWnd)
		return 0;

	//顯示并更新視窗
	ShowWindow(hWnd,SW_SHOW);
	UpdateWindow(hWnd);

    //消息循環,WM_QIUT才停止循環,或程式已經推出
    /*

	BOOL GetMessage(
	LPMSG lpMsg,       //指向一個消息結構體MSG結構體對象,
	                   //用于儲存從消息隊列中擷取的消息 
	HWND hWnd,         //指向某個視窗的句柄,NULL是指擷取程式的所有消息
	UINT wMsgFilterMin, //消息隊列中消息ID的最小值 
	UINT wMsgFilterMax  //消息隊列中消息ID的最大值
	//最後兩個參數适用于指定消息ID的範圍,如果都為0,表示擷取消息隊列所有消息
	);

    //函數傳回值總為TRUE,當獲得WM_QIUT傳回0,出錯時傳回-1
	//菜單中的"檔案"中的"退出",視窗的"X"關閉按鈕,視窗上系統菜單的"關閉"指令
	//上面的3中情況都會發送一個WM_QUIT消息

	*/

	MSG Msg;
    while(GetMessage(&Msg,NULL,0,0))
    {
	TranslateMessage(&Msg);  //讓Windows為與鍵盤相關的消息做一些轉換
	DispatchMessage(&Msg);   //分派消息到視窗過程函數中對消息處理
    }
    
    return 1;

}

//視窗過程函數的實作(消息處理函數)
LRESULT CALLBACK MyWindowProc(
			 HWND hwnd,      // handle to window 視窗句柄
			 UINT uMsg,      // message identifier	消息辨別
			 WPARAM wParam,  // first message parameter
			 LPARAM lParam   // second message parameter
			)
{

   switch(uMsg)
   {
   case WM_PAINT:
	   {
		   TCHAR str[] = _T("這是一個Windows的SDK程式!");
		   HDC hDc;
		   PAINTSTRUCT  Ps;
		   hDc = BeginPaint(hwnd,&Ps); //擷取裝置環境句柄
		   SetTextColor(hDc,RGB(13,25,200)); //設定文本顔色
		   TextOut(hDc,0,0,str,18); //輸出文字
		   EndPaint(hwnd,&Ps); //釋放資源
		   break;
	   }
   case WM_CLOSE: //點選"X"按鈕時發送此消息,使消息循環停止 
	   if(IDYES == MessageBox(hwnd,_T("是否要退出?"),_T("提示"),MB_YESNO))
	   {
          DestroyWindow(hwnd); //銷毀視窗,發送WM_DESTROY消息,注意程式程序還沒退出
	   }	   
	   break;
   case WM_DESTROY:
	   PostQuitMessage(NULL); //程序結束,完全退出程式
	   break;
   case WM_LBUTTONDOWN:
	   MessageBox(hwnd,_T("滑鼠左鍵按下"),_T("提示"),MB_OK);
	   break;
   case WM_COMMAND: //相應菜單消息
	   switch(LOWORD(wParam))
	   {
	   //相應直接加載的菜單消息
		   /*
	   case ID_40001: MessageBox(hwnd,_T("菜單檔案裡的建立"),_T("菜單"),MB_OK); break;
	   case ID_40002: MessageBox(hwnd,_T("菜單檔案裡的打開"),_T("菜單"),MB_OK);break;
	   case ID_40003: MessageBox(hwnd,_T("菜單檔案裡的儲存"),_T("菜單"),MB_OK);break;
	   case ID_40004: MessageBox(hwnd,_T("菜單檔案裡的另存為"),_T("菜單"),MB_OK);break;
	   case ID_40005:  PostQuitMessage(0);  break;
	   */
	   //相應動态菜單消息
	   case IDM_FILE_NEW: MessageBox(hwnd,_T("動态菜單File裡的New"),_T("菜單"),MB_OK); break;
	   case IDM_FILE_OPEN: MessageBox(hwnd,_T("動态菜單File裡的Open"),_T("菜單"),MB_OK); break;
	   case IDM_EDIT_COPY: MessageBox(hwnd,_T("動态菜單Edit裡的Copy"),_T("菜單"),MB_OK); break;
	   case IDM_EDIT_CUT:  MessageBox(hwnd,_T("動态菜單Edit裡的Cut"),_T("菜單"),MB_OK); break;
	   }
	   break;
   default:
	   return DefWindowProc(hwnd,uMsg,wParam,lParam); //處理未處理的消息
	   break;
   }

   return 0;

}