看到别人做菜单有很多种,而我就比较不要麻烦就用较简单的来学习下吧,好用就行了。
一、用编辑资源来添加菜单
我用的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);
}
至此,菜单就先学到这里了。