特殊说明:版权归个人所有,请勿转载,谢谢合作。
本节重点讲解菜单、对话框,通过这两个资源的讲解,会对其他资源进行简单的描述。位图资源在应用软件中应用较为广泛,会在第9章重点讲解。
8.4.1 菜单资源
菜单是Windows图形界面窗口中,最重要的组成部分。菜单通常出现在主窗口的顶部,用文字或图标列出各种选项,供用户选择或操纵。菜单不仅可以直观地了解应用程序提供的各项功能,而且通过加速键资源,可以使菜单的操作更加灵活。菜单分为两种类型:普通菜单以及弹出式菜单。普通菜单由以下五个部分组成(如图8.11所示):
- 菜单栏;
- 下拉式菜单;
- 热键标识;
- 加速键标识;
- 菜单项分隔线。
- 图8.11 普通菜单组成
弹出式菜单可在桌面或应用程序窗口随意弹出,但它没有窗口菜单栏。弹出式菜单,通常使用鼠标右键盘弹出,如图8.12所示。
- 图8.12 弹出式菜单
在Win32 SDK编程中,菜单的应用需要一个过程,在资源视图中添加菜单资源,即在资源视图中,点击鼠标右键,在弹出的菜单中,选择【Insert…】项,如图8.13所示。
- 图8.13 插入菜单项
在弹出的插入资源对话框中,选择【Menu】项,并点击【New】按钮,确认创建,如图8.14所示。
- 图8.14 选择插入菜单资源
在新创建的窗口中,点击鼠标右键,在新弹出的菜单中,选择【Properties】项,操作如图8.15所示。
- 图8.15 菜单属性
在弹出的菜单项属性窗口中的【Caption】处输入“操作(&O)”,此处输入的是顶级菜单显示的内容项。其中“(&O)”是菜单的热键,按ALT+O即可展开菜单。当然也可以输入其他内容,操作如图8.16所示。
- 图8.16 顶层菜单名称
项层菜单输入完成后,即可输入子菜单项,在出现的空白菜单处,点击鼠标右键,在弹出的菜单项中,选择【Properties】项,操作如图8.17所示。
- 图8.17 菜单属性
在弹出的菜单项属性窗口中,在【ID】处输入菜单的ID,本例输入“ID_OPTION_START”(要注意的是此ID名称要唯一,根据代码规范,ID要大写),并在【Caption】处输入菜单名称“开始(&S)”,如图8.18所示。
- 图8.18 子项菜单ID与名称
以同样方式,添加子菜单项“结束(&E)”与“退出(&X)”,并将ID分别设置为“ID_OPTION_END”与“ID_OPTION_EXIT”。将结束与退出功能之前加分隔符,分隔符添加方法是在菜单项属性中,选择【Separator】即可,如图8.19所示。
- 图8.19 设置分隔线
最终设置结果如图8.20所示。
- 图8.20 菜单设置结果
菜单设置完成后,根据代码规范的要求,还要将菜单的ID进行更改(这里是菜单的ID,并不是菜单子项的ID)。在资源视图中,选择【Menu】资源中的菜单项【IDR_MENU1】,点击鼠标右键,在弹出的菜单中,选择【Properties】项,在弹出的菜单属性中,将【ID】内容更改为“IDR_MAIN_MENU”,如图8.21所示。
- 图8.21 更改菜单ID
到目前为止,菜单项已经添加完成,但需要将菜单加载到应用程序中,加载方法有两种,菜单的一种加载方法是在设计窗口类时,将WNDCLASSEX结构体的lpszMenuName参数设置为“IDR_MAIN_MENU”。需要注意的是,IDR_MAIN_MENU的类型与lpszMenuName参数的类型是不匹配的,所以需要进行强制类型转换,如图8.22所示。
- 图8.22 加载菜单
程序运行后的结果如图8.23所示。
- 图8.23 菜单加载完成
菜单的第二种加载方法是,使用LoadMenu函数加载菜单,使用SetMenu函数设置菜单,最后使用DestroyMenu函数,实现菜单的销毁。三个函数的原型如下:
HMENU LoadMenu(HINSTANCE hInstance, LPCTSTR lpMenuName); BOOL SetMenu(HWND hWnd, HMENU hMenu); BOOL DestroyMenu(HMENU hMenu);
虽然菜单已经加载成功,但菜单没有任何响应。Win32中,当菜单项被选择、按钮被按下、或者其他控件被操作会产生命令响应消息(WM_COMMAND)。在响应WM_COMMAND消息时,wParam参数为菜单项、或按钮的ID值,而lParam参数为控件(控件的概念将在第10章节中讲解)的句柄。【例8-1】示例,当点击“开始”与“结束”菜单项时,分别弹出相应的提示框,点击“退出”时退出系统。
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { PAINTSTRUCT ps; // 结构体包含了用于绘制 // 窗口客户区的信息 HDC hdc; // 设备环境句柄 // 消息处理 // switch (message) { // 菜单响应事件 case WM_COMMAND: if(wParam == ID_OPTION_START) { MessageBox(hWnd, "点击了开始菜单项。", "提示", MB_OK); } else if(wParam == ID_OPTION_END) { MessageBox(hWnd, "点击了结束菜单项。", "提示", MB_OK); } else { // 退出系统 DestroyWindow(hWnd); }//end if break; // 图形绘制事件 case WM_PAINT: hdc = BeginPaint(hWnd, &ps); EndPaint(hWnd, &ps); break; // 窗口销毁消息,关闭窗口时响应。 case WM_DESTROY: PostQuitMessage(0); break; default: // 调用系统默认消息处理,即交给系统处理。 return DefWindowProc(hWnd, message, wParam, lParam); }//end switch return 0; }
程序中,捕获了WM_COMMAND消息,通过wParam参数来判断当前选择了哪个菜单项,并对相应的菜单项作出响应,执行结果如图8.24所示。
- 图8.24 菜单示例
8.4.2 对话框资源
对话框是一种特殊的窗口,通常包含编辑框、按钮、单选按钮(radio button)、复选框(check box)和其他控件。用户可以通过对话框输入数据或选择某些功能。如果一个应用程序中包含对话框,那么就必须包含对话框的过程处理函数,这个过程处理函数与应用程序的过程处理函数相同,主要目的是捕获对话框所产生的消息。
对话框是一种非常重要的资源,Windows中大部分应用功能,都是以对话框模式进行交互的。对话框窗口分为两种:模式对话框窗口与无模式对话框窗口。模式对话框窗口也称为模态对话框窗口,是指用户在Win32应用程序的对话框中,想要对对话框以外的应用程序进行操作时,必须先对该对话框进行响应,如单击【确定】或【取消】按钮等操作将该对话框关闭。相对应的另一个对话框是无模式对话框,即可以在不关闭当前的对话框窗口的情况下操作其他窗口。
在资源文件中,定义对话框资源后,即可使用DialogBox函数来显示对话框。DialogBox函数原型如下:
int DialogBox( HINSTANCE hInstance, // handle to application instance LPCTSTR lpTemplate, // identifies dialog box template HWND hWndParent, // handle to owner window DLGPROC lpDialogFunc // pointer to dialog box procedure );
参数hInstance,当前进程的实例句柄。
参数lpTemplate,标识对话框模板。此参数可以是指向一个以NULL结尾的字符串的指针,该字符串指定对话框模板名,或是指定对话框模板的资源标识符中的一个整型值。可以使用MAKEINTRESOURCE宏指令创建此值。
参数hWndParent,父窗口的句柄,如果为NULL说明此窗口无父窗口,从而将变成无模式对话框。
参数lpDialogFunc,响应该对话框消息的回调函数。
下面是基于8.4.1节的示例做了一些简单的修改,当点击菜单上的开始项时,弹出相应的对话框(如何创建对话框资源,在8.3节已经详细地讲解过,这里不过多叙述)。
第一步,根据代码规范要更改对话框的ID。在资源视图中,选择对话框资源,点击鼠标右键,在弹出的菜单中选择【Properties】项,如图8.25所示。
- 图8.25 修改对话框ID
在弹出的对话框属性对话框中的【ID】处,更改ID内容为“IDD_START_DLG”,如图8.26所示。
- 图8.26 修改后的ID
第二步,在程序首部,声明对话框的过程处理函数(函数名任意,这里以DlgProc为例)。此函数用来处理对话框所产生的所有消息事件。代码如下所示:
// 回调函数,用于处理对话框的消息事件。 LRESULT CALLBACK DlgProc(HWND, UINT, WPARAM, LPARAM);
第三步,对话框的过程处理函数的实现。此函数与应用程序的过程处理函数有所不同,因为对话框属性与窗口所有不同,所以处理方法也不相同。对话框只关心上面的按钮即可,如下代码所示,在WM_COMMAND消息中,捕获了按钮ID“IDOK”与“IDCANCEL”,如果在窗口中,点击了这两个按钮,则使用EndDialog函数关闭对话框(要注意,关闭对话框的函数是EndDialog)。
LRESULT CALLBACK DlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_INITDIALOG: return TRUE; case WM_COMMAND: if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) { EndDialog(hDlg, LOWORD(wParam)); return TRUE; } break; } return FALSE; }
第四步,在应用程序的回调函数中,将开始按钮的消息处理进行修改,使用DialogBox函数将对话框创建并显示,代码如下所示。
if(wParam == ID_OPTION_START) { //MessageBox(hWnd, "点击了开始菜单项。", "提示", MB_OK); DialogBox(g_hIns, (LPCTSTR)IDD_START_DLG, hWnd,(DLGPROC)DlgProc); }
第五步,如果此时编译,系统会出现错误,原因是DialogBox函数的第一个参数,它是进程实例句柄类型,这个值需要的是当前进行的实例句柄,即WinMain函数的第一个参数,所以需要定义全局变量g_hIns,并将WinMain函数的hInstance句柄进行保存,代码如下所示。
// 在程序的首部,定义全局变量,用来存放WinMain函数的hInstance句柄。 HINSTANCE g_hIns = NULL;
同时在WinMain函数的开始部分,将hInstance句柄进行保存,代码如下所示。
// 保存进程实例句柄 g_hIns = hInstance;
【例8-2】是实现对话框功能的全部代码(修改的位置已经加入底纹)。
//************************************************************ // NAME : Demo_08.cpp //************************************************************ // POWER : Copyright (c) 2012 for lixinghua. // AUTHOR : 2012-7-24 20:00 Create by lixinghua for functions. // VERSION : V1.0.0.1 // NOTE : 手工整理的创建窗口的代码,本程序只创建一个名称为 // MyWin的窗口。 //************************************************************ // #include <windows.h> #include <stdio.h> #include "resource.h" // 回调函数,用于系统消息的处理。 LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); // 回调函数,用于处理对话框的消息事件。 LRESULT CALLBACK DlgProc(HWND, UINT, WPARAM, LPARAM); HINSTANCE g_hIns = NULL; //----------------------------------------------------------------------------- // FUNC : 入口函数 //----------------------------------------------------------------------------- // IN : hInstance,进程的实例句柄; // hPrevInstance,前一个进程实例句柄,默认为NULL即可; // lpCmdLine,命令行参数; // nCmdShow,当前窗口显示状态。 // OUT : void // RETURN : 返回为整型,代表窗口的状态,其中APIENTRY描述了压栈的顺序。 // AUTHOR : 2012-2-6 11:18 Create by lixinghua for functions. // NOTE : 此函数为Win32入口函数。 //----------------------------------------------------------------------------- // int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { // 定义所用到的参数 // char szWindowClass[] = { "WinClsName" }; // 窗口类的名称 HWND hWnd = NULL; // 用于存放窗口句柄 //char szTitle[] = { "五子棋" }; // 窗口标题名称 char szTitle[] = { "MyWin" }; // 窗口标题名称 MSG msg; // 存放消息的结构体, // 由系统提供 // 保存进程实例句柄 g_hIns = hInstance; // 1. 设计一个窗口类 // WNDCLASSEX wcex; wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = (WNDPROC)WndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; wcex.hIcon = NULL; wcex.hCursor = LoadCursor(NULL, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); wcex.lpszMenuName = (LPCSTR)IDR_MAIN_MENU; wcex.lpszClassName = szWindowClass; wcex.hIconSm = NULL; // 2. 注册窗口 // RegisterClassEx(&wcex); // 3. 创建窗口 // HMENU hMenu = NULL; hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, hMenu, hInstance, NULL); // 判断创建是否成功 if (!hWnd) { return FALSE; }//end if // 4. 显示并更新窗口 ShowWindow(hWnd, nCmdShow); // 请注意 nCmdShow 参数 UpdateWindow(hWnd); // 5. 进入消息循环 while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); }//end while return 0; } //----------------------------------------------------------------------------- // FUNC : 回调函数 //----------------------------------------------------------------------------- // IN : hWnd,窗口句柄; // message,要处理的消息ID,以此来区分消息; // wParam,消息参数,根据消息的不同内容也有所不同; // lParam,消息参数,根据消息的不同内容也有所不同。 // OUT : void // RETURN : void // AUTHOR : 2012-2-6 11:36 Create by lixinghua for functions. // NOTE : 此函数用于系统消息的处理。 //----------------------------------------------------------------------------- // LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { PAINTSTRUCT ps; // 结构体包含了用于绘制窗口客 // 户区的信息 HDC hdc; // 设备环境句柄 // 消息处理 // switch (message) { // 菜单响应事件 case WM_COMMAND: if(wParam == ID_OPTION_START) { //MessageBox(hWnd,"点击了开始菜单项。","提示", MB_OK); DialogBox(g_hIns, (LPCTSTR)IDD_START_DLG, hWnd, (DLGPROC)DlgProc); } else if(wParam == ID_OPTION_END) { MessageBox(hWnd, "点击了结束菜单项。", "提示", MB_OK); } else { // 退出系统 DestroyWindow(hWnd); }//end if break; // 图形绘制事件 case WM_PAINT: hdc = BeginPaint(hWnd, &ps); EndPaint(hWnd, &ps); break; // 窗口销毁消息,关闭窗口时响应。 case WM_DESTROY: PostQuitMessage(0); break; default: // 调用系统默认消息处理,即交给系统处理。 return DefWindowProc(hWnd, message, wParam, lParam); }//end switch return 0; } //----------------------------------------------------------------------------- // FUNC : 对话框回调函数 //----------------------------------------------------------------------------- // IN : hWnd,窗口句柄; // message,要处理的消息ID,以此来区分消息; // wParam,消息参数,根据消息的不同内容也有所不同; // lParam,消息参数,根据消息的不同内容也有所不同。 // OUTPUT : NULL // RETURN : VOID // AUTHOR : 2012-7-25 14:47 Create by lixinghua for functions. // NOTE : 此处用于处理对话框的消息事件。 //----------------------------------------------------------------------------- // LRESULT CALLBACK DlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_INITDIALOG: return TRUE; case WM_COMMAND: if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) { EndDialog(hDlg, LOWORD(wParam)); return TRUE; } break; } return FALSE; }
程序执行结果如图8.27所示。
- 图8.27 执行结果