windows程序设计之菜单简介

 菜单是很重要的资源,我就大概看了些菜单,总结了下菜单的基本使用。

要引入菜单资源:
Resource Script 资源脚本 添加到工程里面,这里要注意,产生后出现两个文件,这里假设是menu.rc和rescource.h,由于rc是资源文件的
集合,能包含图标,菜单,字符文件资源等等,也可以包含多个菜单。所以你在引用菜单的时候一定要把菜单的名字和你指定的引用的相同,你可以用文本编辑器直接打开.rc文件或者.h文件。菜单的名字以“MenuDemo”为例,要加""。菜单对应的值是操作系统规定的,是101。可以打开.h文件看看.

 

关于菜单的基本知识,菜单的三个特性:1.在菜单中显示什么,可以是字符串或者是位图形式。2.WM_COMMAND消息中windows发给程序的ID,或者是用户选择菜单项时windows显示的弹出式菜单的句柄。3.菜单项的属性,包括是否禁用,灰化或者是否被选择。在属性选中了Pop-up的话就不会产生WM_COMMAND的消息,并且和ID无关,就比如我们记事本的菜单栏,你点击“文件”,然后弹出下拉列表,"文件"这个菜单项属性是被设置成Pop-up的,所以就不产生WM_COMMAND的消息,你在写菜单属性的时候也是不能指定其ID的。点击之后仅仅是弹出下列的子菜单。另外关于菜单快捷键的设置,就比如说文件有下划线,你可以在写字符串的时候前面加&即可。windows会使用菜单项文本的第一个字母用于Alt键查找。

如果你在菜单属性设置里面复选Grayed选项,则该项是不能激活的,是灰化的,不产生WM_COMMAND的消息。选择Inactive选项,也是一样,不过能正常显示文本。还有就是选择Separator就会有分栏的线。
windows处理菜单消息比较多的就是WM_COMMAND消息。LOWORD(wParam)就是菜单的ID。

 

下面就开始怎么引用菜单:

第一种方法:
在注册窗口类的时候就指定要菜单,在WNDCLASSEX结构体的成员lpszMenuName中,假设我把引入菜单的叫做"MenuDemo",
那么就可以这样引用:
TCHAR szAppName[] = TEXT ("MenuDemo")
//code
wndclass.lpszMenuName  = szAppName ;
在这里就指定了你要的菜单,在窗口建立之前就已经包含在窗口类中了,建立窗口的时候直接就出来菜单。这种方法比较简单,所以 在一般情况下都用这种方法引用菜单资源。但是这种方法有一种缺陷:由于是在窗口类中指定的,所有由该窗口类派生出的窗口都有
相同的菜单(一猪生九仔,连母十个样(*^__^*) );
第二种方法:
HMENU hMenu
TCHAR MenuName[] = TEXT("MenuDemo");
//code
hMenu = LoadMenu(hInstance,MenuName);
hWinMain = CreateWindowEx(WS_EX_CLIENTEDGE,szClassName,szCaptionMain,
WS_OVERLAPPEDWINDOW,100,100,600,400,NULL,hMenu,hInstance,NULL);
通过在CreateWindowsEx第九个参数指定要引入的菜单。CreateWindow调用中的指定菜单可以覆盖窗口类中指定的任何菜单。
第三种方法:
如果我们把CreateWindowEx第九个参数设置为NULL,并且也不在窗口类中指定NULL菜单,就可以在窗口被创建后在给窗口指定指定一个菜单:

SetMenu(hwnd,hMenu);
举个例子 ,在消息处理函数内 static HMENU hMenuFile1,hMenuFile1;
在WM_CREATE:
hInstance = (HINSTANCE)GetWindowsLong(hwnd,GWL_HINSTANCE);
hMenuFile1 = LoadMenu(hInstance,TEXT("MenuMain1");    //举个例子
hMenuFile2 = LoadMenu(hInstance,TEXT("MenuMain2");面选择资源脚本多次,每个菜单都不同的名字。每当窗口过程处理WM_CREATE消息
的时候windows就会动态的白菜单资源加载到内存中去。SetMenu(hwnd,hMenu)让程序显示hMenu指向的菜单,然后你可以任意
SetMenu(hwnd,hMenuFile1)或者SetMenu(hwnd,hMenuFile2)之间切换,在windows程序设计上面通过按键来实现,这样你甚至可以多创建几个菜单。

 

下面是一小段简单的代码,采用第一张方法引入菜单资源。

#include <windows.h>
#include "resource.h"

LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
void DisplayMenuItem(DWORD dwCommandID);

TCHAR szFormat[] = "您选择了菜单命令:%d";
TCHAR szAppName[] = TEXT ("MenuDemo") ;
TCHAR szCaption[32] = {'�'};

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
HWND hwnd ;
MSG msg ;
WNDCLASS wndclass ;

wndclass.style = CS_HREDRAW | CS_VREDRAW ;
wndclass.lpfnWndProc = WndProc ;
wndclass.cbClsExtra = 0 ;
wndclass.cbWndExtra = 0 ;
wndclass.hInstance = hInstance ;
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
wndclass.lpszMenuName = szAppName ;
wndclass.lpszClassName = szAppName ;

if (!RegisterClass (&wndclass))
{
MessageBox (NULL, TEXT ("This program requires Windows NT!"),
szAppName, MB_ICONERROR) ;
return 0 ;
}

hwnd = CreateWindow (szAppName, TEXT ("Menu Demonstration"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL) ;

ShowWindow (hwnd, iCmdShow) ;
UpdateWindow (hwnd) ;

while (GetMessage (&msg, NULL, 0, 0))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
return msg.wParam ;
}
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HMENU hMenu ;
switch (message)
{
case WM_COMMAND:
hMenu = GetMenu (hwnd) ;
DisplayMenuItem(wParam);
switch (LOWORD (wParam))
{
case ID_FILE_SAYHELLO:
MessageBox(hwnd,TEXT("HELLO"),szAppName,MB_OK);
return 0;
case ID_FILE_EXIT:
SendMessage(hwnd,WM_CLOSE,0,0);//这里也可以发送消息WM_DESTROY,消息循环结束。
return 0;
case ID_ABOUT_HELP:
MessageBox(hwnd,TEXT("This My Menun Hello"),szAppName,MB_OK);
return 0;
}
case WM_DESTROY:
PostQuitMessage (0) ;//产生WM_QUIT消息,GetMessage得到WM_QUIT返回0,消息循环终止,退出程序
return 0 ;
}
return DefWindowProc (hwnd, message, wParam, lParam) ;
}

void DisplayMenuItem(DWORD dwCommandID)
{
char szBuffer[256] = {0};
wsprintf(szBuffer,szFormat,dwCommandID);
MessageBox(NULL,szBuffer,szCaption,MB_OK);
}

这里浅谈一下WM_CLOSE和WM_DESTORY,前面说到GetMessage不停的接收消息,除非接受到WM_QUIT,否则就返回非0,一旦接受到了WM_QUIT就返回0,消息循环终止,程序停止。 那么是谁发送了WM_QUIT呢?就是PostQuitMessage(0),一般这句代码在
WM_DESTROY消息处理里面。那么为什么我SendMessage参数是WM_CLOSE也是可以的呢?程序没有设置WM_CLOSE处理程序,交给默认处理程序。默认处理函数对于WM_CLOSE的处理方式为调用::DestoryWindow,并因而发出WM_DESTORY消息。从而就发出   PostQuitMessage(0)再是消息循环停止。

 

上面只做了一种最简单的菜单引入方法,关于后面两种方法比较灵活,也能根据自己爱好进行扩展。

 

参考资料《windows程序设计》

              《windows+sdk系列》

标签:SDK