第十一章 对话框
基于模板的对话框,包含了弹出窗口,和子窗口控件,而且有一个窗口过程来处理对话框消息。 包括键盘和鼠标的输入。
称为 “对话框管理器” 和标准的Windows窗口消息处理略有不同。稍后能看到具体区别
许多消息不仅被对话框窗口过程处理,还会传递给你自己的程序中的某些函数。 称为对话框过程
对话框过程一般处理初始化自创控件以及子窗口传来的消息。不处理WM_PAINT 也不直接处理键盘和鼠标的输入
子窗口控件由windows对话框管理器来负责。对话框管理器来负责处理在多个控件中转义输入焦点的相关逻辑
11.1 模态对话框
用户不能在该对话框和该程序的其他对话框之间切换。但是可以切换到其他的程序。有些系统模态不允许切换程序。必须结束系统模态才可以进行其他操作。
11.1.1 创建一个about对话框
About.cpp
#include <windows.h>
#include "resource.h"
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //window procedure.
BOOL CALLBACK AboutDlgProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT("About1");
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 = (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("About Box Demo Program"), //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 HINSTANCE hInstance;
switch (message) //get the message
{
case WM_CREATE:
hInstance = ((LPCREATESTRUCT)lParam)->hInstance;
return 0;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDM_APP_ABOUT:
DialogBox(hInstance, TEXT("AboutBox"), hwnd, AboutDlgProc);
break;
}
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
BOOL CALLBACK AboutDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_INITDIALOG:
return TRUE;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDOK:
case IDCANCEL:
EndDialog(hDlg, 0);
return TRUE;
}
break;
}
return FALSE;
}
About1.rc
// Microsoft Visual C++ generated resource script.
//
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "afxres.h"
/
#undef APSTUDIO_READONLY_SYMBOLS
/
// English (United States) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#pragma code_page(1252)
#ifdef APSTUDIO_INVOKED
/
//
// TEXTINCLUDE
//
1 TEXTINCLUDE
BEGIN
"resource.h\0"
END
2 TEXTINCLUDE
BEGIN
"#include ""afxres.h""\r\n"
"\0"
END
3 TEXTINCLUDE
BEGIN
"\r\n"
"\0"
END
#endif // APSTUDIO_INVOKED
/
//
// Dialog
//
ABOUTBOX DIALOGEX 32, 32, 180, 102
STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP
FONT 8, "MS Sans Serif", 0, 0, 0x0
BEGIN
DEFPUSHBUTTON "OK",IDOK,66,81,50,14
ICON "ABOUT1",IDC_STATIC,7,7,20,20
CTEXT "About1",IDC_STATIC,40,12,100,8
CTEXT "About Box Demo Program",IDC_STATIC,7,40,166,8
CTEXT "(c) Charles Petzold, 1998",IDC_STATIC,7,52,166,8
END
/
//
// Menu
//
ABOUT1 MENU
BEGIN
POPUP "&Help"
BEGIN
MENUITEM "&About About1...", IDM_APP_ABOUT
END
END
/
//
// Icon
//
// Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.
ABOUT1 ICON "About1.ico"
/
//
// DESIGNINFO
//
#ifdef APSTUDIO_INVOKED
GUIDELINES DESIGNINFO
BEGIN
"ABOUTBOX", DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 173
TOPMARGIN, 7
BOTTOMMARGIN, 95
END
END
#endif // APSTUDIO_INVOKED
/
//
// AFX_DIALOG_LAYOUT
//
ABOUTBOX AFX_DIALOG_LAYOUT
BEGIN
0
END
#endif // English (United States) 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 About1.rc
//
#define IDM_APP_ABOUT 40001
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 104
#define _APS_NEXT_COMMAND_VALUE 40004
#define _APS_NEXT_CONTROL_VALUE 1003
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif
11.1.2 对话框及其模板
DEFPUSHBUTTON, ICON 和CTEXT只被用于对话框。他们是一些特殊类和窗口样式的缩写。 CTEXT指定窗口控件的类型是静态的,同时其样式是
WS_CHILD | SS_CENTER | WS_VISIBLE | WS_GROUP
文本设定等价于Caption参数
当子窗口像父窗口发送WM_COMMAND时,id字段是子窗口用来表示自己的标识符。 子窗口控件的父窗口是对话框本身。它会把这些消息发送给某个windows的窗口过程。
这里的ID和CreateWindow中 (HMENU)id 参数相同。 由于文本和图标并不需要向父窗口发送消息。所以ID被设定为ID_STATIC 按钮的ID为IDOK 在winuser.h中定义为1
接下来设定子窗口控件的位置和大小。 单位是 字符评均宽度的1/4 和 平均高度的1/8
11.1.3 对话框过程
发送给对话框的消息是用户程序中的对话框过程来处理的。和窗口过程很像,但是其实是不一样的。 对话框的窗口过程属于windows。 而对话框过程是独立的。
对话框的窗口过程会调用这个对话框的过程来处理许多消息。
对话框过程
BOOL CALLBACK AboutDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_INITDIALOG:
return TRUE;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDOK:
case IDCANCEL:
EndDialog(hDlg, 0);
return TRUE;
}
break;
}
return FALSE;
}
对话框过程 和 对话框的窗口过程的区别
1)窗口过程返回值是 LRESULT 而对话框过程的返回值是BOOL
2)窗口过程不处理一条消息时,会调用DefWindowProc。 对话框过程处理一条消息会返回TRUE,不处理则返回FALSE
3) 对话框过程不需要处理WM_PAINT和WM_DESTROY消息。他也不会收到WM_CREATE消息。 然后他会在一条处理WM_INITDIALOG消息时进行初始化,这是对话框过程接收到的第一条消息。当其返回TRUE时,windows会把输入焦点放在第对话框第一个含有WS_TABSTOP样式的窗口子空间。本例中是按钮。
另外在WM_INITDIALOG也可以调用SetFocus来设置焦点,并返回FALSE
除此之外,对话框过程只处理WM_COMMAND,当鼠标单击按钮或者当焦点在按钮控件时按下空格键,按钮控件会像父窗口发送WM_COMMAND消息。wParam低位时控件ID
其他任何消息,对话框过程会返回FALSE来通知对话框的窗口过程该消息未处理。
模态对话框的消息并不通过程序的消息循环。
11.1.4 激活对话框
Dialog(hInstance, TEXT("AboutBox"), hwnd, AboutDlgProc);
实例句柄, 对话框名称在资源该脚本中定义, 父窗口句柄 和对话框过程的地址。
也可以使用MAKEINTRESOURCE宏来将其转换为字符。
当回车或空格被按下,windows会向对话框发送WM_COMMAND消息。 wParam的低位时默认按钮ID。 也可以按ESC来结束对话框。
直到对话框结束以后,用来显示对话框的DialogBox才将控制返回给WndProc。 DialogBox的返回值等于EndDialog函数的第二个参数。并将控制权返回给Windows.
对话框在显示使,WndProc依旧可以处理消息。比如SendMessage(GetParent(Dlg), ...);
11.1.5 主题变换
对话框资源脚本
对话框常用样式
STYLE WS_POPUP | DS_MODALFRAME
带标题栏 WS_CAPTION
CAPTION "Dialog Box Caption" 标题栏文本
或者当对话框过程处理WM_INITDIALOG消息时,你可以使用
SetWindowText(hDlg, TEXT("Dialog Box Caption"));
使用WS_CAPTION样式 还运行使用WS_SYSMENU 增加系统菜单框。这一样式允许用户通过系统菜单来选择Move或Close
WS_THICKFRAME (Resizing) 允许用户改变对话框的大小。甚至还可以给对话框添加菜单
MENU 菜单名
FONT 语句用来设置对话框文本为系统字体以外的字体。 早起VS创建对话框默认的字体是 8pt的 MS Scans Serif。 现在VS2015使用的是 8pt的 MS Shell Dlg 字体
可以指定某个窗口过程来处理对话框消息。
CLASS “类名”
调用DialogBox函数, windows会从对话框资源模板中获取信息来CreateWindow ,windows从DialogBox函数的参数获取hInstance 和 父窗口hwnd
windows所需的唯一其他信息是一个窗口类,假设对话框模板没有指定。 window设为对话框注册一个特殊的窗口类。窗口类用于对话框过程的访问地址。
因此弹出式窗口可以向程序传递其收到的消息。 也可以自己使用CreateWindow并注册窗口类来创建自己的对话框,DialogBox函数只是一种简便的方法。
也可以动态创建对话框,不需要写资源脚本。DialogBoxIndirect函数 使用数据结构来定义对话框模板。
常用控件类型的相应窗口类和窗口样式
除了表中的样式,每个控件默认还有以下样式
WS_CHILD | WS_VISIBLE
EDITTEXT, SCROLLBAR, LISTBOX 和COMBOBOX以下格式
控件类型 id, xPos, yPos, xWidth, yHeight, iStyle
除此之外所有控件的定义都有以下格式
控件类型 "文本" , id, xpos, ypos, xwidth, yHeight, iStyle
对话框模板中的单位是平均字符宽度的1/4 和 高度的 1/8 。
例如复选框
CHECKBOX "TEXT", id, xPos, yPos, xWidth, yHeight, BS_LEFTTEXT checkbox上的说明文本左对齐
EDITTEXT id, xPos, yPos, xWidth, yHeight, NOT WS_BORDER 不带边框的EDITTEXT
资源编辑器还承认这样的一般化语句
CONTROL "文本" , id, "类", IStyle, xPos, yPos, xWidth, yHeight
创建任何类型的子窗口控件
比如
PUSHBUTTON "OK", IDOK, 10, 20, 32, 14
可以使用下面语句
CONTROL "OK" , IDOK, "button", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP,
10, 20, 32, 14
可使用VS中自定义控件选项来生成以上语句
在模板资源中,则无需WS_CHILD | WS_VISIBLE 样式
上面的CONTROL语句转换成如下调用
hCtrl1 = CreateWindow( TEXT("button"), TEXT("OK"),
WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
10 * cxChar / 4, 20 * cyChar / 8,
32 * cxChar / 4, 14 * cyChar / 8,
hDlg, IDOK, hInstance, NULL);
11.1.6 更复杂的对话框
about2.cpp
#include <windows.h>
#include "resource.h"
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //window procedure.
BOOL CALLBACK AboutDlgProc(HWND, UINT, WPARAM, LPARAM);
int iCurrentColor = IDC_BLACK,
iCurrentFigure = IDC_RECT;
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT("About2");
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 = (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("About Box Demo Program"), //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;
}
void PaintWindow(HWND hwnd, int iColor, int iFigure)
{
static COLORREF crColor[8] = { RGB(0, 0, 0), RGB(0, 0, 255),
RGB(0, 255, 0), RGB(0, 255, 255),
RGB(255, 0, 0), RGB(255, 0, 255),
RGB(255, 255, 0), RGB(255, 255, 255) };
HBRUSH hBrush;
HDC hdc;
RECT rect;
hdc = GetDC(hwnd);
GetClientRect(hwnd, &rect);
hBrush = CreateSolidBrush(crColor[iColor - IDC_BLACK]);
hBrush = (HBRUSH)SelectObject(hdc, hBrush);
if (iFigure == IDC_RECT)
Rectangle(hdc, rect.left, rect.top, rect.right, rect.bottom);
else
Ellipse(hdc, rect.left, rect.top, rect.right, rect.bottom);
DeleteObject(SelectObject(hdc, hBrush));
ReleaseDC(hwnd, hdc);
}
void PaintTheBlock(HWND hCtrl, int iColor, int iFigure)
{
InvalidateRect(hCtrl, NULL, TRUE);
UpdateWindow(hCtrl);
PaintWindow(hCtrl, iColor, iFigure);
}
//define the Window Procedure WndProc
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static HINSTANCE hInstance;
PAINTSTRUCT ps;
switch (message) //get the message
{
case WM_CREATE:
hInstance = ((LPCREATESTRUCT)lParam)->hInstance;
return 0;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDM_APP_ABOUT:
if (DialogBox(hInstance, TEXT("AboutBox"), hwnd, AboutDlgProc))
InvalidateRect(hwnd, NULL, TRUE);
break;
}
break;
case WM_PAINT:
BeginPaint(hwnd, &ps);
EndPaint(hwnd, &ps);
PaintWindow(hwnd, iCurrentColor, iCurrentFigure);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
BOOL CALLBACK AboutDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
static HWND hCtrlBlock;
static int iColor, iFigure;
switch (message)
{
case WM_INITDIALOG:
iColor = iCurrentColor;
iFigure = iCurrentFigure;
CheckRadioButton(hDlg, IDC_BLACK, IDC_WHITE, iColor);
CheckRadioButton(hDlg, IDC_RECT, IDC_ELLIPSE, iFigure);
hCtrlBlock = GetDlgItem(hDlg, IDC_PAINT);
SetFocus(GetDlgItem(hDlg, iColor));
return FALSE;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDOK:
iCurrentColor = iColor;
iCurrentFigure = iFigure;
EndDialog(hDlg, TRUE);
return TRUE;
case IDCANCEL:
EndDialog(hDlg, FALSE);
return TRUE;
case IDC_BLACK:
case IDC_RED:
case IDC_GREEN:
case IDC_YELLOW:
case IDC_BLUE:
case IDC_MAGENTA:
case IDC_CYAN:
case IDC_WHITE:
iColor = LOWORD(wParam);
CheckRadioButton(hDlg, IDC_BLACK, IDC_WHITE, LOWORD(wParam)); //The LOWORD(wParam) is the current ID of the command.
PaintTheBlock(hCtrlBlock, iColor, iFigure);
return TRUE;
case IDC_RECT:
case IDC_ELLIPSE:
iFigure = LOWORD(wParam);
CheckRadioButton(hDlg, IDC_RECT, IDC_ELLIPSE, LOWORD(wParam));
PaintTheBlock(hCtrlBlock, iColor, i