⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 11. 对话框.txt

📁 本书介绍了在Microsoft Windows 98、Microsoft Windows NT 4.0和Windows NT 5.0下程序写作的方法
💻 TXT
📖 第 1 页 / 共 5 页
字号:

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消息来追踪按下的按钮。

「OK」和「Cancel」按钮


ABOUT2有两个按键,分别标记为「OK」和「Cancel」。在ABOUT2.RC的对话框模板中,「OK」按钮的ID值为IDOK(在WINUSER.H中被定义为1),「Cancel」按钮的ID值为IDCANCEL(定义为2),「OK」按钮是内定的:

DEFPUSHBUTTON              "OK",IDOK,35,212,50,14
        
  PUSHBUTTON                    "Cancel",IDCANCEL,113,212,50,14
        
在对话框中,通常都这样安排「OK」和「Cancel」按钮:将「OK」按钮作为内定按钮有助于用键盘接口终止对话。一般情况下,您通过单击两个鼠标按键之一,或者当所期望的按钮具有输入焦点时按下Spacebar来终止对话框。不过,如果使用者按下Enter,对话框窗口消息处理程序也将产生一个WM_COMMAND消息,而不管哪个控件具有输入焦点。wParam的低字组被设定为对话框中内定按键的ID值,除非另一个按键拥有输入焦点。在后一种情况下,wParam的低字组被设定为具有输入焦点之按键的ID值。如果对话框中没有内定按键,那么Windows向对话框程序发送一个WM_COMMAND消息,消息中wParam的低字组被设定为IDOK。如果使用者按下Esc键或者Ctrl-Break键,那么Windows令wParam等于IDCANCEL,并给对话框程序发送一个WM_COMMAND消息。所以,您不用在对话框程序中加入单独的处理键盘操作,因为通常终止对话框的按键会由Windows将这两个按键动作转换为WM_COMMAND消息。

AboutDlgProc函数通过呼叫EndDialog来处理这两种WM_COMMAND消息:

switch (LWORD (wParam))
        
{
        
case IDOK:
        
    iCurrentColor  = iColor ;
        
    iCurrentFigure = iFigure ;
        
    EndDialog (hDlg, TRUE) ;
        
    return TRUE ;
        
case IDCANCEL :
        
    EndDialog (hDlg, FALSE) ;
        
    return TRUE ;
        
ABOUT2的窗口消息处理程序在程序的显示区域中绘制矩形或椭圆时,使用了整体变量iCurrentColor和iCurrentFigure。AboutDlgProc在对话框中画图时使用了静态区域变量iColor和iFigure。

注意EndDialog的第二个参数的值不同,这个值是在WndProc中作为原DialogBox函数的传回值传回的:

case        IDM_ABOUT:
        
           if (DialogBox (hInstance, TEXT ("AboutBox"), hwnd, AboutDlgProc))
        
                  InvalidateRect (hwnd, NULL, TRUE) ;
        
           return 0 ;
        
如果DialogBox传回TRUE(非0),则意味着按下了「OK」按钮,然后需要使用新的颜色来更新WndProc显示区域。当AboutDlgProc收到一个WM_COMMAND消息并且消息的wParam的低字组等于IDOK时,AboutDlgProc将图形和颜色储存在整体变量iCurrentColor和iCurrentFigure中。如果DialogBox传回FALSE,则主窗口继续使用iCurrentColor和iCurrentFigure的原始设定。

TRUE和FALSE通常用于EndDialog呼叫中,以告知主窗口消息处理程序使用者是用「OK」还是用「Cancel」来终止对话框的。不过,EndDialog的参数实际上是一个int值,而DialogBox也传回一个int值。所以,用这种方法能比仅用TRUE或者FALSE传回更多的信息。

避免使用整体变量


在ABOUT2中使用整体变量可能会、也可能不会影响您。一些程序写作者(包括我自己)较喜欢少用整体变量。ABOUT2中的整体变量iCurrentColor和iCurrentFigure看来使用得完全合法,因为它们必须同时在窗口消息处理程序和对话框程序中使用。不过,在一个有一大堆对话框的程序中,每个对话框都可能改变一堆变量的值,使整体变量的数量容易用得过多。

您可能更喜欢将程序中的对话框与数据结构相联系,该数据结构含有对话框可以改变的所有变量。您将在typedef叙述中定义这些结构。例如,在ABOUT2中,可以定义与「About」方块相联系的结构:

typedef struct
        
{
        
           int iColor, iFigure ;
        
}
        
ABOUTBOX_DATA ;
        
在WndProc中,您可以依据此结构来定义并初始化一个静态变量:

static ABOUTBOX_DATA ad = { IDC_BLACK, IDC_RECT } ;
        
在WndProc中也是这样,用ad.iColor和ad.iFigure替换了所有的iCurrentColor和iCurrentFigure。呼叫对话框时,使用DialogBoxParam而不用DialogBox。此函数的第五个参数可以是任意的32位值。一般来说,此值设定为指向一个结构的指针,在这里是WndProc中的ABOUTBOX_DATA结构。

case        IDM_ABOUT:
        
           if (DialogBoxParam (hInstance, TEXT ("AboutBox"),
        
                        hwnd, AboutDlgProc, &ad))
        
                  InvalidateRect (hwnd, NULL, TRUE) ;
        
           return 0 ;
        
这是关键:DialogBoxParam的最后一个参数是作为WM_INITDIALOG消息中的lParam传递给对话框程序的。

对话框程序有两个ABOUTBOX_DATA结构型态的静态变量(一个结构和一个指向结构的指针):

static ABOUTBOX_DATA ad, * pad ;
        
在AboutDlgProc中,此定义代替了iColor和iFigure的定义。在WM_INITDIALOG消息的开始部分,对话框程序根据lParam设定了这两个变量的值:

pad = (ABOUTBOX_DATA *) lParam ;
        
ad = * pad ;
        
第一道叙述中,pad设定为lParam的指标。亦即,pad实际是指向在WndProc定义的ABOUTBOX_DATA结构。第二个参数完成了从WndProc中的结构,到DlgProc中的区域结构的字段对字段内容复制。

现在,除了使用者按下「OK」按钮时所用的程序代码以之外,所有的AboutDlgProc都用ad.iColor和ad.iFigure替换了iFigure和iColor。这时,将区域结构的内容复制回WndProc中的结构:

case        IDOK:
        
           * pad = ad ;
        
           EndDialog (hDlg, TRUE) ;
        
           return TRUE ;
        
Tab停留和分组


在第九章,我们利用窗口子类别化为COLORS1增加功能,使我们能够按下Tab键从一个滚动条转移到另一个滚动条。在对话框中,窗口子类别化是不必要的,因为Windows完成了将输入焦点从一个控件移动到另一个控件的所有工作。尽管如此,您必须在对话框模板中使用WS_TABSTOP和WS_GROUP窗口样式达到此目的。对于所有想要使用Tab键存取的控件,都要在其窗口样式中指定WS_TABSTOP。

如果参阅表11-1,您就会注意到许多控件将WS_TABSTOP定义为内定样式,其它一些则没有将它作为内定样式。一般而言,不包含WS_TABSTOP样式的控件(特别是静态控件)不应该取得输入焦点,因为即使有了输入焦点,它们也不能完成操作。除非在处理WM_INITDIALOG消息时您将输入焦点设定给一个特定的控件,并从消息中传回FALSE。否则Windows将输入焦点设定为对话框内第一个具有WS_TABSTOP样式的控件。

Windows给对话框增加的第二个键盘接口包括光标移动键,这种接口对于单选按钮有特殊的重要性。如果您使用Tab键移动到某一组内目前选中的单选按钮,那么,就需要使用光标移动键,将输入焦点从该单选按钮移动到组内其它单选按钮上。使用WS_GROUP窗口样式即可获得这个功能。对于对话框模板中的特定控件序列,Windows将使用光标移动键把输入焦点从第一个具有WS_GROUP样式的控制权切换到下一个具有WS_GROUP样式的控件中。如果有必要,Windows将从对话框的最后一个控件循环到第一个控件,以便找到分组的结尾。

在内定设定下,控件LTEXT、CTEXT、RTEXT和ICON包含有WS_GROUP样式,这种样式方便地标记了分组的结尾。您必须经常将WS_GROUP样式加到其它型态的控件中。

让我们来看一看ABOUT2.RC中的对话框模板。四个具有WS_TABSTOP样式的控件是每个组的第一个单选按钮(明显地包含)和两个按键(内定设定)。在第一次启动对话框时,您可以使用Tab键在这四个控件之间移动。

在每组单选按钮中,您可以使用光标移动键切换输入焦点并改变选中标记。例如, Color下拉式清单方块的第一个单选按钮(Black)和 Figure下拉式清单方块都具有WS_GROUP样式。这意味着您可以用光标移动键将焦点从「Black」单选按钮移动到 Figure分组方块中。类似的情形,Figure分组方块的第一个单选按钮( Rectangle)和DEFPUSHBUTTON都具有WS_GROUP样式,所以您可以使用光标移动键在组内两个单选按钮- Rectangle和Ellipse之间移动。两个按键都有WS_GROUP样式,以阻止光标移动键在按键具有输入焦点时起作用。

使用ABOUT2时,Windows的对话框管理器在两组单选按钮中完成一些相当复杂的处理。正如所预期的那样,处于单选按钮组内时,光标移动键切换输入焦点,并给对话框程序发送WM_COMMAND消息。但是,当您改变了组内选中的单选按钮时,Windows也给新选中的单选按钮设定了WS_TABSTOP样式。当您下一次使用Tab切换到这一组后,Windows将会把输入焦点设定为选中的单选按钮。

文字字段中的「&」将导致紧跟其后的字母以底线显示,这就增加了另一种键盘接口,您可以通过按底线字母来将输入焦点移动到任意单选按钮上。透过按下C(代表 Color下拉式清单方块)或者F(代表Figure下拉式清单方块),您可以将输入焦点移动到相对应组内目前选中的单选按钮上。

尽管程序写作者通常让对话框管理器来完成这些工作,但是Windows提供了两个函数,以便程序写作者找寻下一个或者前一个Tab键停留项或者组项。这些函数为:

hwndCtrl = GetNextDlgTabItem (hDlg, hwndCtrl, bPrevious) ;
        

hwndCtrl = GetNextDlgGroupItem (hDlg, hwndCtrl, bPrevious) ;
        
如果bPrevious为TRUE,那么函数传回前一个Tab键停留项或组项;如果为FALSE,则传回下一个Tab键停留项或者组项。

在对话框上画图


ABOUT2还完成了一些相对说来很特别的事情,亦即在对话框上画图。让我们来看一看它是怎样做的。在ABOUT2.RC的对话框模板内,使用位置和大小为我们想要画图的区域定义了一块空白文字控件:

LTEXT  ""  IDC_PAINT, 114, 67, 72, 72
        
这个区域为18个字符宽和9个字符高。由于这个控件没有文字,所以窗口消息处理程序为「静态」类别所做的工作,只是在必须重绘这个子窗口控件时清除其背景。

在目前颜色或图形选择发生改变,或者对话框自身获得一个WM_PAINT消息时,对话框过程调用PaintTheBlock,这个函数在ABOUT2.C中:

PaintTheBlock (hCtrlBlock, iColor, iFigure) ;
        
在AboutDlgProc中,窗口句柄hCtrlBlock已经在处理WM_INITDIALOG消息时被设定:

hCtrlBlock = GetDlgItem (hDlg, IDD_PAINT) ;
        
下面是PaintTheBlock函数:

void PaintTheBlock (HWND hCtrl, int iColor, int iFigure)
        
{
        
           InvalidateRect (hCtrl, NULL, TRUE) ;
        
           UpdateWindow (hCtrl) ;
        
           PaintWindow (hCtrl, iColor, iFigure) ;
        
}
        
这个函数使得子窗口控件无效,并为控件窗口消息处理程序产生一个WM_PAINT消息,然后呼叫ABOUT2中的另一个函数PaintWindow 。

PaintWindow函数取得一个设备内容句柄,并将其放到hCtrl中,画出所选图形,根据所选颜色用一个着色画刷填入图形。子窗口控件的大小从GetClientRect获得。尽管对话框模板以字符为单位定义了控件的大小,但GetClientRect取得以图素为单位的尺寸。您也可以使用函数MapDialogRect将对话框中的字符坐标转换为显示区域中的图素坐标。

我们并非真的绘制了对话框的显示区域,实际绘制的是子窗口控件的显示区域。每当对话框得到一个WM_PAINT消息时,就令子窗口控件的显示区域失效,并更新它,使它确信现在其显示区域又有效了,然后在其上画图。

将其它函数用于对话框


大多数可以用在子窗口的函数也可以用于对话框中的控件。例如,如果您想捣乱的话,那么可以使用MoveWindow在对话框内移动控件,强迫使用者用鼠标来追踪它们。

有时,您需要根据其它控件的设定,动态地启用或者禁用某些控件,这需要呼叫:

EnableWindow (hwndCtrl, bEnable) ;
        
当bEnable为TRUE(非0)时,它启用控件;当bEnable为FALSE(0)时,它禁用控件。在控件被禁用时,它不再接收键盘或者鼠标输入。您不能禁用一个拥有输入焦点的控件。

定义自己的控件


尽管Wi

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -