《Windows游戏编程大师技巧》学习笔记(五)

创建菜单

菜单的配置与前述各类资源一样,编写在.RC资源脚本中,同时,和字符串资源相似,菜单各项的标识符也必须是整数ID,而不能是名称字符串;
一个简单的菜单配置如下:

1
2
3
4
5
6
7
// 在头文件 RESOURCES.H 中
#define MENU_FILE_ID_OPEN 1000
#define MENU_FILE_ID_CLOSE 1001
#define MENU_FILE_ID_SAVE 1002
#define MENU_FILE_ID_EXIT 1003

#define MENU_HELP_ABOUT 2000
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 在资源脚本 xxx.RC 中
MainMenu MENU DISCARDABLE
{
POPUP "File"
{
MENUITEM "Open", MENU_FILE_ID_OPEN
MENUITEM "Close", MENU_FILE_ID_CLOSE
MENUITEM "Save", MENU_FILE_ID_SAVE
MENUITEM "Exit", MENU_FILE_ID_EXIT
}

POPUP "Help"
{
MENUITEM "About", MENU_HELP_ABOUT
}
}

上述代码可以生成如下图所示的菜单栏:

带有两个下拉菜单的菜单条

作者在书中写道“关键字DISCARDABLE是过时了但还是必须的”,但是实测在VisualStudio2019环境下此声明是不需要的
与其他资源的定义相同,花括号可以被替换为BEGIN...END对;
如果需要设置热键或配合Alt触发的快捷键,只需要在POPUP菜单或MENUITEM字符串中将&放到想要标识的快捷键字符前面,如下:

1
2
3
4
5
// 使 x 成为热键
MENUITEM "E&xit", MENU_FILE_ID_EXIT

// 设置可以通过 Alt+F 触发的快捷键
POPUP "&File"

装载菜单

装载菜单的方式有如下三种:

1. 初始化Windows类时指定菜单名

前文中所定义的菜单资源,就可以通过如下方式加载:

1
winclass.lpszMenuName = "MainMenu";

与其他资源同理,如果”MainMenu”被定义为了整数ID时,则需要使用MAKEINTRESOURCE宏进行处理,后文不再赘述;
在初始化Windows类时指定菜单名,如果使用当前类创建了多个窗口,则这些窗口具有相同的菜单;

2. 创建窗口时指定菜单句柄

可以使用LoadMenu函数动态加载资源中的菜单,在非Unicode环境下函数原型如下:

1
2
3
4
HMENU WINAPI LoadMenuA(
HINSTANCE hInstance, // 应用程序句柄
LPCSTR lpMenuName // 菜单名称
);

函数加载返回的菜单句柄,可以传入CreateWindowEx函数中,在创建窗口时指定菜单;

3. 动态设置菜单

可以使用SetMenu函数动态设置已存在窗口的菜单,函数原型如下:

1
2
3
4
BOOL WINAPI SetMenu(
HWND hWnd, // 窗口句柄
HMENU hMenu // 菜单资源句柄
);

使用此方式设置的菜单优先级是最高的,可以覆盖替换在初始化时指定的菜单;

响应菜单事件消息

当菜单项被点击时,消息依然是被事件回调函数进行处理,如下图所示:

窗口菜单选择消息流

参数对应关系如下:

  • msg:一定为WM_COMMAND
  • lparam:发出消息的窗口句柄;
  • wparam:选中的菜单项ID;

使用LOWORD宏从wparam中提取低位的WORD再进行处理更安全;
剩下的操作便是用switch语句来处理wparam的低位值就可以了,参考前文中定义的菜单项,一个可能的事件回调函数如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
PAINTSTRUCT ps;
HDC hdc;

switch (msg)
{
case WM_CREATE:
// 在此处执行初始化操作
return 0;
case WM_COMMAND:
switch (LOWORD(wparam))
{
case MENU_FILE_ID_OPEN:
// 在此处填写逻辑代码
break;
case MENU_FILE_ID_CLOSE:
// 在此处填写逻辑代码
break;
case MENU_FILE_ID_SAVE:
// 在此处填写逻辑代码
break;
case MENU_FILE_ID_EXIT:
// 在此处填写逻辑代码
break;
case MENU_HELP_ABOUT:
// 在此处填写逻辑代码
break;
default: break;
}
return 0;
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
// 在此处执行绘图操作
EndPaint(hwnd, &ps);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
default:
// 调用默认的消息回调处理我们不关心的事件
return DefWindowProc(hwnd, msg, wparam, lparam);
}
}

上述代码仅展示了菜单项被选中时的常见处理操作,还有其他用于操作一级菜单和菜单项本身的消息,更多内容见Win32SDK - 菜单通知

作者

Voidmatrix

发布于

2022-01-30

更新于

2022-01-30

许可协议

评论