Bootstrap

《Windows API 每日一练》10.1 模态对话框

对话框分“模态”和“非模态”两种,其中模态对话框最为常见。当程序显示一个模态对话框时,用户不能在对话框和该程序的其他窗口之间进行切换。用户必须先明确地终止该对话框。这通常由单击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。 

  1. 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。

  1. 添加静态文本控件

工具箱选择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), ...);

;