博客
关于我
《Windows程序设计》读书笔十 菜单和其他资源
阅读量:375 次
发布时间:2019-03-04

本文共 49496 字,大约阅读时间需要 164 分钟。

第十章  菜单和资源

windows通过LoadIcon LoadCursor等函数来加载资源

图标

鼠标指针

字符串

自定义资源

菜单

键盘加速键

对话框

位图

10.1  图标,鼠标指针,字符串和自定义资源

10.1.1 向程序添加图标

Tools->Options->Build->Export makefile when saving project file.

导出mak文件

注:该方法在VS2015中已经不可用。VS2015需要自己写makefile或者使用makefile project来生成mak文件,然后使用nmake编译文件。

nmake的配置方法参考

http://www.cnblogs.com/cvbnm/articles/1954872.html

源代码

#include 
#include "resource.h"LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //window procedure. int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow){ static TCHAR szAppName[] = TEXT("IconDemo"); HWND hwnd; MSG msg; WNDCLASS wndClass; //The window Class wndClass.style = CS_HREDRAW | CS_VREDRAW; wndClass.lpfnWndProc = WndProc;// assign the window procedure to windows class. wndClass.cbClsExtra = 0; wndClass.cbWndExtra = 0; wndClass.hInstance = hInstance; wndClass.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON));//LoadIcon(NULL, IDI_APPLICATION); wndClass.hCursor = LoadCursor(NULL, IDC_ARROW); wndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wndClass.lpszMenuName = NULL; wndClass.lpszClassName = szAppName; //Register the Window Class to the Windows System. if (!RegisterClass(&wndClass)) { MessageBox(NULL, TEXT("This program require Windows NT!"), szAppName, MB_ICONERROR); return 0; } //This function will generate an WM_CREATE message. hwnd = CreateWindow(szAppName, //Window class name TEXT("Icon Demo"), //Window caption WS_OVERLAPPEDWINDOW, //Window Style CW_USEDEFAULT, //initial x position CW_USEDEFAULT, //initial y position CW_USEDEFAULT, //initial x size CW_USEDEFAULT, //initial y size NULL, //parent window handle NULL, //window menu handle hInstance, //program instance handle NULL); //creation parameters ShowWindow(hwnd, iCmdShow); UpdateWindow(hwnd); //This function will generate a WM_PAINT message. /* The message loop for this program. if received the WM_QUIT message, the function will return 0.*/ while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam;}//define the Window Procedure WndProc LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam){ static HICON hIcon; static int cxIcon, cyIcon, cxClient, cyClient; HDC hdc; HINSTANCE hInstance; PAINTSTRUCT ps; int x, y; switch (message) //get the message { case WM_CREATE: hInstance = ((LPCREATESTRUCT)lParam)->hInstance; hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON)); cxIcon = GetSystemMetrics(SM_CXICON); cyIcon = GetSystemMetrics(SM_CYICON); return 0; case WM_SIZE: cxClient = LOWORD(lParam); cyClient = HIWORD(lParam); return 0; case WM_PAINT: hdc = BeginPaint(hwnd, &ps); for (y = 0; y < cyClient; y += cyIcon) for (x = 0; x < cxClient; x += cxIcon) DrawIcon(hdc, x, y, hIcon); EndPaint(hwnd, &ps); return 0; case WM_DESTROY: PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, message, wParam, lParam);}
这段代码不能够直接编译

还需要创建资源文件,使用AddItem来增加icondemo.rc文件,VS会自动创建Resource.h头文件。

使用Resource Editor中的Add Resource 添加图标资源

原书中的图标是32x32的,在VS2015和Win7时代已经支持256x256的超大图标了

同时将ICON的ID改为IDI_ICON

然后可以编译程序了

运行结果

SM_CXICON  和SM_CYICON 为32x32

更小的图标是  SM_CXSMSIZE   和  SM_CYSMSIZE

10.1.2 获得图标的句柄

hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON));

#define MAKEINTRESOURCE (i) (LPTSTR) ((DWORD)((WORD)(i)))

资源ID也可以直接使用字符串

10.3.3  在应用程序中使用图标

RegisterClassEx   使用  WNDCLASSEX结构

有额外字段

ebSize   表示WNDCLASSEX结构大小

hIconSm  应该设定为小图标的句柄。

因此你需要设定两个图标句柄, 一个标准一个是小

在程序运行时改变图标,使用  SetClassLong函数实现。

SetClassLong(hwnd, GCL_HICON, LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ALTICON));

不想保存图标句柄可以使用一下函数

DrawIcon(hdc, x, y, GetClassLong(hwnd, GCL_HICON));

hIcon 是特例 生成的句柄不需要手动销毁,

10.1.4   使用自定义鼠标指针

和图标类似,在资源编辑器中添加指针

然后

wndclass.hCursor = LoadCursor(hInstance, MAKEINTRESOURCE(IDC_CURSOR));

或者用文本名字来定义鼠标指针

当鼠标在基于此类创建的窗口上,自定义的鼠标指针就会显示出来。

更改子窗口的hCursor字段

SetClassLong(hwndChild, GCL_HCURSOR, LoadCursor(hInstance, TEXT("childcursor"));

也可以使用SetCursor(hCursor);  来设定鼠标指针

应该在WM_MOUSEMOVE时调用SetCursor 否则一旦移动鼠标指针windows会重绘鼠标指针

10.1.5 字符串资源

使用资源管理器新建String Table

使用LoadString 来加载字符串

LoadString(hInstance, id, szBuffer, iMaxLength);  支持c语言的格式设置符号

所有字符串表都是以Unicode格式保持,LoadStringW直接加载Unicode文本,  LoadStringA则会进行代码页转换

10.1.6 自定义资源

hResource = LoadResource(hInstance, FindResource(hInstance, MAKEINTRESOURCE(IDR_BINTYPE), TEXT("BINTYPE")));

需要访问文本时

pData = LockResource(hResource);

使用完以后释放

FreeResource(hResource);

一个使用图标,字符串和自定义资源的例子

poepoem.cpp

#include 
#include "resource.h"LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //window procedure. HINSTANCE hInst;int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow){ static TCHAR szAppName[16], szCaption[64], szErrMsg[64]; HWND hwnd; MSG msg; WNDCLASS wndClass; //The window Class hInst = hInstance; LoadString(hInstance, IDS_APPNAME, szAppName, sizeof(szAppName) / sizeof(TCHAR)); LoadString(hInstance, IDS_CAPTION, szCaption, sizeof(szCaption) / sizeof(TCHAR)); wndClass.style = CS_HREDRAW | CS_VREDRAW; wndClass.lpfnWndProc = WndProc;// assign the window procedure to windows class. wndClass.cbClsExtra = 0; wndClass.cbWndExtra = 0; wndClass.hInstance = hInstance; wndClass.hIcon = LoadIcon(hInstance, szAppName);//LoadIcon(NULL, IDI_APPLICATION); wndClass.hCursor = LoadCursor(NULL, IDC_ARROW); wndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wndClass.lpszMenuName = NULL; wndClass.lpszClassName = szAppName; //Register the Window Class to the Windows System. if (!RegisterClass(&wndClass)) { //Support the ANSI encode. LoadStringA(hInstance, IDS_APPNAME, (char*)szAppName, sizeof(szAppName)); LoadStringA(hInstance, IDS_ERRMSG, (char*)szErrMsg, sizeof(szErrMsg)); MessageBoxA(NULL, (char *)szErrMsg, (char *)szAppName, MB_ICONERROR); return 0; } //This function will generate an WM_CREATE message. hwnd = CreateWindow(szAppName, //Window class name szCaption, //Window caption WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, //Window Style CW_USEDEFAULT, //initial x position CW_USEDEFAULT, //initial y position CW_USEDEFAULT, //initial x size CW_USEDEFAULT, //initial y size NULL, //parent window handle NULL, //window menu handle hInstance, //program instance handle NULL); //creation parameters ShowWindow(hwnd, iCmdShow); UpdateWindow(hwnd); //This function will generate a WM_PAINT message. /* The message loop for this program. if received the WM_QUIT message, the function will return 0.*/ while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam;}//define the Window Procedure WndProc LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam){ static char* pText; static HGLOBAL hResource; static HWND hScroll; static int iPosition, cxChar, cyChar, cyClient, iNumLines, xScroll; HDC hdc; PAINTSTRUCT ps; RECT rect; TEXTMETRIC tm; int ilength; switch (message) //get the message { case WM_CREATE: hdc = GetDC(hwnd); GetTextMetrics(hdc, &tm); cxChar = tm.tmAveCharWidth; cyChar = tm.tmHeight + tm.tmExternalLeading; ReleaseDC(hwnd, hdc); xScroll = GetSystemMetrics(SM_CXVSCROLL); hScroll = CreateWindow(TEXT("scrollbar"), NULL, WS_CHILD | WS_VISIBLE | SBS_VERT, 0, 0, 0, 0, hwnd, (HMENU)1, hInst, NULL); hResource = LoadResource(hInst, FindResource(hInst, TEXT("AnnabelLee"), TEXT("TEXT"))); pText = (char *)LockResource(hResource); iNumLines = 0; while (*pText != TEXT('\\') && *pText != TEXT('\0')) { if (*pText == TEXT('\n')) iNumLines++; pText = AnsiNext(pText); } //*pText = TEXT('\0'); SetScrollRange(hScroll, SB_CTL, 0, iNumLines, FALSE); SetScrollPos(hScroll, SB_CTL, 0, FALSE); return 0; case WM_SIZE: MoveWindow(hScroll, LOWORD(lParam) - xScroll, 0, xScroll, cyClient = HIWORD(lParam), TRUE); SetFocus(hwnd); return 0; case WM_SETFOCUS: SetFocus(hScroll); return 0; case WM_VSCROLL: switch (wParam) { case SB_TOP: iPosition = 0; break; case SB_BOTTOM: iPosition = iNumLines; break; case SB_LINEUP: iPosition -= 1; break; case SB_LINEDOWN: iPosition += 1; break; case SB_PAGEUP: iPosition -= cyClient / cyChar; break; case SB_PAGEDOWN: iPosition += cyClient / cyChar; break; case SB_THUMBPOSITION: case SB_THUMBTRACK: iPosition = LOWORD(lParam); break; } iPosition = max(0, min(iPosition, iNumLines)); if (iPosition != GetScrollPos(hScroll, SB_CTL)) { SetScrollPos(hScroll, SB_CTL, iPosition, TRUE); InvalidateRect(hwnd, NULL, TRUE); } return 0; case WM_PAINT: hdc = BeginPaint(hwnd, &ps); pText = (char*)LockResource(hResource); GetClientRect(hwnd, &rect); rect.left += cxChar; rect.top += cyChar * (1 - iPosition); DrawTextA(hdc, pText, -1, &rect, DT_EXTERNALLEADING); EndPaint(hwnd, &ps); return 0; case WM_DESTROY: FreeResource(hResource); PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, message, wParam, lParam);}

poepoem.rc

// Microsoft Visual C++ generated resource script.//#include "resource.h"#define APSTUDIO_READONLY_SYMBOLS///// Generated from the TEXTINCLUDE 2 resource.//#include "winres.h"/#undef APSTUDIO_READONLY_SYMBOLS/// Chinese (Simplified, PRC) resources#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED#ifdef APSTUDIO_INVOKED///// TEXTINCLUDE//1 TEXTINCLUDE BEGIN    "resource.h\0"END2 TEXTINCLUDE BEGIN    "#include ""winres.h""\r\n"    "\0"END3 TEXTINCLUDE BEGIN    "\r\n"    "\0"END#endif    // APSTUDIO_INVOKED///// Icon//// Icon with lowest ID value placed first to ensure application icon// remains consistent on all systems.PoePoem                 ICON                    "POEPOEM.ICO"///// TEXT//ANNABELLEE              TEXT    DISCARDABLE     "poepoem.txt"///// String Table//STRINGTABLEBEGIN    IDS_APPNAME             "PoePoem"    IDS_CAPTION             """Annabel Lee"" by Edgar Allan Poe"    IDS_ERRMSG              "This program requires Windows NT!"END#endif    // Chinese (Simplified, PRC) resources/#ifndef APSTUDIO_INVOKED///// Generated from the TEXTINCLUDE 3 resource.///#endif    // not APSTUDIO_INVOKED
resource.h

//{  {NO_DEPENDENCIES}}// Microsoft Visual C++ generated include file.// Used by poepoem.rc//#define IDS_APPNAME                     102#define IDS_CAPTION                     103#define IDS_ERRMSG                      104// Next default values for new objects// #ifdef APSTUDIO_INVOKED#ifndef APSTUDIO_READONLY_SYMBOLS#define _APS_NEXT_RESOURCE_VALUE        105#define _APS_NEXT_COMMAND_VALUE         40001#define _APS_NEXT_CONTROL_VALUE         1001#define _APS_NEXT_SYMED_VALUE           101#endif#endif
运行结果

10.2.1和菜单有关的概念

主菜单or顶级菜单

子菜单or 下拉菜单

弹出菜单可以被选中,顶级菜单不能被选中

菜单可以被启用,或禁用。 活动 ,非活动。

10.2.2 菜单结构

每个菜单有三个特征定义。  菜单显示什么, 文本或位图

ID号或者指向菜单的句柄

菜单的属性,是否禁用或选中

10.2.3 定义菜单

使用&F  windows会使用ALT+F 激活菜单

gray  菜单变灰

enabled  文字变灰

checked  菜单可选

Separator 选项会绘制一条水平的分割线

\t后面的文本会被放置在右边足够远的新栏中

\a 会使后面的文本右对齐

指定ID值是windows在菜单消息发送给窗口过程的数字。ID值在一个菜单中应该是唯一的。

使用IDM_XXX 开头

10.2.4 在程序中引用菜单

wndClass.lpszMenuName = szAppName; //给菜单指定一个和程序一样的名字菜单资源的ID

或者

hMenu = LoadMenu(hInstance, TEXT("MyMenu"));

或者

hMenu = LoadMenu(hInstance, MAKEINTRESOURCE(ID_MENU));

作为CreateWindow的参数hMenu

CreateWindow中指定的菜单会覆盖窗口类中指定的任何菜单。如果第九个参数为NULL 则基于窗口类中指定的菜单创建窗口。

也可以在窗口创建后

SetMenu(hwnd, hMenu); 来设定菜单

动态改变窗口

当窗口被销毁时,附加到该窗口的任何菜单也将被销毁。而在程序结束前,任何没有附加到窗口的菜单应该通过DestoryMenu调用被显示销毁(DestroyIcon, DestroyCursor等 销毁自己创建的其他资源句柄,自定义资源使用FreeResource)

10.2.5 菜单和消息

当用户选择菜单windows会像窗口过程发送几个不同消息。多数情况下,应用程序可以忽略大部分交给DefWindowProc处理

有一个消息如下WM_INITMENU

wParam   主菜单句柄

lParam    0

即使用户选择了第一项,wParam也是主菜单的句柄。

WM_MENUSELECT 消息。移动鼠标时,这对实现状态栏非常有用

LOWORD(wParam) 所选的菜单项, 菜单ID或者弹出菜单的索引

HIWORD(wParam) 所选的标记

lParam    锁选菜单项的句柄

选中标记可以是, MF_GRAYED, MF_DISABLED, MF_CHECKED, MF_BITMAP, MF_POPUP, MF_HELP, MF_SYSMENU 或MF_MOUSESELECT.

windows 要显示弹出菜单,会像窗口过程发送一个带有如下参数的WM_INITMENUPOPUP

wParam   弹出菜单的句柄

LOWORD(lParam) 弹出菜单的索引

HIWORD(lParam) 1代表系统菜单,0代表其他菜单

最重要的是WM_COMMAND 

如果子菜单和子窗口控件使用相同的ID

检查lParam 对于窗口,该值是0

WM_SYSCOMMAND 类似于WM_COMMAND 不过他表示用户从系统菜单选择了一个启用的菜单项

wParam   菜单ID

lParam    0

如果WM_SYSCOMMAND是鼠标单击的结果,LOWORD(lParam) HIWORD(lParam)将包含鼠标指针的x和y坐标

系统菜单

SC_SIZE, SC_MOVE  SC_MINIMIZE  SC_MAXIMIZE  SC_NEXTWINDOW, SC_PREVWINDOW, SC_CLOSE< SC_VSCROLL, SC_HSCROLL, SC_ARRANGE,

SC_RESTORE,  SC_TASKLIST.

WM_MENUCHAR 按下Alt和不对应于任何菜单项的字符犍  或者   弹出菜单时,用户按了一个不对应任何弹出菜单项的字符键

LOWORD(wParam)    字符码

HIWORD(wParam)    选择码

lParam    菜单句柄

选择码如下

0  没有弹出菜单显示

MF_POPUP 弹出菜单被显示

MF_SYSMENU 系统弹出菜单被显示

一般程序会把改消息传给DefWindowProc, 然后DefWindowProc返回0, 使得windows发出嘟嘟声。

10.2.6 范例程序

menudemo.cpp

#include 
#include "resource.h"#define ID_TIMER 1LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //window procedure. TCHAR szAppName[] = TEXT("MenuDemo");HINSTANCE hInst;int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow){ HWND hwnd; MSG msg; WNDCLASS wndClass; //The window Class hInst = hInstance; wndClass.style = CS_HREDRAW | CS_VREDRAW; wndClass.lpfnWndProc = WndProc;// assign the window procedure to windows class. 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; //Register the Window Class to the Windows System. if (!RegisterClass(&wndClass)) { MessageBox(NULL, TEXT("This program require Windows NT!"), szAppName, MB_ICONERROR); return 0; } //This function will generate an WM_CREATE message. hwnd = CreateWindow(szAppName, //Window class name TEXT("Menu Demonstration"), //Window caption WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, //Window Style CW_USEDEFAULT, //initial x position CW_USEDEFAULT, //initial y position CW_USEDEFAULT, //initial x size CW_USEDEFAULT, //initial y size NULL, //parent window handle NULL, //window menu handle hInstance, //program instance handle NULL); //creation parameters ShowWindow(hwnd, iCmdShow); UpdateWindow(hwnd); //This function will generate a WM_PAINT message. /* The message loop for this program. if received the WM_QUIT message, the function will return 0.*/ while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam;}//define the Window Procedure WndProc LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam){ static int idColor[5] = {WHITE_BRUSH, LTGRAY_BRUSH, GRAY_BRUSH, DKGRAY_BRUSH, BLACK_BRUSH}; static int iSelection = IDM_BKGND_WHITE; HMENU hMenu; switch (message) //get the message { case WM_COMMAND: hMenu = GetMenu(hwnd); switch (LOWORD(wParam)) { case IDM_FILE_NEW: case IDM_FILE_OPEN: case IDM_FILE_SAVE: case IDM_FILE_SAVE_AS: MessageBeep(0); return 0; case IDM_FILE_EXIT: SendMessage(hwnd, WM_CLOSE, 0, 0); case IDM_EDIT_UNDO: case IDM_EDIT_CUT: case IDM_EDIT_COPY: case IDM_EDIT_PASTE: case IDM_EDIT_CLEAR: MessageBeep(0); return 0; //The logic below assumes that IDM_WHITE through IDM_BLACK //are consecutive numbers in the order show here. case IDM_BKGND_WHITE: case IDM_BKGND_LTGRAY: case IDM_BKGND_GRAY: case IDM_BKGND_DKGRAY: case IDM_BKGND_BLACK: CheckMenuItem(hMenu, iSelection, MF_UNCHECKED); iSelection = LOWORD(wParam); CheckMenuItem(hMenu, iSelection, MF_CHECKED); SetClassLong(hwnd, GCL_HBRBACKGROUND, (LONG) GetStockObject(idColor[LOWORD(wParam) - IDM_BKGND_WHITE])); InvalidateRect(hwnd, NULL ,TRUE); return 0; case IDM_TIMER_START: if (SetTimer(hwnd, ID_TIMER, 1000, NULL)) { EnableMenuItem(hMenu, IDM_TIMER_START, MF_GRAYED); EnableMenuItem(hMenu, IDM_TIMER_STOP, MF_ENABLED); } return 0; case IDM_TIMER_STOP: KillTimer(hwnd, ID_TIMER); EnableMenuItem(hMenu, IDM_TIMER_START, MF_ENABLED); EnableMenuItem(hMenu, IDM_TIMER_STOP, MF_GRAYED); return 0; case IDM_APP_HELP: MessageBox(hwnd, TEXT("Help not yet implemented!"), szAppName, MB_ICONEXCLAMATION | MB_OK); return 0; case IDM_APP_ABOUT: MessageBox(hwnd, TEXT("Menu Demonstration Program\n") TEXT("(c) Charles Petzold, 1998"), szAppName, MB_ICONINFORMATION | MB_OK); return 0; } break; case WM_TIMER: MessageBeep(0); return 0; case WM_DESTROY: PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, message, wParam, lParam);}

menudemo.rc

//Microsoft Developer Studio generated resource script.//#include "resource.h"#define APSTUDIO_READONLY_SYMBOLS///// Generated from the TEXTINCLUDE 2 resource.//#include "afxres.h"/#undef APSTUDIO_READONLY_SYMBOLS/// English (U.S.) resources#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)#ifdef _WIN32LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US#pragma code_page(1252)#endif //_WIN32#ifdef APSTUDIO_INVOKED///// TEXTINCLUDE//1 TEXTINCLUDE DISCARDABLE BEGIN    "resource.h\0"END2 TEXTINCLUDE DISCARDABLE BEGIN    "#include ""afxres.h""\r\n"    "\0"END3 TEXTINCLUDE DISCARDABLE BEGIN    "\r\n"    "\0"END#endif    // APSTUDIO_INVOKED///// Menu//MENUDEMO MENU DISCARDABLE BEGIN    POPUP "&File"    BEGIN        MENUITEM "&New",                        ID_MENUITEM40020        MENUITEM "&Open",                       IDM_FILE_OPEN        MENUITEM "&Save",                       IDM_FILE_SAVE        MENUITEM "Save &As...",                 IDM_FILE_SAVE_AS        MENUITEM SEPARATOR        MENUITEM "E&xit",                       IDM_APP_EXIT    END    POPUP "&Edit"    BEGIN        MENUITEM "&Undo",                       IDM_EDIT_UNDO        MENUITEM SEPARATOR        MENUITEM "C&ut",                        IDM_EDIT_CUT        MENUITEM "&Copy",                       IDM_EDIT_COPY        MENUITEM "&Paste",                      IDM_EDIT_PASTE        MENUITEM "De&lete",                     IDM_EDIT_CLEAR    END    POPUP "&Background"    BEGIN        MENUITEM "&White",                      IDM_BKGND_WHITE, CHECKED        MENUITEM "&Light Gray",                 IDM_BKGND_LTGRAY        MENUITEM "&Gray",                       IDM_BKGND_GRAY        MENUITEM "&Dark Gray",                  IDM_BKGND_DKGRAY        MENUITEM "&Black",                      IDM_BKGND_BLACK    END    POPUP "&Timer"    BEGIN        MENUITEM "&Start",                      IDM_TIMER_START        MENUITEM "S&top",                       IDM_TIMER_STOP, GRAYED    END    POPUP "&Help"    BEGIN        MENUITEM "&Help...",                    IDM_APP_HELP        MENUITEM "&About MenuDemo...",          IDM_APP_ABOUT    ENDEND#endif    // English (U.S.) resources/#ifndef APSTUDIO_INVOKED///// Generated from the TEXTINCLUDE 3 resource.///#endif    // not APSTUDIO_INVOKED

resource.h

//{  {NO_DEPENDENCIES}}// Microsoft Visual C++ generated include file.// Used by menudemo.rc//#define IDM_FILE_NEW                    40001#define IDM_FILE_OPEN                   40002#define IDM_FILE_SAVE                   40003#define IDM_FILE_SAVE_AS                40004#define IDM_FILE_EXIT                   40005#define IDM_EDIT_UNDO                   40006#define IDM_EDIT_CUT                    40007#define IDM_EDIT_COPY                   40008#define IDM_Menu                        40009#define IDM_EDIT_CLEAR                  40010#define IDM_BKGND_WHITE                 40011#define IDM_BKGND_LTGRAY                40012#define IDM_BKGND_GRAY                  40013#define IDM_BKGND_DKGRAY                40014#define IDM_BKGND_BLACK                 40015#define IDM_TIMER_START                 40016#define IDM_TIMER_STOP                  40017#define IDM_APP_HELP                    40018#define IDM_APP_ABOUT                   40019#define IDM_EDIT_PASTE                  40039// Next default values for new objects// #ifdef APSTUDIO_INVOKED#ifndef APSTUDIO_READONLY_SYMBOLS#define _APS_NEXT_RESOURCE_VALUE        102#define _APS_NEXT_COMMAND_VALUE         40040#define _APS_NEXT_CONTROL_VALUE         1001#define _APS_NEXT_SYMED_VALUE           101#endif#endif
运行结果如下

10.2.7 菜单设计中的规范

10.2.8 定义菜单的繁琐方式

可以不适用资源脚本而使用CreateMenu和AppendMenu 动态创建菜单。完成定以后可以把菜单句柄传递给CreateWindow 或者使用SetMenu来设定窗口菜单。

hMenu = CreateMenu();

对于顶级菜单和弹出菜单必须使用不同的句柄

hMenuPop = CreateMenu();

AppendMenu(hMenuPopup, MF_STRING, IDM_FILE_NEW, "&NEW");

...

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

hMenuPop = CreateMenu();

...

具体例子参考截图

第三种方法,  使用LoadMenuIndirect函数接受一个指向MENUITEMPLATE类型的结构指针,并返回一个指向菜单的句柄。

10.2.9 浮动弹出菜单

popmenu.cpp

#include 
#include "resource.h"LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //window procedure. HINSTANCE hInst;TCHAR szAppName[] = TEXT("PopMenu");int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow){ HWND hwnd; MSG msg; WNDCLASS wndClass; //The window Class hInst = hInstance; wndClass.style = CS_HREDRAW | CS_VREDRAW; wndClass.lpfnWndProc = WndProc;// assign the window procedure to windows class. 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 = NULL; wndClass.lpszClassName = szAppName; //Register the Window Class to the Windows System. if (!RegisterClass(&wndClass)) { MessageBox(NULL, TEXT("This program require Windows NT!"), szAppName, MB_ICONERROR); return 0; } //This function will generate an WM_CREATE message. hwnd = CreateWindow(szAppName, //Window class name TEXT("PopupMenu Demonstration"), //Window caption WS_OVERLAPPEDWINDOW, //Window Style CW_USEDEFAULT, //initial x position CW_USEDEFAULT, //initial y position CW_USEDEFAULT, //initial x size CW_USEDEFAULT, //initial y size NULL, //parent window handle NULL, //window menu handle hInstance, //program instance handle NULL); //creation parameters ShowWindow(hwnd, iCmdShow); UpdateWindow(hwnd); //This function will generate a WM_PAINT message. /* The message loop for this program. if received the WM_QUIT message, the function will return 0.*/ while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam;}//define the Window Procedure WndProc LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam){ static HMENU hMenu; static int idColor[5] = { WHITE_BRUSH, LTGRAY_BRUSH, GRAY_BRUSH, DKGRAY_BRUSH, BLACK_BRUSH }; static int iSelection = IDM_BKGND_WHITE; POINT point; switch (message) //get the message { case WM_CREATE: hMenu = LoadMenu(hInst, szAppName); hMenu = GetSubMenu(hMenu, 0); return 0; case WM_RBUTTONUP: point.x = LOWORD(lParam); point.y = HIWORD(lParam); ClientToScreen(hwnd, &point); TrackPopupMenu(hMenu, TPM_RIGHTBUTTON, point.x, point.y, 0, hwnd, NULL); return 0; case WM_COMMAND: switch (LOWORD(wParam)) { case IDM_FILE_NEW: case IDM_FILE_OPEN: case IDM_FILE_SAVE: case IDM_FILE_SAVE_AS: MessageBeep(0); return 0; case IDM_APP_EXIT: SendMessage(hwnd, WM_CLOSE, 0, 0); case IDM_EDIT_UNDO: case IDM_EDIT_CUT: case IDM_EDIT_COPY: case IDM_EDIT_PASTE: case IDM_EDIT_CLEAR: MessageBeep(0); return 0; //The logic below assumes that IDM_WHITE through IDM_BLACK //are consecutive numbers in the order show here. case IDM_BKGND_WHITE: case IDM_BKGND_LTGRAY: case IDM_BKGND_GRAY: case IDM_BKGND_DKGRAY: case IDM_BKGND_BLACK: CheckMenuItem(hMenu, iSelection, MF_UNCHECKED); iSelection = LOWORD(wParam); CheckMenuItem(hMenu, iSelection, MF_CHECKED); SetClassLong(hwnd, GCL_HBRBACKGROUND, (LONG) GetStockObject(idColor[LOWORD(wParam) - IDM_BKGND_WHITE])); InvalidateRect(hwnd, NULL, TRUE); return 0; case IDM_APP_HELP: MessageBox(hwnd, TEXT("Help not yet implemented!"), szAppName, MB_ICONEXCLAMATION | MB_OK); return 0; case IDM_APP_ABOUT: MessageBox(hwnd, TEXT("Menu Demonstration Program\n") TEXT("(c) Charles Petzold, 1998"), szAppName, MB_ICONINFORMATION | MB_OK); return 0; } break; case WM_DESTROY: PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, message, wParam, lParam);}
popmenu.rc

//Microsoft Developer Studio generated resource script.//#include "resource.h"#define APSTUDIO_READONLY_SYMBOLS///// Generated from the TEXTINCLUDE 2 resource.//#include "afxres.h"/#undef APSTUDIO_READONLY_SYMBOLS/// English (U.S.) resources#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)#ifdef _WIN32LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US#pragma code_page(1252)#endif //_WIN32#ifdef APSTUDIO_INVOKED///// TEXTINCLUDE//1 TEXTINCLUDE DISCARDABLE BEGIN    "resource.h\0"END2 TEXTINCLUDE DISCARDABLE BEGIN    "#include ""afxres.h""\r\n"    "\0"END3 TEXTINCLUDE DISCARDABLE BEGIN    "\r\n"    "\0"END#endif    // APSTUDIO_INVOKED///// Menu//POPMENU MENU DISCARDABLE BEGIN    POPUP "MyMenu"    BEGIN        POPUP "&File"        BEGIN            MENUITEM "&New",                        IDM_FILE_NEW            MENUITEM "&Open",                       IDM_FILE_OPEN            MENUITEM "&Save",                       IDM_FILE_SAVE            MENUITEM "Save &As",                    IDM_FILE_SAVE_AS            MENUITEM SEPARATOR            MENUITEM "E&xit",                       IDM_APP_EXIT        END        POPUP "&Edit"        BEGIN            MENUITEM "&Undo",                       IDM_EDIT_UNDO            MENUITEM SEPARATOR            MENUITEM "Cu&t",                        IDM_EDIT_CUT            MENUITEM "&Copy",                       IDM_EDIT_COPY            MENUITEM "&Paste",                      IDM_EDIT_PASTE            MENUITEM "De&lete",                     IDM_EDIT_CLEAR        END        POPUP "&Background"        BEGIN            MENUITEM "&White",                      IDM_BKGND_WHITE, CHECKED            MENUITEM "&Light Gray",                 IDM_BKGND_LTGRAY            MENUITEM "&Gray",                       IDM_BKGND_GRAY            MENUITEM "&Dark Gray",                  IDM_BKGND_DKGRAY            MENUITEM "&Black",                      IDM_BKGND_BLACK        END        POPUP "&Help"        BEGIN            MENUITEM "&Help...",                    IDM_APP_HELP            MENUITEM "&About PopMenu...",           IDM_APP_ABOUT        END    ENDEND#endif    // English (U.S.) resources/#ifndef APSTUDIO_INVOKED///// Generated from the TEXTINCLUDE 3 resource.///#endif    // not APSTUDIO_INVOKED
resource.h

//{  {NO_DEPENDENCIES}}// Microsoft Developer Studio generated include file.// Used by PopMenu.rc//#define IDM_FILE_NEW                    40001#define IDM_FILE_OPEN                   40002#define IDM_FILE_SAVE                   40003#define IDM_FILE_SAVE_AS                40004#define IDM_APP_EXIT                    40005#define IDM_EDIT_UNDO                   40006#define IDM_EDIT_CUT                    40007#define IDM_EDIT_COPY                   40008#define IDM_EDIT_PASTE                  40009#define IDM_EDIT_CLEAR                  40010#define IDM_BKGND_WHITE                 40011#define IDM_BKGND_LTGRAY                40012#define IDM_BKGND_GRAY                  40013#define IDM_BKGND_DKGRAY                40014#define IDM_BKGND_BLACK                 40015#define IDM_HELP_HELP                   40016#define IDM_APP_HELP                    40016#define IDM_APP_ABOUT                   40017// Next default values for new objects// #ifdef APSTUDIO_INVOKED#ifndef APSTUDIO_READONLY_SYMBOLS#define _APS_NEXT_RESOURCE_VALUE        102#define _APS_NEXT_COMMAND_VALUE         40018#define _APS_NEXT_CONTROL_VALUE         1000#define _APS_NEXT_SYMED_VALUE           101#endif#endif
运行结果

10.2.10 使用系统菜单

包含WS_SYSMENU样式创建窗口,在标题栏左边会有一个系统该菜单。

系统菜单加入ID号必须小于0xF000. 否则会和windows标准命令的ID冲突

并且处理完WM_SYSCOMMAND消息要把其他消息传递给DefWindowProc

#include 
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //window procedure. #define IDM_SYS_ABOUT 1#define IDM_SYS_HELP 2#define IDM_SYS_REMOVE 3static TCHAR szAppName[] = TEXT("PoorMenu");int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow){ HMENU hMenu; HWND hwnd; MSG msg; WNDCLASS wndClass; //The window Class wndClass.style = CS_HREDRAW | CS_VREDRAW; wndClass.lpfnWndProc = WndProc;// assign the window procedure to windows class. 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 = NULL; wndClass.lpszClassName = szAppName; //Register the Window Class to the Windows System. if (!RegisterClass(&wndClass)) { MessageBox(NULL, TEXT("This program require Windows NT!"), szAppName, MB_ICONERROR); return 0; } //This function will generate an WM_CREATE message. hwnd = CreateWindow(szAppName, //Window class name TEXT("The Poor-Person's Menu"), //Window caption WS_OVERLAPPEDWINDOW, //Window Style CW_USEDEFAULT, //initial x position CW_USEDEFAULT, //initial y position CW_USEDEFAULT, //initial x size CW_USEDEFAULT, //initial y size NULL, //parent window handle NULL, //window menu handle hInstance, //program instance handle NULL); //creation parameters hMenu = GetSystemMenu(hwnd, FALSE); AppendMenu(hMenu, MF_SEPARATOR, 0, NULL); AppendMenu(hMenu, MF_STRING, IDM_SYS_ABOUT, TEXT("About...")); AppendMenu(hMenu, MF_STRING, IDM_SYS_HELP, TEXT("Help...")); AppendMenu(hMenu, MF_STRING, IDM_SYS_REMOVE, TEXT("Remove Additions")); ShowWindow(hwnd, iCmdShow); UpdateWindow(hwnd); //This function will generate a WM_PAINT message. /* The message loop for this program. if received the WM_QUIT message, the function will return 0.*/ while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam;}//define the Window Procedure WndProc LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam){ switch (message) //get the message { case WM_SYSCOMMAND: switch (LOWORD(wParam)) { case IDM_SYS_ABOUT: MessageBox(hwnd, TEXT("Menu Demonstration Program\n") TEXT("(c) Charles Petzold, 1998"), szAppName, MB_ICONINFORMATION | MB_OK); return 0; case IDM_SYS_HELP: MessageBox(hwnd, TEXT("Help not yet implemented!"), szAppName, MB_ICONEXCLAMATION | MB_OK); return 0; case IDM_SYS_REMOVE: GetSystemMenu(hwnd, TRUE); return 0; } break; case WM_DESTROY: PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, message, wParam, lParam);}

GetSystemMenu(hwnd, FALSE)  表明系统菜单将要被修改

GetSystemMenu(hwnd, TRUE); 还原被修改的系统菜单

10.2.11 改变菜单

windows3.0 以前使用ChangeMenu

现在使用 

AppendMenu 在菜单末尾追加一个新菜单项

DeleteMenu 从菜单中删除已存在菜单项并销毁他

InsertMenu 像菜单中插入一个新菜单

ModifyMenu  修改一个已经存在的菜单项

RemoveMenu 从菜单中去除一个已有的菜单项

DeleteMenu 会销毁该弹出菜单而Remove不会。

10.2.12 其他菜单命令

改变一个顶级菜单以后,知道windows重绘菜单才能显示。

DrawMenuBar(hwnd) 强制重绘菜单

hwnd为窗口句柄

hMenuPopup = GetSubMenu(hMenu, iPosition);  //获得弹出菜单的句柄

iPosition为弹出菜单在顶级菜单中的索引

iCount = GetMenuItemCount(hMenu) //顶级菜单或弹出菜单中现有菜单项的数目

id = GetMenuItemID(hMenuPopup, iPosition) // 获得弹出菜单中某个菜单项的ID

CheckMenuItem(hMenu, id, iCheck); 在弹出菜单中选择和取消选择某一菜单项 MF_CHECKED 或 MF_UNCHECKED  hMenu是顶级菜单句柄

如果hMenu是弹出菜单,那么id可以使iPosition

CheckMenuItem(hMenu, iPosition, MF_CHECKED | MF_BYPOSITION);  使用索引而非ID

EnableMenuItem(hMenu, id, MF_ENABLED);

第三个参数可以是  MF_ENABLED, MF_DISABLED, MF_GRAYED.   如果在顶级菜单上使用,并且改菜单项还有子菜单。那么第三个参数必须使用MF_BYPOSITION

HiliteMenuItem       MF_HILITE   MF_UNHILITE 加亮显示 

iFlags = GetMenuString(hMenu, id, pString, iMaxCount, iFlag)   iFlag 可以使MF_BYCOMMAND 或者MF_BYPOSITION   //获得菜单项的string

iFlags = GetMenuState(hMenu, id, iFlag)   iFlag 是 MF_COMMAND 或者MF_BYPOSITION。 iFlags是当前标识的组合值

可能是 MF_CHECKED, MF_SIABLED, MF_GRAYED, MF_MENUBREAK, MF_MENUUNBREAK, MF_SEPARATOR

Destory(hMenu) 销毁菜单,使得该菜单句柄无效

10.2.13菜单的另类用法

#include 
#include "resource.h"LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //window procedure. int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow){ static TCHAR szAppName[] = TEXT("NoPopUps"); HWND hwnd; MSG msg; WNDCLASS wndClass; //The window Class wndClass.style = CS_HREDRAW | CS_VREDRAW; wndClass.lpfnWndProc = WndProc;// assign the window procedure to windows class. 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 = NULL; wndClass.lpszClassName = szAppName; //Register the Window Class to the Windows System. if (!RegisterClass(&wndClass)) { MessageBox(NULL, TEXT("This program require Windows NT!"), szAppName, MB_ICONERROR); return 0; } //This function will generate an WM_CREATE message. hwnd = CreateWindow(szAppName, //Window class name TEXT("No-Popup Nested Menu Demonstration"), //Window caption WS_OVERLAPPEDWINDOW, //Window Style CW_USEDEFAULT, //initial x position CW_USEDEFAULT, //initial y position CW_USEDEFAULT, //initial x size CW_USEDEFAULT, //initial y size NULL, //parent window handle NULL, //window menu handle hInstance, //program instance handle NULL); //creation parameters ShowWindow(hwnd, iCmdShow); UpdateWindow(hwnd); //This function will generate a WM_PAINT message. /* The message loop for this program. if received the WM_QUIT message, the function will return 0.*/ while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam;}//define the Window Procedure WndProc LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam){ static HMENU hMenuMain, hMenuEdit, hMenuFile; HINSTANCE hInstance; switch (message) //get the message { case WM_CREATE: hInstance = (HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE); hMenuMain = LoadMenu(hInstance, TEXT("MenuMain")); hMenuFile = LoadMenu(hInstance, TEXT("MenuFile")); hMenuEdit = LoadMenu(hInstance, TEXT("MenuEdit")); SetMenu(hwnd, hMenuMain); return 0; case WM_COMMAND: switch (LOWORD(wParam)) { case IDM_MAIN: SetMenu(hwnd, hMenuMain); return 0; case IDM_FILE: SetMenu(hwnd, hMenuFile); return 0; case IDM_EDIT: SetMenu(hwnd, hMenuEdit); case IDM_FILE_NEW: case IDM_FILE_OPEN: case IDM_FILE_SAVE: case IDM_FILE_SAVE_AS: case IDM_EDIT_UNDO: case IDM_EDIT_CUT: case IDM_EDIT_COPY: case IDM_EDIT_PASTE: case IDM_EDIT_CLEAR: MessageBeep(0); return 0; } break; case WM_DESTROY: SetMenu(hwnd, hMenuMain); DestroyMenu(hMenuFile); DestroyMenu(hMenuEdit); PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, message, wParam, lParam);}
NOPOPUPS.rc

// Microsoft Visual C++ generated resource script.//#include "resource.h"#define APSTUDIO_READONLY_SYMBOLS///// Generated from the TEXTINCLUDE 2 resource.//#include "winres.h"/#undef APSTUDIO_READONLY_SYMBOLS/// Chinese (Simplified, PRC) resources#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED#ifdef APSTUDIO_INVOKED///// TEXTINCLUDE//1 TEXTINCLUDE BEGIN    "resource.h\0"END2 TEXTINCLUDE BEGIN    "#include ""winres.h""\r\n"    "\0"END3 TEXTINCLUDE BEGIN    "\r\n"    "\0"END#endif    // APSTUDIO_INVOKED///// Menu//MENUMAIN MENUBEGIN    MENUITEM "MAIN:",                       0, INACTIVE    MENUITEM "&File...",                    IDM_FILE    MENUITEM "&Edit...",                    IDM_EDITENDMENUFILE MENUBEGIN    MENUITEM "FILE:",                       0, INACTIVE    MENUITEM "&New",                        IDM_FILE_NEW    MENUITEM "&Open...",                    IDM_FILE_OPEN    MENUITEM "&Save",                       IDM_FILE_SAVE    MENUITEM "Save &As",                    IDM_FILE_SAVE_AS    MENUITEM "(&Main)",                     IDM_MAINENDMENUEDIT MENUBEGIN    MENUITEM "EDIT:",                       ID_EDIT, INACTIVE    MENUITEM "&Undo",                       IDM_EDIT_UNDO    MENUITEM "Cu&t",                        IDM_EDIT_CUT    MENUITEM "&Copy",                       IDM_EDIT_COPY    MENUITEM "&Paste",                      IDM_EDIT_PASTE    MENUITEM "De&lete",                     IDM_EDIT_CLEAR    MENUITEM "(&Main)",                     IDM_MAINEND#endif    // Chinese (Simplified, PRC) resources/#ifndef APSTUDIO_INVOKED///// Generated from the TEXTINCLUDE 3 resource.///#endif    // not APSTUDIO_INVOKED
resource.h

//{  {NO_DEPENDENCIES}}// Microsoft Visual C++ generated include file.// Used by nopopups.rc//#define IDM_FILE                        40001#define IDM_EDIT                        40002#define IDM_FILE_NEW                    40003#define IDM_FILE_OPEN                   40004#define IDM_FILE_SAVE                   40005#define IDM_FILE_SAVE_AS                40006#define IDM_MAIN                        40007#define IDM_EDIT_UNDO                   40008#define IDM_EDIT_CUT                    40009#define IDM_EDIT_COPY                   40010#define IDM_EDIT_PASTE                  40011#define IDM_EDIT_CLEAR                  40012#define ID_EDIT                         40013// Next default values for new objects// #ifdef APSTUDIO_INVOKED#ifndef APSTUDIO_READONLY_SYMBOLS#define _APS_NEXT_RESOURCE_VALUE        102#define _APS_NEXT_COMMAND_VALUE         40014#define _APS_NEXT_CONTROL_VALUE         1001#define _APS_NEXT_SYMED_VALUE           101#endif#endif

运行结果

10.3 键盘加速

可以生成WM_COMMAND  或者有时候是 WM_SYSCOMMAND

10.3.1 为什么你应该使用键盘加速键

windows会把WM_COMMAND消息发送给在windows函数TranslateAccelerator中指定的窗口过程,多窗口编程中非常重要

10.3.2 指定加速键的一些原则

常见加速键 F1  帮助,  F4~F6 用在多文档界面

10.3.3 加速键表

作为一种资源加载  每个加速键有一个ID和一个击键组合

可以使虚拟代码或者ascii字符与shift, ctrl 或alt的组合。

Tab字符能把文本和加速犍分开,这样加速键会排在第二列。

可以使用文字在菜单项后面说明 例如  (Shift+F6)

10.3.4 加载加速键列表

使用LoadAccelerators 加载

HANDLE hAccel;

hAccel = LoadAccelerators(hInstance, TEXT("MyAccelerators"));

也可以使用 MAKEINTRESOURCE 使用ID, 或者使用 #数字

10.3.5  键盘按键

修改消息循环

while (GetMessage(&msg, NULL, 0, 0))	{		if (!TranslateAccelerator(hwnd, hAccel, &msg))		{			TranslateMessage(&msg);			DispatchMessage(&msg);		}		}
TranslateAccelerator 会优先判断是否有加速键消息,然后发给对于的窗口过程

当该返回返回0,说明消息已经被翻译过,不需要再处理。继续吓一条消息

模态对话框或消息框有输入焦点时,TranslateAccelerator不翻译键盘消息,因为这些窗口的消息不通过程序的消息循环。

有些时候应用程序另一窗口拥有输入焦点时,你不想翻译键盘消息。如何处理该情况参考11章

10.3.6 接受加速键消息

对应系统菜单项发送  WM_SYSCOMMAND

否则发送WM_COMMAND

WM_COMMAND 消息

如果加速键对应某个菜单项,窗口过程还会接收到WM_INITMENU  WM_INITMENUPOPUP   和  WM_MENUSELECT

在处理WM_INITMENUPOPUP时,可以启用或禁用弹出菜单的菜单项

当一个菜单项变灰,加速键不会对其发送WM_COMMAND 或者WM_SYSOMMAND

如果窗口最小化,映射到系统菜单的键盘加速键会对窗口发送WM_SYSCOMMAND

没映射到系统菜单项的加速键,TranslateAccelerator也会想窗口过程发送WM_COMMAND消息

10.3.7 带有菜单项的POPPAD

#include 
#include "resource.h"#define ID_EDIT 1 LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //window procedure. TCHAR szAppName[] = TEXT("PopPad2");int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow){ //static TCHAR szAppName[] = TEXT("Colors1"); HACCEL hAccel; HWND hwnd; MSG msg; WNDCLASS wndClass; //The window Class wndClass.style = CS_HREDRAW | CS_VREDRAW; wndClass.lpfnWndProc = WndProc;// assign the window procedure to windows class. wndClass.cbClsExtra = 0; wndClass.cbWndExtra = 0; wndClass.hInstance = hInstance; wndClass.hIcon = LoadIcon(hInstance, szAppName);// LoadIcon(NULL, IDI_APPLICATION); wndClass.hCursor = LoadCursor(NULL, IDC_ARROW); wndClass.hbrBackground = CreateSolidBrush(0);//(HBRUSH)GetStockObject(WHITE_BRUSH); wndClass.lpszMenuName = szAppName; wndClass.lpszClassName = szAppName; //Register the Window Class to the Windows System. if (!RegisterClass(&wndClass)) { MessageBox(NULL, TEXT("This program require Windows NT!"), szAppName, MB_ICONERROR); return 0; } //This function will generate an WM_CREATE message. hwnd = CreateWindow(szAppName, //Window class name szAppName, //Window caption WS_OVERLAPPEDWINDOW, //Window Style GetSystemMetrics(SM_CXSCREEN) / 4, //initial x position GetSystemMetrics(SM_CYSCREEN) / 4, //initial y position GetSystemMetrics(SM_CXSCREEN) / 2, //initial x size GetSystemMetrics(SM_CYSCREEN) / 2, //initial y size NULL, //parent window handle NULL, //window menu handle hInstance, //program instance handle NULL); //creation parameters ShowWindow(hwnd, iCmdShow); UpdateWindow(hwnd); //This function will generate a WM_PAINT message. hAccel = LoadAccelerators(hInstance, szAppName); /* The message loop for this program. if received the WM_QUIT message, the function will return 0.*/ while (GetMessage(&msg, NULL, 0, 0)) { if (!TranslateAccelerator(hwnd, hAccel, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } return msg.wParam;}int AskConfirmation(HWND hwnd){ return MessageBox(hwnd, TEXT("Really want to close PopPad2?"), szAppName, MB_YESNO | MB_ICONQUESTION);}//define the Window Procedure WndProc LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam){ static HWND hwndEdit; int iSelect, iEnable; switch (message) //get the message { case WM_CREATE: hwndEdit = CreateWindow(TEXT("edit"), NULL, WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL | WS_BORDER | ES_LEFT | ES_MULTILINE | ES_AUTOHSCROLL | ES_AUTOVSCROLL, 0, 0, 0, 0, hwnd, (HMENU)ID_EDIT, ((LPCREATESTRUCT)lParam)->hInstance, NULL); return 0; case WM_SETFOCUS: SetFocus(hwndEdit); return 0; case WM_SIZE: MoveWindow(hwndEdit, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE); return 0; case WM_INITMENUPOPUP: if (lParam == 1) { EnableMenuItem((HMENU)wParam, IDM_EDIT_UNDO, SendMessage(hwndEdit, EM_CANUNDO, 0, 0) ? MF_ENABLED : MF_GRAYED); EnableMenuItem((HMENU)wParam, IDM_EDIT_PASTE, IsClipboardFormatAvailable(CF_TEXT) ? MF_ENABLED : MF_GRAYED); iSelect = SendMessage(hwndEdit, EM_GETSEL, 0, 0); if (HIWORD(iSelect) == LOWORD(iSelect)) iEnable = MF_GRAYED; else iEnable = MF_ENABLED; EnableMenuItem((HMENU)wParam, IDM_EDIT_CUT, iEnable); EnableMenuItem((HMENU)wParam, IDM_EDIT_COPY, iEnable); EnableMenuItem((HMENU)wParam, IDM_EDIT_CLEAR, iEnable); } case WM_COMMAND: if (lParam) { if (LOWORD(wParam) == ID_EDIT) if (HIWORD(wParam) == EN_ERRSPACE || HIWORD(wParam) == EN_MAXTEXT) MessageBox(hwnd, TEXT("Edit control out of spae."), szAppName, MB_OK | MB_ICONSTOP); } else switch (LOWORD(wParam)) { case IDM_FILE_NEW: case IDM_FILE_OPEN: case IDM_FILE_SAVE: case IDM_FILE_SAVE_AS: case IDM_FILE_PRINT: MessageBeep(0); return 0; case IDM_APP_EXIT: SendMessage(hwnd, WM_CLOSE, 0, 0); return 0; case IDM_EDIT_UNDO: SendMessage(hwndEdit, WM_UNDO, 0, 0); return 0; case IDM_EDIT_CUT: SendMessage(hwndEdit, WM_CUT, 0, 0); return 0; case IDM_EDIT_COPY: SendMessage(hwndEdit, WM_COPY, 0, 0); return 0; case IDM_EDIT_PASTE: SendMessage(hwndEdit, WM_PASTE, 0, 0); return 0; case IDM_EDIT_CLEAR: SendMessage(hwndEdit, WM_CLEAR, 0, 0); return 0; case IDM_EDIT_SELECT_ALL: SendMessage(hwndEdit, EM_SETSEL, 0, -1); return 0; case IDM_HELP_HELP: MessageBox(hwnd, TEXT("Help not yet implemented!"), szAppName, MB_ICONEXCLAMATION | MB_OK); return 0; case IDM_APP_ABOUT: MessageBox(hwnd, TEXT("Menu Demonstration Program\n") TEXT("(c) Charles Petzold, 1998"), szAppName, MB_ICONINFORMATION | MB_OK); return 0; } return 0; case WM_CLOSE: if (IDYES == AskConfirmation(hwnd)) DestroyWindow(hwnd); return 0; case WM_QUERYENDSESSION: if (IDYES == AskConfirmation(hwnd)) return 1; else return 0; case WM_DESTROY: PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, message, wParam, lParam);}
POPPAD.rc

// Microsoft Visual C++ generated resource script.//#include "resource.h"#define APSTUDIO_READONLY_SYMBOLS///// Generated from the TEXTINCLUDE 2 resource.//#include "winres.h"/#undef APSTUDIO_READONLY_SYMBOLS/// Chinese (Simplified, PRC) resources#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED#ifdef APSTUDIO_INVOKED///// TEXTINCLUDE//1 TEXTINCLUDE BEGIN    "resource.h\0"END2 TEXTINCLUDE BEGIN    "#include ""winres.h""\r\n"    "\0"END3 TEXTINCLUDE BEGIN    "\r\n"    "\0"END#endif    // APSTUDIO_INVOKED///// Menu//POPPAD2 MENUBEGIN    POPUP "&File"    BEGIN        MENUITEM "&New",                        IDM_FILE_NEW        MENUITEM "&Open...",                    IDM_FILE_OPEN        MENUITEM "&Save",                       IDM_FILE_SAVE        MENUITEM "Save &As...",                 IDM_FILE_SAVE_AS        MENUITEM SEPARATOR        MENUITEM "&Print",                      IDM_FILE_PRINT        MENUITEM SEPARATOR        MENUITEM "E&xit",                       IDM_APP_EXIT    END    POPUP "&Edit"    BEGIN        MENUITEM "&Undo\tCtrl+Z",               IDM_EDIT_UNDO        MENUITEM SEPARATOR        MENUITEM "Cu&t\tCtrl+X",                IDM_EDIT_CUT        MENUITEM "&Copy\tCtrl+C",               IDM_EDIT_COPY        MENUITEM "&Paste\tCtrl+V",              IDM_EDIT_PASTE        MENUITEM "De&lete\tDel",                IDM_EDIT_CLEAR        MENUITEM SEPARATOR        MENUITEM "&Select All",                 IDM_EDIT_SELECT_ALL    END    POPUP "&Help"    BEGIN        MENUITEM "&Help...",                    IDM_HELP_HELP        MENUITEM "&About PopPad2...",           IDM_APP_ABOUT    ENDEND///// Accelerator//POPPAD2 ACCELERATORSBEGIN    VK_DELETE,      IDM_EDIT_CLEAR,         VIRTKEY, NOINVERT    "^C",           IDM_EDIT_COPY,          ASCII,  NOINVERT    "^X",           IDM_EDIT_CUT,           ASCII,  NOINVERT    VK_DELETE,      IDM_EDIT_CUT,           VIRTKEY, SHIFT, NOINVERT    "^V",           IDM_EDIT_PASTE,         ASCII,  NOINVERT    VK_INSERT,      IDM_EDIT_PASTE,         VIRTKEY, CONTROL, NOINVERT    VK_INSERT,      IDM_EDIT_PASTE,         VIRTKEY, SHIFT, NOINVERT    "^Z",           IDM_EDIT_UNDO,          ASCII,  NOINVERT    VK_BACK,        IDM_EDIT_UNDO,          VIRTKEY, ALT, NOINVERT    VK_F1,          IDM_HELP_HELP,          VIRTKEY, NOINVERT    "^A",           IDM_EDIT_SELECT_ALL,    ASCII,  NOINVERTEND///// Icon//// Icon with lowest ID value placed first to ensure application icon// remains consistent on all systems.POPPAD2                 ICON                    "POPPAD2.ICO"#endif    // Chinese (Simplified, PRC) resources/#ifndef APSTUDIO_INVOKED///// Generated from the TEXTINCLUDE 3 resource.///#endif    // not APSTUDIO_INVOKED
resource.h

//{  {NO_DEPENDENCIES}}// Microsoft Visual C++ generated include file.// Used by poppad.rc//#define IDM_FILE_NEW                    40001#define IDM_FILE_OPEN                   40002#define IDM_FILE_SAVE                   40003#define IDM_FILE_SAVE_AS                40004#define IDM_FILE_PRINT                  40005#define IDM_APP_EXIT                    40006#define IDM_EDIT_UNDO                   40007#define IDM_EDIT_CUT                    40008#define IDM_EDIT_COPY                   40009#define IDM_EDIT_PASTE                  40010#define IDM_EDIT_CLEAR                  40011#define IDM_EDIT_SELECT_ALL             40012#define IDM_HELP_HELP                   40013#define IDM_APP_ABOUT                   40014// Next default values for new objects// #ifdef APSTUDIO_INVOKED#ifndef APSTUDIO_READONLY_SYMBOLS#define _APS_NEXT_RESOURCE_VALUE        105#define _APS_NEXT_COMMAND_VALUE         40028#define _APS_NEXT_CONTROL_VALUE         1001#define _APS_NEXT_SYMED_VALUE           101#endif#endif

10.3.8 启动菜单项

判断是否可以做undo

EnableMenuItem((HMENU)wParam, IDM_EDIT_UNDO,

SendMessage(hwndEdit, EM_CANUNDO, 0, 0) ?
MF_ENABLED : MF_GRAYED);

判断是否可以做Paste

EnableMenuItem((HMENU)wParam, IDM_EDIT_PASTE,

IsClipboardFormatAvailable(CF_TEXT) ?
MF_ENABLED : MF_GRAYED);

判断编辑框有文本被选中

iSelect = SendMessage(hwndEdit, EM_GETSEL, 0, 0);

if (HIWORD(iSelect) == LOWORD(iSelect))
iEnable = MF_GRAYED;
else
iEnable = MF_ENABLED;

10.3.9 处理菜单项

WM_CLOSE  用户关闭程序时响应

WM_QUERYENDSESSION  当用户选择关闭windows时响应

返回0 则终止操作失败

你可能感兴趣的文章
Mysql 数据类型一日期
查看>>
MySQL 数据类型和属性
查看>>
mysql 敲错命令 想取消怎么办?
查看>>
Mysql 整形列的字节与存储范围
查看>>
mysql 断电数据损坏,无法启动
查看>>
MySQL 日期时间类型的选择
查看>>
Mysql 时间操作(当天,昨天,7天,30天,半年,全年,季度)
查看>>
MySQL 是如何加锁的?
查看>>
MySQL 是怎样运行的 - InnoDB数据页结构
查看>>
mysql 更新子表_mysql 在update中实现子查询的方式
查看>>
MySQL 有什么优点?
查看>>
mysql 权限整理记录
查看>>
mysql 权限登录问题:ERROR 1045 (28000): Access denied for user ‘root‘@‘localhost‘ (using password: YES)
查看>>
MYSQL 查看最大连接数和修改最大连接数
查看>>
MySQL 查看有哪些表
查看>>
mysql 查看锁_阿里/美团/字节面试官必问的Mysql锁机制,你真的明白吗
查看>>
MySql 查询以逗号分隔的字符串的方法(正则)
查看>>
MySQL 查询优化:提速查询效率的13大秘籍(避免使用SELECT 、分页查询的优化、合理使用连接、子查询的优化)(上)
查看>>
mysql 查询数据库所有表的字段信息
查看>>
【Java基础】什么是面向对象?
查看>>