windows基础应用程序编程(四):菜单

添加菜单

凡是接触过windows系统的,我想对菜单都不会感到陌生,菜单是Windows应用程序提供刚给一致用户界面的很重要的部分。(还记得窗口类结构中的lpszMenuName项吗?)
想要在程序中使用菜单,我们首先需要定义菜单资源,打开VS2010,新建一个Win32的空项目,新建一个源文件并把之前的windows应用程序的通用框架复制粘贴上去。然后,我们需要再新建一个资源脚本文件,在解决方案资源管理器中,右键单击资源文件,选择添加->新建项,如下图所示:
windows基础应用程序编程(四):菜单_第1张图片
在弹出的对话框中选择资源文件(.rc)。输入名称后,点击添加。然后在资源视图中,右键单击Menu(你定义的资源文件名称).rc,选择添加资源,如下图所示:
windows基础应用程序编程(四):菜单_第2张图片
在弹出的添加资源对话框中,我们可以看到由很多资源类型:我们选择Menu,单击新建,为我们的程序来添加一个菜单资源。这时,我们会进入程序的菜单编辑界面。我们可以再此来编辑我们想要的菜单,需要注意的是,当我们在编辑每一个菜单项的时候,属性窗口对应着该菜单性的属性,如下所示:
windows基础应用程序编程(四):菜单_第3张图片
其中,主要项的含义如下,Caption为显示在对应菜单栏中的名称,当我们在键入文字的时候可以键入一个“&”符,如上图所示,这个符号表示后面的一个字符在Windows显示菜单时要加上下划线。这样我们在运行程序的时候就可以通过Alt+&后的字符来打开对应的菜单项了。Checked选项在菜单项边上放置一个浮选标记。Grayed表示该菜单项为灰色不可用状态,Popup表示该菜单项还可以弹出一个新的子菜单。ID为该菜单项对应的ID值,在程序中用以区分是哪个菜单项发出的消息。Separator表示在弹出式菜单上产生一个分栏的横线。大家可以自己去设置,然后观察一下会有哪些效果。在这里不一一讲解。
编辑好我们的菜单之后,我们点击生成->编译。然后回到我们的解决方案资源管理器,我们会看到在我们的头文件中会多了一个resource.h的头文件。该文件中定义了一些我们的资源ID。那么现在想要使用我们的菜单资源,我们还需要在我们的程序中去引用菜单。首先我们需要在源程序中包含我们的资源头文件#include "resource.h"。之后我们可以在窗口类中指定菜单,例如下面所示:
wcex.lpszMenuName = MAKEINTRESOURCE(IDR_MENU1);
MAKEINTRESOURCE是一个宏,把整数改制成资源串,生成指向非数字的指针。IDR_MENU1即是我们的菜单ID。我们可以在资源视图中看到,当然自己也可以手动去修改该ID值。完成之后,我们编译运行程序,可以看到此时我们的程序已经把菜单添加进来了,当然此时点击菜单还得不到任何反应。
在我们的程序中,有很多时候,我们并不在窗口类中指定菜单,而是使用另外一种方法,我们使用LoadMenu函数来把菜单资源加载到内存中,同LoadIcon和LoadCursor函数一样,LoadMenu返回一个菜单句柄。我们可以在程序中调用下句代码。
HMENU hMenu = LoadMenu( hInstance, MAKEINTRESOURCE(IDR_MENU1));
然后,将这个菜单句柄作为CreateWindow的第9个参数传进去。还记得CreateWindow函数的各个参数的意义吗?
值得注意的是,在CreateWindow调用中指定的菜单可以覆盖窗口类中指定的任何菜单,也就是说,如果我们CreateWindow函数中调用的菜单如果和窗口类中指定的不一样的话,那么将以CreateWindow函数中指定的为准。(我们在程序中可以定义多个菜单,每个菜单通过其ID来标识。)
如果我们需要动态的修改窗口的菜单,比如我们需要在程序的运行中修改菜单,那么我们可以再窗口的创建后使用
SetMenu(hwnd, hMenu)来指定一个菜单。
当我们选择一个菜单项的时候,Windows通常向窗口过程发送几个不同的消息。这些消息,一般情况下,我们直接交给DefWindowProc函数处理即可,菜单消息中最重要的是WM_COMMAND消息,(通常,我们只处理这一个消息),WM_COMMAND也可以由子窗口控制产生,我们通过lParam的值来加以区分,菜单项的lParam的值为0。菜单项lParam值为0。WM_COMMAD消息的参数如下所示:
LOWORD(wParam):菜单ID
HIWORD(wParam):0
lParam:0

示例:

下面我们来完成一个通过菜单项来更改程序颜色背景的小程序,即更改画刷。首先,我们需要在菜单中定义如下:
windows基础应用程序编程(四):菜单_第4张图片
ID号分别为ID_BKGND_WHITE、ID_BKGND_LTGRAY、ID_BKGND_GRAY、ID_BKGND_BLACK。其中ID_BKGND_WHITE项Check属性设置为True。
其中,窗口过程处理函数如下:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
                HMENU hMenu;
                 static int iSelection = ID_BKGND_WHITE;
                 static int idColor[4] = { WHITE_BRUSH, LTGRAY_BRUSH, GRAY_BRUSH, BLACK_BRUSH };
                 switch (message)
                {
                 case WM_COMMAND:
                                hMenu = GetMenu(hWnd);
                                 switch(LOWORD(wParam))
                                {
                                 case ID_BKGND_WHITE:
                                 case ID_BKGND_LTGRAY:
                                 case ID_BKGND_GRAY:
                                 case ID_BKGND_BLACK:

                                                CheckMenuItem( hMenu, iSelection, MF_UNCHECKED );
                                                iSelection = LOWORD(wParam);
                                                CheckMenuItem( hMenu, iSelection, MF_CHECKED );

                                                SetClassLong( hWnd, GCL_HBRBACKGROUND, (LONG)
                                                                GetStockObject( idColor[LOWORD(wParam) - ID_BKGND_WHITE]));

                                                InvalidateRect( hWnd, NULL, TRUE );
                                                 return 0;
                                }

                 case WM_DESTROY:
                                PostQuitMessage(0);
                                 break;
                }
                 return DefWindowProc(hWnd, message, wParam, lParam);
}
其中,CheckMenuItem函数用来设置菜单项前一个√号复选框。第一个参数为菜单句柄,第二个参数为菜单项ID,第三项为标记,MF_UNCHECKED表示没选上,MF_CHECKED表示选上。
我们通过SetClassLong函数来设置画刷,画刷通过GetStockObject函数来获得。OK,现在运行程序,就可以看到我们想要的运行结果了。
还需要提到的一个消息是WM_INITMENUPOPUP,这个消息是当Windows准备显示一个弹出式菜单的时候所发送的。以后我们需要在菜单显示之前启用或者禁用某些菜单项时,我们就需要捕获这个消息了,在之后的程序中,我们会用到这个消息。

纯粹的方式

提到菜单,不得不提的是,我们不仅可以使用资源描述文件中,就像上面所提到的那样。但是我们依然可以不使用资源描述文件,而仅使用代码,我们依然可以再程序中创建菜单。虽然,我们一般并不这么做,但是了解这些函数,还是非常有必要的。
CreateMenu()函数简单的把一个句柄返回给新菜单。
AppendMenu()函数将菜单项插入菜单中
我们可以这样来使用

hMenu = CreateMenu();
hMenuPopup = CreateMenu();,
AppendMenu(hMenuPopup, MF_STRING, IDM_FILE_NEW, "&New");
AppendMenu(hMenuPopup, MF_STRING, IDM_FILE_OPEN, "&Open" );
AppendMenu(hMenuPopup, MF_SEPARATOR, 0);
AppendMenu(hMenuPopup, MF_STRING, IDM_APP_EXIT, "E&xit");

AppendMenu(hMenu, MF_POPUP, hMenuPopup, "&File");
...

系统菜单

运行上面的程序,在标题栏上单击右键,我们会发现也弹出了一个菜单,这个菜单我们称之为系统菜单。我们当然也可以修改这个菜单。但是需要注意的是,我们在往系统菜单中添加的命令的ID值必须小于0xF000;否则他们将会同Windows系统菜单命令所使用的ID值相冲突。我们继续在上面代码的程序中修改,以达到在系统菜单中添加一个分隔符,关于、帮助、和清除添加菜单项的命令。
首先我们需要在程序的开始处定义ID,如下所示:

之后,我们需要在CreateWindow函数后,来添加我们修改系统菜单的代码。如下所示:
windows基础应用程序编程(四):菜单_第5张图片
之后增加系统菜单消息的处理,系统菜单向窗口处理函数发送WM_SYSCOMMAND消息。
我们需要在窗口处理函数中添加以下代码:
windows基础应用程序编程(四):菜单_第6张图片
现在OK了,运行程序,右键单击标题栏,会发现我们成功的修改了系统菜单。需要注意的是,我们第一次调用GetSystemMenu函数,是为了为修改菜单做准备,将第二个参数设置为FALSE。当我们需要删除我们附加的菜单项时,我们需要把第二个参数设置为TRUE,再次调用GetSystemMenu即可。如上面所示。

常用的菜单命令

上面我们已经使用了AppendMenu函数和CheckMenuItem函数,Windows还为我们提供了一些关于菜单操作的函数,常用的一些函数如下:
  • DeleteMenu 删除菜单中一个现有的菜单项,并清除该项。
  • InsertMenu 在菜单中插入一个新项。
  • ModifyMenu 修改一个现有的菜单项。
  • RemoveMenu 从菜单中删除某一项,但不清除。
  • DrawMenuBar 当改变顶层菜单项时,需要调用改函数来重画菜单条,参数为窗口句柄hWnd。
  • GetSubMenu 获得弹出式菜单的句柄。
  • GetMenuItemCount 获得顶层菜单或者弹出式菜单中的当前项数。
  • GetMenuItemID 获取弹出式菜单项的菜单ID。
  • GetMenuString 获得菜单中使用的字符串。
  • GetMenuState 获得菜单项的当前属性。
  • DestroyMenu 清除菜单,使菜单句柄无效。

关于菜单就总结到这,坚持不懈!加油!


你可能感兴趣的:(windows基础应用程序编程)