一、Windows模态对话框—使用对话框控件
在第九章中,您会发现大多数子窗口控件发送WM_COMMAND消息给其父窗口(唯一例外的是滚动条控件)。您还看到,经由发送消息给子窗口控件,父窗口可以改变子窗口控件的状态(例如,选择或不选择单选按钮、复选框)。您也可以用类似方法在对话框程序中改变控件。例如,如果您设计了一系列单选按钮,就可以发送消息给它们,以选择或者不选择这些按钮。不过,Windows也提供了几种使用对话框控件的简单办法。我们来看一看对话框程序与子窗口控件相互通信的方式。
ABOUT2的对话框模板显示在程序11-2的ABOUT2.RC资源描述档中。GROUPBOX控件只是一个带标题(标题为「Color」或者「Figure」)的分组方块,每组单选按钮都由这样的分组方块包围。前一组的八个单选按钮是互斥的,第二组的两个单选按钮也是如此。
当用鼠标单击其中一个单选按钮时(或者当单选按钮拥有输入焦点时按空格键),子窗口向其父窗口发送一个WM_COMMAND消息,消息的wParam的低字组被设为控件的ID,wParam的高字组是一个通知码,lParam值是控件的窗口句柄。对于单选按钮,这个通知码是BN_CLICKED或者0。然后Windows中的对话框窗口消息处理程序将这个WM_COMMAND消息发送给ABOUT2.C内的对话框程序。当对话框程序收到一个单选按钮的WM_COMMAND消息时,它为此按钮设定选中标记,并为组中其它按钮清除选中标记。
您可能还记得在第九章中已经提过,选中和不选中按钮均需要向子窗口控件发送BM_CHECK消息。要设定一个按钮选中标记,您可以使用:
SendMessage (hwndCtrl, BM_SETCHECK, 1, 0) ;
要消除选中标记,您可以使用:
SendMessage (hwndCtrl, BM_SETCHECK, 0, 0) ;
其中hwndCtrl参数是子窗口按钮控件的窗口句柄。
但是在对话框程序中使用这种方法是时有点问题的,因为您不知道所有单选按钮的窗口句柄,只是从您获得的消息中知道其中一个句柄。幸运的是,Windows为您提供了一个函数,可以用对话框句柄和控件ID来取得一个对话框控件的窗口句柄:
hwndCtrl = GetDlgItem (hDlg, id) ;
(您也可以使用如下函数,从窗口句柄中取得控件的ID值:
id = GetWindowLong (hwndCtrl, GWL_ID) ;
但是在大多数情况下这是不必要的。)
您会注意到,在程序11-2所示的表头文件ABOUT2.H中,八种颜色的ID值是从IDC_BLACK到IDC_WHITE连续变化的,这种安排在处理来自单选按钮的WM_COMMAND消息时将会很有用。在第一次尝试选中或者不选中单选按钮时,您可能会在对话框程序中编写如下的程序:
static int iColor ; 其它行程序 case WM_COMMAND: switch (LOWORD (wParam)) { 其它行程序 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) ; for (i = IDC_BLACK, i <= IDC_WHITE, i++) SendMessage (GetDlgItem (hDlg, i), BM_SETCHECK, i == LOWORD (wParam), 0) ; return TRUE ; 其它行程序
这种方法能让人满意地执行。您将新的颜色值储存在iColor中,并且还建立了一个循环,轮流使用所有八种颜色的ID值。您取得每个单选按钮控件的窗口句柄,并用SendMessage给每个句柄发送一条BM_SETCHECK消息。只有对于向对话框窗口消息处理程序发送WM_COMMAND消息的按钮,这个消息的wParam值才被设定为1。
第一种简化的方法是使用专门的对话框程序SendDlgItemMessage:
SendDlgItemMessage (hDlg, id, iMsg, wParam, lParam) ;
它相同于:
SendMessage (GetDlgItem (hDlg, id), id, wParam, lParam) ;
现在,循环将变成这样:
for (i = IDC_BLACK, i <= IDC_WHITE, i++) SendDlgItemMessage (hDlg, i, BM_SETCHECK, i == LWORD (wParam), 0) ;
稍微有些改进。但是真正的重大突破要等到使用了CheckRadioButton函数时才会出现:
CheckRadioButton (hDlg, idFirst, idLast, idCheck) ;
这个函数将ID在idFirst到idLast之间的所有单选按钮的选中标记都清除掉,除了ID为idCheck的单选按钮,因为它是被选中的。这里,所有ID必须是连续的。从此我们可以完全摆脱循环,并使用:
CheckRadioButton (hDlg, IDC_BLACK, IDC_WHITE, LOWORD (wParam)) ;
这正是ABOUT2对话框程序所采用的方法。
在使用复选框时,也提供了类似的简化函数。如果您建立了一个「CHECKBOX」对话框窗口控件,那么可以使用如下的函数来设定和清除选中标记:
CheckDlgButton (hDlg, idCheckbox, iCheck) ;
如果iCheck设定为1,那么按钮被选中;如果设定为0,那么按钮不被选中。您可以使用如下的方法来取得对话框中某个复选框的状态:
iCheck = IsDlgButtonChecked (hDlg, idCheckbox) ;
在对话框程序中,您既可以将选中标记的目前状态储存在一个静态变量中,又可以在收到一个WM_COMMAND消息后,使用如下方法触发按钮:
CheckDlgButton (hDlg, idCheckbox, !IsDlgButtonChecked (hDlg, idCheckbox)) ;
如果您定义了BS_AUTOCHECKBOX控件,那么完全没有必要处理WM_COMMAND消息。在终止对话框之前,您只要使用IsDlgButtonChecked就可以取得按钮目前的状态。不过,如果您使用BS_AUTORADIOBUTTON样式,那么IsDlgButtonChecked就不能令人满意了,因为需要为每个单选按钮都呼叫它,直到函数传回TRUE。实际上,您还要拦截WM_COMMAND消息来追踪按下的按钮。