1. 程式人生 > >學習win32 API開發6-給視窗新增選單

學習win32 API開發6-給視窗新增選單

看到別人做選單有很多種,而我就比較不要麻煩就用較簡單的來學習下吧,好用就行了。

一、用編輯資源來新增選單

我用的VC++6.0,VS2013也是一樣的!其他的工具嘛,沒有用, 不知道。

先插入或者說新增一個型別為選單(MENU)的資原始檔,然後就儲存下,儲存到當前專案的目錄下面,然後編輯這個資原始檔,資原始檔的字尾名是“.rc”,把選單資源的ID改好。

現在新增選單項吧,就做個簡單的,比如,一個“檔案”,然後下級添加個“退出”。同時把在屬性裡面填寫好ID,比如:我的“退出”的ID是:IDM_EXIIT。

也可以直接開啟resource.h標頭檔案,直接在上面修改,前提是要生成這個檔案,這個檔案是在儲存資原始檔後生成的。

儲存,下面要寫響應選單的過程函數了,在這之前先新增一個頭檔案#include "resource.h",完整程式:

#include "windows.h"
#include "resource.h"
void ShowErrMsg() 
{ 
    LPVOID lpMsgBuf;
    DWORD dw = GetLastError(); 
    FormatMessage(
        FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
        NULL,
        dw,
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        (LPTSTR) &lpMsgBuf,
        0, NULL );
    MessageBox(NULL, (LPTSTR)lpMsgBuf, "系統錯誤", MB_OK|MB_ICONSTOP); 
		//OutputDebugString();
    LocalFree(lpMsgBuf);
}
LRESULT CALLBACK WinProc(HWND hwnd,UINT umsg,WPARAM wparam,LPARAM lparam);
int WINAPI WinMain(HINSTANCE hInstance,
				   HINSTANCE hPrecInstance,
				   LPSTR lpCmdLine,
				   int nShowCmd
				   )
{
	TCHAR* clsName="myclass";
	WNDCLASS mwc={sizeof(WNDCLASS)};

	//設計視窗類
	mwc.lpfnWndProc = WinProc;	//視窗處理函式
	mwc.hbrBackground = (HBRUSH)COLOR_WINDOW;	//視窗背景色
	mwc.lpszClassName = clsName;	// 視窗類名
	mwc.style = CS_HREDRAW | CS_VREDRAW;	//視窗型別
	mwc.cbClsExtra = 0;	//視窗擴充套件
	mwc.cbWndExtra = 0;	//視窗例項擴充套件
	mwc.hCursor = NULL;	//視窗滑鼠游標
	mwc.hIcon = NULL;	//視窗的最小化圖示
	mwc.lpszMenuName = (CHAR *)IDR_MENUmain;	//視窗選單
	mwc.hInstance = hInstance;	//例項控制代碼
	
	//註冊視窗類
	RegisterClass(&mwc);

	//建立視窗
	HWND hwnd = CreateWindow(
		clsName,	//類名,就是剛才註冊的
		//"HEHE",
		TEXT("Windows的標題"),	//視窗標題
		WS_SYSMENU|WS_MINIMIZEBOX|WS_MAXIMIZEBOX,	//外觀樣式
		400,	//相對於父視窗的X座標
		150,	//相對於父視窗的Y座標
		400,	//視窗的寬
		400,	//視窗的高
		NULL,	//父視窗,沒有就寫NULL
		LoadMenu(hInstance,MAKEINTRESOURCE(IDR_MENUmain)),	//視窗選單,沒有就可以寫NULL
		hInstance,	//當前應用程式的例項控制代碼
		NULL);	//附加資料,沒有,為NULL
	if( hwnd == NULL)	//檢查視窗是否建立成功
	{
		ShowErrMsg();
		//DWORD err = GetLastError();
		//TCHAR  er[123] ;
		//wsprintf(er,"%i",err);
		//MessageBox(NULL,er,TEXT("提示"),MB_ICONWARNING);
		
		MessageBox(NULL,TEXT("建立視窗失敗"),TEXT("提示"),MB_ICONWARNING);
		return 0;
	}
	//顯示視窗和更新視窗
	ShowWindow(hwnd,SW_SHOW);
	UpdateWindow(hwnd);

	//訊息迴圈
	MSG msg;
	while(GetMessage(&msg,NULL,0,0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	//MessageBox(NULL,TEXT("你好 Windows 世界^_^"),TEXT("標題"),MB_OK);
	return 0;
}
//訊息處理過程函式
LRESULT CALLBACK WinProc(HWND hwnd,UINT umsg,WPARAM wparam,LPARAM lparam)
{
	switch(umsg)
	{
	case WM_PAINT:
		{
			PAINTSTRUCT ps;
			BeginPaint(hwnd,&ps);
			DrawText(ps.hdc,TEXT("你好啊"),6,&(ps.rcPaint),DT_CENTER);
			EndPaint(hwnd,&ps);

		}
		return 0;
	case WM_COMMAND:
		{
			switch(LOWORD(wparam))
			{
			case IDM_EXIIT:	PostQuitMessage(0);break;
			case IDM_ABOUT: PostQuitMessage(0);break;
			default:break;
			}
		}
		return 0;
	case WM_DESTROY:
		{
			PostQuitMessage(0);	//終止程式
			return 0;
		}
	}
	return DefWindowProc(hwnd,umsg,wparam,lparam);
}

要讓選單顯示在視窗上面,第一種方法,那就是在設計視窗類的時候指定選單:mwc.lpszMenuName = (CHAR *)IDR_MENUmain;,IDR_MENUmain是選單的ID。

第二種方法是在建立視窗時候指定選單:LoadMenu(hInstance,MAKEINTRESOURCE(IDR_MENUmain)),這兩種方法都差不多的嘛!

這樣選單是可以顯示了,但是還沒有功能,點選選單都不會有什麼反應,那就要新增選單的處理程式了,在過程函式WinProc裡面實現。

當單擊某個選單項後,視窗處理程式(WinProc)會收到一條WM_COMMAND訊息,收到WM_COMMAND後,可以用LOWORD取得視窗處理程式(WinProc)的第三個引數wparam的低數位,wParam的低位值表示選單的資源ID,我們通過它的值與哪個選單的ID相等,就知道使用者點選了哪個選單項。在視窗處理程式(WinProc)裡面新增如下片段:

case WM_COMMAND:
{
switch(LOWORD(wparam))
{
case IDM_EXIIT:PostQuitMessage(0);break;
case IDM_ABOUT: PostQuitMessage(0);break;
default:break;
}
}

這裡我的選單是點選“退出”和“關於”都跟碘酒關閉按鈕是一樣的功能。

選單有了

二、給選單增加一點功能,能夠在某一項選單前面打上一個勾。

DWORD CheckMenuItem(HMENU hmenu, UINT uIDCheckItem, UINT uCheck);這個函式功能是複選或撤消複選指定的選單條目,有3個引數,第一個是選單控制代碼,第二個是要標記的那項選單的ID,第三個是表示標記的狀態。

BOOL CheckMenuRadioItem(HMEN hMENU,UINT idFirst,UINT idLast,UINT idCheck,UINT uFlags)這個函式是讓選單具有單選功能。

HMENU GetMenu(HWND hWnd)這個函式是獲取指定視窗的選單控制代碼。

HMENU GetSubMenu(HMENU hMenu,int nPos)這個函式是獲取選單的啟用的子選單的控制代碼。

在視窗處理程式(WinProc)的WM_COMMAND訊息裡面實現這個功能。

case WM_COMMAND:
{
HMENU hm=GetMenu(hwnd);//獲取選單控制代碼
HMENU hsm = GetSubMenu(hm,1);//獲取選單下級的選單控制代碼
switch(LOWORD(wparam))
{
case IDM_EXIIT:PostQuitMessage(0);
break;
case IDM_ABOUT: 
CheckMenuItem(hsm,IDM_ABOUT,MF_CHECKED);//hsm用hm也可以
//CheckMenuRadioItem(hsm,0,1,0,MF_BYPOSITION);
break;
case IDM_HELP:
CheckMenuRadioItem(hsm,IDM_ABOUT,IDM_HELP,IDM_HELP,MF_BYCOMMAND);
break;
default:
break;
}
}
return 0;

這裡可以對比下CheckMenuRadioItem(hsm,0,1,0,MF_BYPOSITION);CheckMenuRadioItem(hsm,IDM_ABOUT,IDM_HELP,IDM_HELP,MF_BYCOMMAND);的不同處:第五個引數是指定idFirst,idLast,idCheck含義的值,如果此引數為MF_BYCOMMAND,則其他引數指定選單項識別符號,如果此引數為MF_BYPOSITION,則其他引數指定選單項位置。這個不好理解啊!

選單單選和複選

三、選單可以複選了(看到那個打勾了),但是還是不能去選選中,現在就實現下第一次點選選中(打勾),然後再次點選就取消選中(去掉對勾)。

就怎麼簡單怎麼弄吧,我想到的是定義一個全域性變數,就m吧!int m=0;

然後就在訊息處理函式裡面實現這個功能。

#include "windows.h"
#include "resource.h"

char m=0;
void ShowErrMsg() 
{ 
    //TCHAR szBuf[80]; 
    LPVOID lpMsgBuf;
    DWORD dw = GetLastError(); 
    FormatMessage(
        FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
        NULL,
        dw,
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        (LPTSTR) &lpMsgBuf,
        0, NULL );

    MessageBox(NULL, (LPTSTR)lpMsgBuf, "系統錯誤", MB_OK|MB_ICONSTOP); 
		//OutputDebugString();
    LocalFree(lpMsgBuf);
}
LRESULT CALLBACK WinProc(HWND hwnd,UINT umsg,WPARAM wparam,LPARAM lparam);

int WINAPI WinMain(HINSTANCE hInstance,
				   HINSTANCE hPrecInstance,
				   LPSTR lpCmdLine,
				   int nShowCmd
				   )
{
	TCHAR* clsName="myclass";
	WNDCLASS mwc={sizeof(WNDCLASS)};

	//設計視窗類
	mwc.lpfnWndProc = WinProc;	//視窗處理函式
	mwc.hbrBackground = (HBRUSH)COLOR_WINDOW;	//視窗背景色
	mwc.lpszClassName = clsName;	// 視窗類名
	mwc.style = CS_HREDRAW | CS_VREDRAW;	//視窗型別
	mwc.cbClsExtra = 0;	//視窗擴充套件
	mwc.cbWndExtra = 0;	//視窗例項擴充套件
	mwc.lpszMenuName = MAKEINTRESOURCE(IDR_MENUmain);	//視窗選單
	//mwc.lpszMenuName = (CHAR *)IDR_MENUmain;
	mwc.hInstance = hInstance;	//例項控制代碼

	//註冊視窗類
	RegisterClass(&mwc);

	//建立視窗
	HWND hwnd = CreateWindow(
		clsName,	//類名,就是剛才註冊的
		//"HEHE",
		TEXT("Windows的標題"),	//視窗標題
		WS_SYSMENU|WS_MINIMIZEBOX|WS_MAXIMIZEBOX,	//外觀樣式
		400,	//相對於父視窗的X座標
		150,	//相對於父視窗的Y座標
		400,	//視窗的寬
		400,	//視窗的高
		NULL,	//父視窗,沒有就寫NULL
		0,
		//LoadMenu(hInstance,MAKEINTRESOURCE(IDR_MENUmain)),	//視窗選單,也沒有,寫NULL
		hInstance,	//當前應用程式的例項控制代碼
		NULL);	//附加資料,沒有,為NULL
	if( hwnd == NULL)	//檢查視窗是否建立成功
	{
		ShowErrMsg();
		//DWORD err = GetLastError();
		//TCHAR  er[123] ;
		//wsprintf(er,"%i",err);
		//MessageBox(NULL,er,TEXT("提示"),MB_ICONWARNING);
		
		MessageBox(NULL,TEXT("建立視窗失敗"),TEXT("提示"),MB_ICONWARNING);
		return 0;
	}
	//顯示視窗和更新視窗
	ShowWindow(hwnd,SW_SHOW);
	UpdateWindow(hwnd);

	//訊息迴圈
	MSG msg;
	while(GetMessage(&msg,NULL,0,0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	//MessageBox(NULL,TEXT("你好 Windows 世界^_^"),TEXT("標題"),MB_OK);
	return 0;
}

LRESULT CALLBACK WinProc(HWND hwnd,UINT umsg,WPARAM wparam,LPARAM lparam)
{
	switch(umsg)
	{
	case WM_PAINT:
		{
			PAINTSTRUCT ps;
			BeginPaint(hwnd,&ps);
			DrawText(ps.hdc,TEXT("你好啊"),6,&(ps.rcPaint),DT_CENTER);
			EndPaint(hwnd,&ps);

		}
		return 0;
	case WM_COMMAND:
		{
			HMENU hm=GetMenu(hwnd);			//獲取選單控制代碼
			HMENU hsm = GetSubMenu(hm,1);	//獲取選單下級的選單控制代碼
			
			switch(LOWORD(wparam))
			{
			case IDM_EXIIT:	PostQuitMessage(0);
				break;
			case IDM_ABOUT: 
				if(m==0)
				{
					CheckMenuItem(hm,IDM_ABOUT,MF_CHECKED);	//選中
					//CheckMenuRadioItem(hsm,0,1,0,MF_BYPOSITION);
					m=1;	//選中標誌,為1表示選中狀態
				}
				else
				{
					CheckMenuItem(hsm,IDM_ABOUT,MF_UNCHECKED);	//取消選中
					m=0;
				}
				break;
			case IDM_HELP:
				CheckMenuRadioItem(hsm,IDM_ABOUT,IDM_HELP,IDM_HELP,MF_BYCOMMAND);	//單選項
				break;
			default:
				break;
			}
		}
		return 0;
	case WM_DESTROY:
		{
			PostQuitMessage(0);	//終止程式
			return 0;
		}
	}
	return DefWindowProc(hwnd,umsg,wparam,lparam);
}

至此,選單就先學到這裡了。