學習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);
}
至此,選單就先學到這裡了。