对话框分“模态”和“非模态”两种,其中模态对话框最为常见。当程序显示一个模态对话框时,用户不能在对话框和该程序的其他窗口之间进行切换。用户必须先明确地终止该对话框。这通常由单击OK或Cancel按钮来实现。但是当对话框正在显示时,用户可以切换到其他的程序。有些对话框(所谓“系统模态”)则连这种切换都不允许。在Windows中,用户必须先结束系统模态对话框才可以进行其他操作。本节我们将首先介绍模态对话框。
本节必须掌握的知识点:
第63练:模态对话框
模态对话框
第64练:复杂的模态对话框
第65练:自定义的模态对话框
10.1.1 第63练:模态对话框
/*------------------------------------------------------------------------
063 WIN32 API 每日一练
第63个例子ABOUT1.C:模态对话框
AboutDlgProc对话框过程
DialogBox函数
EndDialog函数
WM_INITDIALOG消息
(c) www.bcdaren.com 编程达人
-----------------------------------------------------------------------*/
#include <windows.h>
#include "resource.h"
LRESULT CALLBACK Wndproc(HWND, UINT, WPARAM, LPARAM);
BOOL CALLBACK AboutDlgProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain( HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nShowCmd )
{
static TCHAR szAppName[] = TEXT("ABOUT1");
…(略)
return msg.wParam;
}
LRESULT CALLBACK Wndproc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
{
static HINSTANCE hInstance;
switch (message)
{
case WM_CREATE:
hInstance = ((LPCREATESTRUCT)lparam)->hInstance;//获取当前实例句柄
return 0;
case WM_COMMAND:
switch (LOWORD(wparam))
{
case IDM_APP_ABOUT: //激活对话框
//生成对话框宏DialogBox --对DialogBoxParam函数的调用。
DialogBox(hInstance,TEXT("AboutBox"),hwnd,AboutDlgProc);
break;
}
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd,message,wparam,lparam);
}
//对话框过程属于Windows,父窗口为"ABOUT1",对话框中的控件的父窗口是对话框
//主窗口的消息由WinProc处理,对话框中的控件消息由AboutDlgProc处理
BOOL CALLBACK AboutDlgProc(HWND hDlg, UINT message, WPARAM wparam, LPARAM lparam)
{
switch (message)
{
case WM_INITDIALOG:
//设置对话框标题栏,直接在rc资源文件文件中添加WS_CAPTION样式,动手实验
//对话框没有标题栏则会失败
SetWindowText(hDlg, TEXT("Dialog Box Caption"));
//焦点设置为第一个含有WS_TABSTOP的子窗口控件是按钮
return TRUE;//完成对话框初始化
case WM_COMMAND://处理对话框控件消息
switch(LOWORD(wparam))
{
case IDOK:
case IDCANCEL:
EndDialog(hDlg,0);//销毁对话框,控制返回给WndProc
return TRUE;
}
break;
}
return FALSE;//未处理的对话框消息
}
/******************************************************************************
AboutDlgProc对话框过程:对话框过程属于Windows,与窗口过程相似,发送给对话框的消息是由用户程序中的对话框过程来处理的。
对话框过程与窗口过程的区别:
函数返回值类型不同,窗口过程是LRESULT,对话框是BOOL类型。
窗口过程不处理一条消息时,它会调用DefWindowProc;当对话框过程处理一条消息时,它会返回TRUE(非零),而当它不处理一条消息时,返回FALSE(零)。
对话框过程不需要处理WM_PAINT和WM_DESTROY消息。它也不会收到 WM_CREATE消息。然而它会在处理一条专门的WM_INITDIALOG消息时进行初始化。
*******************************************************************************
DialogBox函数 :从对话框模板资源创建模式对话框
void DialogBoxA(
hInstance, //实例句柄
lpTemplate,//对话框模板
hWndParent,//拥有对话框的窗口的句柄
lpDialogFunc//指向对话框窗口的指针
);
返回值:调用成功返回值=0;调用失败,返回值=-1;
*******************************************************************************
EndDialog函数:销毁一个模态对话框,使系统结束该对话框的所有处理。
BOOL EndDialog(
HWND hDlg, //对话框句柄
INT_PTR nResult //从创建对话框的函数返回给应用程序的值---0.
);
*******************************************************************************
WM_INITDIALOG消息:是对话框过程接收到的第一条消息。这条消息只发送给对话框过程。当对话框过程返回TRUE时,
Windows会把输入焦点设置到对话框的第一个含有 WS_TABSTOP样式的子窗口控件。在本程序的对话框中,
第一个含有WS_TABSTOP的子窗口控件是按钮。此外,在处理 WM_INITDIALOG时,
对话框过程也可以调用SetFocus函数来设置焦点所在的子窗口控件,同时返回FALSE---参见ABOUT2实例。
*/
资源脚本文件063_ABOUT1.rc中包含对话框、图标和菜单3个资源:
/
//
// Dialog
//
ABOUTBOX DIALOGEX 32, 32, 313, 199
STYLE DS_SETFONT | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
EXSTYLE WS_EX_NOACTIVATE
MENU ABOUT1
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
DEFPUSHBUTTON "确定",IDOK,121,155,50,14
ICON "ABOUT1",IDC_STATIC,10,10,20,20
CTEXT "ABOUT1",IDC_STATIC,95,43,83,8
CTEXT "About Box Demo Program",IDC_STATIC,95,63,83,8
CTEXT "(C) Chales Petzold,1998",IDC_STATIC,95,84,83,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 "favicon.ico"
运行结果:
图10-1 模态对话框
总结
实例ABOUT1.C由主窗口菜单“HELP”激活一个模态对话框。模态对话框由资源添加,包含一个图标,三个静态文本控件和一个button控件。只有当关闭模态对话框时,才可以切换到主窗口。接下来我们分析一下具体实现步骤。
在WinMain主程序中,初始化窗口类时,绑定菜单资源和主窗口过程。
主窗口过程Wndproc处理WM_CREATE消息时,获取当前进程句柄。
处理WM_COMMAND消息时,如果点击HELP菜单,则调用DialogBox宏激活一个模态对话框窗口。DialogBox宏是对创建模态对话框窗口DialogBoxParam函数的调用。参数包括当前进程句柄,窗口句柄,对话框ID和绑定的对话框过程。
对话框过程AboutDlgProc用来处理模态对话框消息。对话框过程首先处理WM_INITDIALOG消息,相当于主窗口过程的WM_CREATE消息,进行一些初始化工作。实例调用SetWindowText函数给对话框窗口添加了一个标题。
在本程序的对话框中,第一个含有WS_TABSTOP的子窗口控件是按钮。此外,在处理 WM_INITDIALOG时,对话框过程也可以调用SetFocus函数来设置焦点所在的子窗口控件,同时返回FALSE。
【注意】添加标题的前提是对话框属性中必须包含标题栏,可以在VS中设置对话框属性,也可以直接在rc资源文件文件中添加WS_CAPTION样式。初始化完成后,默认对话框焦点为含有WS_TABSTOP的子窗口控件,本例为“确定”按钮控件。最后是return TRUE语句结束,表示自定义对话框过程已经处理过该消息。如果是return FALSE语句,则表示该消息交给Windows系统默认的对话框窗口过程处理。
处理WM_COMMAND消息,为对话框内的子窗口控件消息,调用EndDialog函数销毁对话框窗口,返回主窗口过程WndProc。
对话框过程的最后是return FALSE;,表示未处理的对话框消息都交给Windows系统默认的对话框过程处理。
10.1.2 模态对话框
■对话框及其模板
在VS中,给程序添加对话框要先在项目解决方案的资源文件中添加资源,选择Dialog Box。然后会显示出一个具有标题栏、OK和Cancel按钮的对话框, 标题栏的标题是“Dialog”。同时显示的Controls工具栏可用来在对话框中插入各种控件。
●修改或设置对话框属性
VS会给予对话框一个标准的ID名:"AboutBox"。你可以在资源视图栏用鼠标选中对话框资源,按下ALT+ENTER键弹出属性边框,修改对话框属性。也可以在VS中打开对话框资源,选中对话框后点击鼠标右键,单机属性菜单,在属性栏修改对话框属性。还有一种方法就是直接在rc资源脚本文件中修改对话框属性。
/
//
// Dialog
//
ABOUTBOX (资源ID)DIALOGEX (资源类型)32, 32, 313, 199(矩形尺寸)
STYLE DS_SETFONT | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU(窗口样式:设置字体、系统等宽字体、弹出、可见、标题栏、系统菜单)
EXSTYLE WS_EX_NOACTIVATE(对话框窗口扩展样式:被激活时不获取焦点)
MENU ABOUT1(对话框菜单)
FONT 8, "MS Shell Dlg", 400, 0, 0x1 (对话框窗口字体)
BEGIN(对话框窗口包含的控件)
DEFPUSHBUTTON "确定",IDOK,121,155,50,14(BUTTON控件,文本,ID和尺寸)
ICON "ABOUT1",IDC_STATIC,10,10,20,20(图标控件,ID和尺寸)
CTEXT "ABOUT1",IDC_STATIC,95,43,83,8(静态文本控件,ID和尺寸)
CTEXT "About Box Demo Program",IDC_STATIC,95,63,83,8(上同)
CTEXT "(C) www.bcdaren.com 编程达人",IDC_STATIC,83,84,134,8(上同)
END
在VS中打开对话框属性栏,请选择Style选项卡为Popup。因为这个对话框有标题栏和系统菜单,所以请选择Title Bar和System Menu为TRUE。Menu菜单选项绑定菜单资源”ABOUT1”。(NAME)项填写对话框标题,Use System Font 系统选择 TRUE。
- STYLE语句与CreateWindow中的样式相似,模态对话框用WS_POPUP|DS_MODALFRAME
样式 | 功能 | 备注 |
WM_POPUP | 弹出式窗口 | 模态对话框用WS_POPUP|DS_MODALFRAME |
DS_MODALFRAME | 去边框 | |
WM_CAPTION | 标题栏,允许移动对话框 | CAPTION “DialogBox Caption”SetWindowText(hDlg,TEXT(“对话框”)); |
WS_SYSMENU | 添加系统菜单 | 允许Move或Close对话框 |
WS_THICKFRAME | 允许改变对话框大小 | 等价于对话框Border属性中选择Resizing |
图10-2 VS中设置对话框属性
2.控件类型
控件类型 | 窗口类 | 窗口样式 |
PUSHBUTTON | button | BS_PUSHBUTTON|WS_TABSTOP |
DEFPUSHBUTTON | button | BS_DEFPUSHBUTTON|WS_TABSTOP |
CHECKBOX | button | BS_CHECKBOX|WS_TABSTOP |
RADIOBUTTON | button | BS_RADIOBUTTON|WS_TABSTOP |
GROUPBOX | button | BS_GROUPBOX |
LTEXT | static | SS_LEFT|WS_GROUP |
CTEXT | static | SS_CENTER|WS_GROUP |
RTEXT | static | SS_RIGHT|WS_GROUP |
ICON | static | SS_ICON |
EDITTEXT | edit | ES_LEFT|WS_BORDER|WS_TABSTOP |
SCROLLBAR | scrollbar | SBS_HORZ |
LISTBOX | listbox | LBS_NOTIFY|WS_BORDER |
COMBOBOX | combobox | CBS_SIMPLE|WS_TABSTOP |
●对话框添加控件
1.添加位图
打开VS工具箱,如图10-3所示。
图10-3 VS工具箱
选中Picture Control(位图)控件,移动鼠标到对话框窗口选择添加位置。然后鼠标右键点击属性菜单,编辑位图属性。如图10-4所示。
图10-4 位图属性
Image绑定位图文件名,Type类型为Icon。
- 添加静态文本控件
工具箱选择Static Text控件,在对话框窗口添加静态文本控件。同样是鼠标右键,编辑static控件属性。
图10-5 设置静态文本控件属性
Caption选项填写文本内容,Align Text选项选择文本对齐方式。ID填写控件ID。Grroup为TRUE表示将其指定为Tab键顺序的一组控件中的第一个控件。
3.添加按钮控件
工具箱选择Button控件,添加到对话框窗口。
图10-6 设置Button控件属性
Tabstop选项为True,表示当前对话框焦点停靠在Button控件。Button控件ID为IDOK。
■对话框过程
对话框过程(Dialog Procedure)是在 Windows 程序中用于处理对话框消息的回调函数。当用户与对话框进行交互时,操作系统会将消息发送给对话框过程函数,并由该函数进行相应的处理。
●以下是一些关于对话框过程的要点:
对话框过程函数的定义:
1.对话框过程函数是一个特定格式的回调函数,其原型通常为 INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)。
hwndDlg 是对话框窗口的句柄,uMsg 是表示消息类型的无符号整数,wParam 和 lParam 是与消息相关的参数。
2.消息处理:
对话框过程函数可以根据不同的消息类型(uMsg)执行相应的处理逻辑。
常见的对话框消息包括 WM_INITDIALOG(对话框初始化)、WM_COMMAND(控件命令)、WM_CLOSE(关闭对话框)等。对话框过程不需要处理WM_PAINT和WM_DESTROY消息。它也不会收到 WM_CREATE消息。然而它会在处理一条专门的WM_INITDIALOG消息时进行初始化。
使用 switch 语句或一系列的 if 语句来根据消息类型进行分支处理。
3.控件消息处理:
对话框中的控件(如按钮、文本框等)通常会发送与其相关的消息,例如按钮点击事件、文本框内容变化等。
在对话框过程函数中,可以使用 WM_COMMAND 消息来处理控件消息。
通过检查 wParam 和 lParam 参数,可以确定具体是哪个控件发送了消息,以及消息的类型。
4.对话框过程函数的返回值:
窗口过程的返回值是LRESULT;而对话框过程的返回值是BOOL。BOOL在Windows头文件中被定义成int。
当窗口过程不处理一条消息时,它会调用DefWindowProc;当对话框过程处理一条消息时,它会返回TRUE(非零——消息处理成功),而当它不处理一条消息时,返回FALSE(零——消息处理失败)。
●示例
发送给对话框的消息是由用户程序中的对话框过程来处理的。这个过程与真正的窗口过程看起来很像,但其实是不一样的。对话框的窗口过程属于Windows。对于许多消息,这个窗口过程会调用对话框过程。下面是ABOUT1程序的对话框过程:
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 ;
}
对话框过程与一般窗口过程的参数是一样的,而且也必须被定义成CALLBACK函数。 尽管我使用了 hDlg作为对话框窗口的句柄,但其实你也可以使用hwnd。
●对话框过程与窗口过程的区别:
1.窗口过程的返回值是LRESULT;而对话框过程的返回值是BOOL。BOOL在Windows头文件中被定义成int。
2.当窗口过程不处理一条消息时,它会调用DefWindowProc;当对话框过程处理一条消息时,它会返回TRUE(非零),而当它不处理一条消息时,返回FALSE(零)。
3.对话框过程不需要处理WM_PAINT和WM_DESTROY消息。它也不会收到 WM_CREATE消息。然而它会在处理一条专门的WM_INITDIAL0G消息时进行初始化。
由于模态对话框的消息并不通过程序的消息循环,所以你不用担心这种对话框内的键盘快捷键会有什么问题。
■激活对话框
当WndProc处理WM_CREATE时,ABOUT1获取程序的实例句柄并把它存在一个静态变量里:
hlnstance = ((LPCREATESTRUCT) - IParam)->hInsCance;
ABOUT 1会判断WM_COMMAND消息的wParam低位字是否为IDM_APP_ABOUT。 当结果为真时,程序会调用DialogBox:
DialogBox (hlnstance, TEXT ("AbouCBox"), hwnd, AboutDlgProc);
这个函数的参数包括实例句柄、对话框的名称(在资源脚本中定义)、父窗口(此程序的主窗口)和对话框过程的地址。如果对话框模板中没有使用名称,而是使用数字标识符的话,你可以用MAKEINTRESOURCE宏来将其转换为字符串。
当从菜单中选择HELP时,会显示对话框。你可以通过用鼠标单击OK按钮,按下空格键,或者按下回车键来结束此对话框。当回车键或空格键被按下时,Windows会向对话框发送WM_COMMAND消息,消息的wParam参数的低位字是默认按钮的ID。本例中该ID是IDOK。你还可以通过按下Esc键来结束对话框。此时,Windows 发送的 WM_COMMAND 消息中的 ID 等于 IDCANCEL。
直到对话框结束以后,用来显示对话框的DialogBox函数才将控制返回给WndProc。 DialogBox的返回值等于对话框过程中调用的EndDialog函数的第二个参数。(这个值在 ABOUT 1中没有使用,而在ABOUT2中使用了。)之后WndProc可以将控制返回给Windows。
当对话框正在显示时,WndProc依旧可以处理消息。事实上,你可以从对话框过程中向WndProc发送消息。ABOUT1的主窗口是对话框弹出窗口的父窗口,故此AboutDlgProc中对SendMessage的调用可如下开始:
SendMessage (GetParent (hDlg), ...);