对话框--windows内幕

对话框的一般形式是包含多种子窗口控件的弹出式窗口,这些控件的大小和位置在程序资源描述文件的「对话框模板」中指定。虽然程序写作者能够「手工」定义对话框模板,但是现在通常是在Visual C++ Developer Studio中以交谈式操作的方式设计的,然后由Developer Studio建立对话框模板。

当程序呼叫依据模板建立的对话框时,Sys负责建立弹出式对话框窗口和子窗口控件,并提供处理对话框消息(包括所有键盘和鼠标输入)的窗口消息处理程序。有时候称呼完成这些功能的Windows内部程序代码为「对话框管理器」。

对话框分为两类:「模态的」和「非模态的」,其中模态对话框最为普遍。当您的程序显示一个模态对话框时,使用者不能在对话框与同一个程序中的另一个窗口之间进行切换,使用者必须主动结束该对话框,这藉由通过按一下「OK」或者「Cancel」键来完成。不过,在显示模态对话框时,使用者通常可以从目前的程序切换到另一个程序。而有些对话框(称为「系统模态」)甚至连这样的切换程序操作也不允许。在Windows中,显示了系统模态对话框之后,要完成其它任何工作,都必须先结束该对话框。

 

与窗口消息处理程序类似,对话框程序都必须定义为一个CALLBACK(callback)函数。尽管我使用了hDlg作为对话框窗口的句柄,但是您也可以按照您自己的意思使用hwnd。首先,让我们来看一下这个函数与窗口消息处理程序的区别:

  • 窗口消息处理程序传回一个LRESULT。对话框传回一个BOOL,它在Windows表头文件中定义为int型态。
     
  • 如果窗口消息处理程序不处理某个特定的消息,那么它将呼叫DefWindowProc。如果对话框程序处理一个消息,那么它传回TRUE(非0),如果不处理,则传回FALSE(0)。
     
  • 对话框程序不需要处理WM_PAINT或WM_DESTROY消息。对话框程序不接收WM_CREAT消息,而是在特殊的WM_INITDIALOG消息处理期间,对话框程序执行初始化操作。
     

WM_INITDIALOG消息是对话框接收到的第一个消息,这个消息只发送给对话框程序。如果对话框程序传回TRUE,那么Windows将输入焦点设定给对话框中第一个具有WS_TABSTOP样式(我们将在ABOUT2的讨论中加以解释)的子窗口控件。在这个对话框中,第一个具有WS_TABSTOP样式的子窗口控件是按键。另外,对话框程序也可以在处理WM_INITDIALOG时使用SetFocus来将输入焦点设定为对话框中的某个子窗口控件,然后传回FALSE。

此外,对话框程序只处理WM_COMMAND消息。这是当按键被鼠标点中,或者在按钮具有输入焦点的情况下按下空格键时,按键控件发送给其父窗口的消息。这个控件的ID(我们在对话框模板中将其设定为IDOK)在wParam的低字组中。对于这个消息,对话框过程调用EndDialog,它告诉Windows清除对话框。对于所有其它消息,对话框程序传回FALSE,并告诉Windows内部的对话框窗口消息处理程序:我们的对话框程序不处理这些消息。

模态对话框的消息不通过您程序的消息队列,所以不必担心对话框中键盘快捷键的影响。

 

激活对话框

在WndProc中处理WM_CREATE消息时,ABOUT1取得程序的执行实体句柄并将它放在静态变量中:

hInstance = ((LPCREATESTRUCT) lParam)->hInstance ;
        

ABOUT1检查WM_COMMAND消息,以确保消息wParam的低位字等于IDM_APP_ABOUT。当它获得这样一个消息时,程序呼叫DialogBox:

DialogBox (hInstance, TEXT ("AboutBox"), hwnd, AboutDlgProc) ;
        

该函数需要执行实体句柄(在处理WM_CREATE时储存的)、对话框名称(在资源描述文件中定义的)、对话框的父窗口(也是程序的主窗口)和对话框程序的地址。如果您使用一个数字而不是对话框模板名称,那么可以用MAKEINTRESOURCE宏将它转换为一个字符串。

从菜单中选择「About About1」,将显示图11-2所示的对话框。您可以使用鼠标单击「OK」按钮、按空格键或者按Enter键来结束这个对话框。对任何包含内定按钮的对话框,在按下Enter键或空格键之后,Windows发送一个WM_COMMAND消息给对话框,并令wParam的低字组等于内定按键的ID,此时的ID为IDOK。按下Escape键也可以关闭对话框,这时Windows将发送一个WM_COMMAND消息,并令ID等于IDCANCEL。

直到对话框结束之后,用来显示对话框的DialogBox才将控制权传回给WndProc。DialogBox的传回值是对话框程序内部呼叫的EndDialog函数的第二个参数(这个值未在ABOUT1中使用,但会在ABOUT2中使用)。然后,WndProc可以将控制权传回给Windows。

即使在显示对话框时,WndProc也可以继续接收消息。实际上,您可以从对话框程序内部给WndProc发送消息。ABOUT1的主窗口是弹出式对话框窗口的父窗口,所以AboutDlgProc中的SendMessage呼叫可以使用如下叙述来开始:

SendMessage (GetParent (hDlg),  . . . ) ;

非模态对话框

在本章的开始,我曾经说过对话框分为「模态的」和「非模态的」两种。现在我们已经研究过这两种对话框中最常见的一种-模态对话框。模态对话框(不包括系统模态对话框)。允许使用者在对话框与其它程序之间进行切换。但是,使用者不能切换到同一程序的另一个窗口,直到模态对话框被清除为止。非模态对话框允许使用者在对话框与其它程序之间进行切换,又可以在对话框与建立对话框的窗口之间进行切换。因此,非模态对话框与使用者程序常见的普通弹出式窗口可能更为相似。

当使用者觉得让对话框保留片刻会更加方便时,使用非模态对话框是合适的。例如,文书处理程序经常使用非模态对话框来进行「Find」和「Change」操作。如果「Find」对话框是模态的,那么使用者必须从菜单中选择「Find」,然后输入要寻找的字符串,结束对话框,传回到文件中,接着再重复整个程序来寻找同一字符串的另一次出现。允许使用者在文件与对话框之间进行切换则会方便得多。

您已经看到,模态对话框是用DialogBox来建立的。只有在清除对话框之后,函数才会传回值。在对话框程序内使用EndDialog呼叫来终止对话框,DialogBox传回的是该呼叫的第二个参数的值。非模态对话框是使用CreateDialog来建立的,该函数所使用的参数与DialogBox相同。

hDlgModeless = CreateDialog (      hInstance, szTemplate,
        
                                  hwndParent, DialogProc) ;
        

区别是CreateDialog函数立即传回对话框的窗口句柄,并通常将这个窗口句柄存放到整体变量中。

尽管将DialogBox这一名字用于模态对话框而CreateDialog用于非模态对话框是随意的,但是您可以通过非模态对话框与普通窗口类似这一点来记住这两个函数的区别。CreateDialog可以令人想起CreateWindow函数来,而后者建立的是普通窗口。

模态对话框与非模态对话框的区别

使用非模态对话框与使用模态对话框相似,但是也有一些重要的区别:

首先,非模态对话框通常包含一个标题列和一个系统菜单按钮。当您在Developer Studio中建立对话框时,这些是内定选项。用于非模态对话框的对话框模板中的STYLE叙述形如:

STYLE WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_VISIBLE
        

标题列和系统菜单允许使用者,使用鼠标或者键盘将非模态对话框移动到另一个显示区域。对于模态对话框,您通常无须提供标题列和系统菜单,因为使用者不能在其下面的窗口中做任何其它的事情。

第二项重要的区别是:注意,在我们的范例STYLE叙述中包含有WS_VISIBLE样式。在 Developer Studio中,从「Dialog Properties」对话框的「More Styles」页面卷标中选择此选项。如果省略了WS_VISIBLE,那么您必须在CreateDialog呼叫之后呼叫ShowWindow:

hDlgModeless = CreateDialog (  . . .  ) ;
        
    ShowWindow (hDlgModeless, SW_SHOW) ;
        

如果您既没有包含WS_VISIBLE样式,又没有呼叫ShowWindow,那么非模态对话框将不会被显示。如果忽略这个事实,那么习惯于模态对话框的程序写作者在第一次试图建立非模态对话框时,经常会出现问题。

第三项区别:与模态对话框和消息框的消息不同,非模态对话框的消息要经过程序式的消息队列。要将这些消息传送给对话框窗口消息处理程序,则必须改变消息队列。方法如下:当您使用CreateDialog建立非模态对话框时,应该将从呼叫中传回的对话框句柄储存在一个整体变量(如hDlgModeless)中,并将消息循环改变为:

while (GetMessage (&msg, NULL, 0, 0))
        
{
        
           if (hDlgModeless == 0 || !IsDialogMessage (hDlgModeless, &msg))
        
    {
        
                  TranslateMessage (&msg) ;
        
                 DispatchMessage  (&msg) ;
        
    }
        
}
        

如果消息是发送给非模态对话框的,那么IsDialogMessage将它发送给对话框中窗口消息处理程序,并传回TRUE(非0);否则,它将传回FALSE(0)。只有hDlgModeless为0或者消息不是该对话框的消息时,才必须呼叫TranslateMessage和DispatchMessage函数。如果您将键盘快捷键用于您的程序窗口,那么消息循环将如下所示:

while (GetMessage (&msg, NULL, 0, 0))
        
{
        
           if (hDlgModeless == 0 || !IsDialogMessage (hDlgModeless, &msg))
        
           {
        
                  if (!TranslateAccelerator (hwnd, hAccel, &msg))
        
                 {
        
                                         TranslateMessage (&msg) ;
        
                                         DispatchMessage  (&msg) ;
        
                  }
        
    }
        
}
        

由于整体变量被初始化为0,所以hDlgModeless将为0,直到建立对话框为止,从而保证不会使用无效的窗口句柄来呼叫IsDialogMessage。在清除非模态对话框时,您也必须注意这一点,正如最后一点所说明的。

hDlgModeless变量也可以由程序的其它部分使用,以便对非模态对话框是否存在加以验证。例如,程序中的其它窗口可以在hDlgModeless不等于0时给对话框发送消息。

最后一项重要的区别:使用DestroyWindow而不是EndDialog来结束非模态对话框。当您呼叫DestroyWindow后,将hDlgModeless整体变量设定为0。

使用者习惯于从系统菜单中选择「Close」来结束非模态对话框。尽管启用了「Close」选项,Windows内的对话框窗口消息处理程序并不处理WM_CLOSE消息。您必须自己在对话框程序中处理它:

case        WM_CLOSE :
        
           DestroyWindow (hDlg) ;
        
           hDlgModeless = NULL ;
        
           break ;
        

注意这两个窗口句柄之间的区别:DestroyWindow的hDlg参数是传递给对话框程序的参数;hDlgModeless是从CreateDialog传回的整体变量,程序在消息循环内检验它。

您也可以允许使用者使用按键来关闭非模态对话框,处理方式与处理WM_CLOSE消息一样。对话框必须传回给建立它的窗口之任何数据都可以储存在整体变量中。如果不喜欢使用整体变量,那么您也可以用CreateDialogParam来建立非模态对话框,并按前面介绍的方法让它储存一个结构指针。

 
 
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值