使用VS2017(Visual studio 2017)进行程序的编译。
要使主窗口有自定义的菜单,应将wcex.lpszMenuName
这个注册窗口类的默认值修改,改为null,这样就不会显示VS默认的菜单了。
下面的代码我直接调用MAKEINTRESOURCE()函数(这是一个进行资源名转换的宏,将数字类型ID转换为字符类型,这里我们注意到在wcex.lpszMenuName
的类型是LPCTSTR,所以我们使用该函数进行类型转换,类似C语言的强制类型转换)并将参数设为自定义菜单的ID。
而如何在VS中构建自定义菜单我会在下文进行操作的说明。
wcex.lpszMenuName = MAKEINTRESOURCE(IDR_MENU1);
在VS中,点击**“视图”,在菜单项目中点击“其他窗口”,再点击“资源视图”,我们便获得了定义项目的资源视图
exp:
可以看到.rc下的各个部分,Menu就是我们要用到的,点开Menu,可以看到程序自带的Menu的ID,之后如何自定义菜单,右键Menu,在弹出菜单栏中点击添加资源即可。
可以根据菜单的属性(点击要编辑的地方便会出现属性,属性栏位于屏幕右下角,可以直接修改内容)进行相应的修改和自定义,左边边栏的工具箱是非常实用的,包括文本框等(一股VB的既视感)。
使用新插入对菜单的项目进行增添,注意,每次增添新项目系统会自动生成相应的IDM**(可在resource.h中查看),便于之后你在WM_COMMAND中进行菜单项目的不同case的代码编写。
模态对话框:
当程序显示一个模态对话框时,使用者不能再对话方块与同一个程序中的另一个视窗之间进行切换,必须主动结束该对话框(通过按下OK或者Cancel键来完成)。但是在显示对话框时,使用者可以从目前的程序切换到另一个程序。然后,有些称为【系统模态】的对话框甚至不允许像这样的,进行程序间的切换。
Windows程式即使不需要接收使用者的输入,也通常具有功能表上的【About】选项启动的对话框,该对话框用来显示程式的名字、图示、版权旗标和标记为【OK】的按键。
什么是功能表?可参考《Windows程序设计》第十章:功能表和其他资源。
启动对话框:
在WndProc中处理WM_CREATE讯息,取得程式的执行实体代号并将它放在静态变数中:
hInstance = ((LPCREATESTRUCT)lParam)->hInstance;
检查WM_COMMAND讯息,确保wParam的低位元字等于所要执行的IDM。当它获得这样一个讯息时,程式呼叫DialogBox:
DialogBox(hInstance,TEXT("AboutBox"),hwnd,AboutDlgProc);
这里贴一个该函数的详细信息:
DialogBox(hInstance, lpTemplate, hWndParent, lpDialogFunc);
该函数需要执行实体代号(在处理 WM_CREATE 时储存的)、对话方块名称(在资源描述档中定义的)、对话方块的父视窗(也是程式的主视窗)和对话 方块程序的位址。如果您使用一个数字而不是对话方块模板名称,那么可以用 MAKEINTRESOURCE 巨集将它转换为一个字串(上文有特别提到)。
如同添加自定义菜单,可以使用VS进行快速的自定义对话框。
非模态对话框:
允许使用者在对话框和其他程式之间进行切换,也可以在对话框和建立对话框的视窗之间进行切换。模态对话框通过DialogBox建立,只有在清除对话框后,函数才会传回值。在对话框程序内使用EndDialog呼叫来中止对话框,DialogBox传回的是该呼叫的第二个参数的值。
EndDialog(
_In_ HWND hDlg,
_In_ INT_PTR nResult);
非模态对话框是使用CreateDialog来建立的。该函数树勇的参数与DialogBox相同:
CreateDialog(hInstance, lpTemplate, hWndParent, lpDialogFunc);
区别:
CreateDialog立即传回对话框的视窗代号,并通常将这个视窗代号存放在全局变量中。
而销毁非模态对话框则用
EndPaint( _In_ HWND hWnd, _In_ CONST PAINTSTRUCT *lpPaint);
将参数设置为你创建的非模态对话框的句柄即可。
附上一个简单的程序作业,帮助理解菜单和对话框的使用。
要求:
重新定义菜单名为File(F),包含show、hire、exit三个菜单子项目。点击show触发对话框,显示特定语句(无其他按钮,单单只有文本),点击hire销毁show所触发的对话框(暗示在对话框弹出时还能对父窗口进行操作,涉及到模态对话框和非模态对话框的辨别),点击exit关闭整个窗口。
样例代码:
//WindowsProject11.cpp: 定义应用程序的入口点。
//
#include "stdafx.h"
#include "WindowsProject11.h"
#define MAX_LOADSTRING 100
// 全局变量:
HINSTANCE hInst; // 当前实例
WCHAR szTitle[MAX_LOADSTRING] = TEXT("Dialog Demo"); // 标题栏文本
WCHAR szWindowClass[MAX_LOADSTRING]; // 主窗口类名
// 此代码模块中包含的函数的前向声明:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// TODO: 在此放置代码。
// 初始化全局字符串
//LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadStringW(hInstance, IDC_WINDOWSPROJECT11, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
// 执行应用程序初始化:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_WINDOWSPROJECT11));
MSG msg;
// 主消息循环:
while (GetMessage(&msg, nullptr, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int) msg.wParam;
}
//
// 函数: MyRegisterClass()
//
// 目的: 注册窗口类。
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WINDOWSPROJECT11));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = MAKEINTRESOURCE(IDR_MENU1);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassExW(&wcex);
}
//
// 函数: InitInstance(HINSTANCE, int)
//
// 目的: 保存实例句柄并创建主窗口
//
// 注释:
//
// 在此函数中,我们在全局变量中保存实例句柄并
// 创建和显示主程序窗口。
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance; // 将实例句柄存储在全局变量中
// HMENU hMenu = CreateMenu();//菜单句柄,句柄就是一个数,标识一个资源
//HMENU hPopMenu = CreatePopupMenu();//创建一个弹出菜单
// AppendMenu(hMenu, MF_POPUP, (UINT_PTR)hPopMenu, _T("操作"));
//AppendMenu(hPopMenu, MF_STRING, ID_MOVE, _T("移动"));
HWND hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
//
// 函数: WndProc(HWND, UINT, WPARAM, LPARAM)
//
// 目的: 处理主窗口的消息。
//
// WM_COMMAND - 处理应用程序菜单
// WM_PAINT - 绘制主窗口
// WM_DESTROY - 发送退出消息并返回
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static HWND hdialog = NULL;
switch (message)
{
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
int wmEvent = HIWORD(wParam);//代表高位事件
// 分析菜单选择:
switch (wmId)
{
case ID_FILE_SHOW:
hdialog = CreateDialog(hInst, MAKEINTRESOURCE(IDD_WINDOWSPROJECT11_DIALOG), hWnd, About);
ShowWindow(hdialog, SW_SHOW);
break;
case ID_FILE_HIRE:
//CreateDialog(hInst, MAKEINTRESOURCE(IDD_WINDOWSPROJECT11_DIALOG), hWnd, About);
DestroyWindow(hdialog);
break;
case ID_FILE_EXIT32774:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// TODO: 在此处添加使用 hdc 的任何绘图代码...
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
// “关于”框的消息处理程序。
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}
运行结果: