读书人

Windows程式开发设计指南(十一)会话

发布时间: 2012-09-15 19:09:29 作者: rapoo

Windows程式开发设计指南(十一)对话方块

11. 对话方块

如果有很多输入超出了功能表可以处理的程度,那么我们可以使用对话方块来取得输入资讯。程式写作者可以通过在某选项後面加上省略号(…)来表示该功能表项将启动一个对话方块。

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

当程式呼叫依据模板建立的对话方块时,Microsoft Windows 98负责建立弹出式对话方块视窗和子视窗控制项,并提供处理对话方块讯息(包括所有键盘和滑鼠输入)的视窗讯息处理程式。有时候称呼完成这些功能的Windows内部程式码为「对话方块管理器」。

Windows的内部对话方块视窗讯息处理程式所处理的许多讯息也传递给您自己程式中的函式,这个函式即是所谓的「对话方块程序」或者「对话程序」。对话程序与普通的视窗讯息处理程式类似,但是也存在著一些重要区别。一般来说,除了在建立对话方块时初始化子视窗控制项,处理来自子视窗控制项的讯息以及结束对话方块之外,程式写作者不需要再给对话方块程序增加其他功能。对话程序通常不处理WM_PAINT讯息,也不直接处理键盘和滑鼠输入。

对话方块这个主题的含义太广了,因为它还包含子视窗控制项的使用。不过,我们已经在第九章研究了子视窗控制项。当您在对话方块中使用子视窗控制项时,第九章所提到的许多工作都可以由Windows的对话方块管理器来完成。尤其是,在程式COLORS1中遇到在卷动列之间切换输入焦点的问题也不会在对话方块中出现。Windows会处理对话方块中的控制项之间切换输入焦点所必需完成的全部工作。

不过,在程式中添加对话方块要比添加图示或者功能表更麻烦一些。我们将从一个简单的对话方块开始,让您对各部分之间的相互联系有所了解。

模态对话方块
 

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

建立「About」对话方块
 

Windows程式即使不需要接收使用者输入,也通常具有由功能表上的「About」选项启动的对话方块,该对话方块用来显示程式的名字、图示、版权旗标和标记为「OK」的按键,也许还会有其他资讯(例如技术支援的电话号码)。我们将要看到的第一个程式除了显示一个「About」对话方块外,别无它用。这个ABOUT1程式如程式11-1所示:

 程式11-1  ABOUT1ABOUT1.C/*------------------------------------   ABOUT1.C -- About Box Demo Program No. 1               (c) Charles Petzold, 1998-------------------------------------*/#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,                    PSTR szCmdLine, int iCmdShow){     static TCHAR szAppName[] = TEXT ("About1") ;     MSG          msg ;     HWND         hwnd ;WNDCLASS     wndclass ;          wndclass.style         = CS_HREDRAW | CS_VREDRAW ;     wndclass.lpfnWndProc   = WndProc ;     wndclass.cbClsExtra    = 0 ;     wndclass.cbWndExtra    = 0 ;     wndclass.hInstance     = hInstance ;     wndclass.hIcon         = LoadIcon (hInstance, szAppName) ;     wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;     wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;     wndclass.lpszMenuName  = szAppName ;     wndclass.lpszClassName = szAppName ;          if (!RegisterClass (&wndclass))     {          MessageBox (NULL, TEXT ("This program requires Windows NT!"),                szAppName, MB_ICONERROR) ;          return 0 ;     }          hwnd = CreateWindow (szAppName, TEXT ("About Box Demo Program"),                          WS_OVERLAPPEDWINDOW,                          CW_USEDEFAULT, CW_USEDEFAULT,                          CW_USEDEFAULT, CW_USEDEFAULT,                          NULL, NULL, hInstance, NULL) ;          ShowWindow (hwnd, iCmdShow) ;     UpdateWindow (hwnd) ;           while (GetMessage (&msg, NULL, 0, 0))     {          TranslateMessage (&msg) ;          DispatchMessage (&msg) ;     }     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 (hInstance, TEXT ("AboutBox"), hwnd, AboutDlgProc) ;               break ;          }          return 0 ;               case WM_DESTROY :          PostQuitMessage (0) ;          return 0 ;     }     return DefWindowProc (hwnd, message, wParam, lParam) ;}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 ;}
 ABOUT1.RC (摘录)//Microsoft Developer Studio generated resource script.#include "resource.h"#include "afxres.h"/////////////////////////////////////////////////////////////////////////////// DialogABOUTBOX DIALOG DISCARDABLE  32, 32, 180, 100STYLE DS_MODALFRAME | WS_POPUPFONT 8, "MS Sans Serif"BEGIN    DEFPUSHBUTTON              "OK",IDOK,66,80,50,14    ICON            "ABOUT1",IDC_STATIC,7,7,21,20    CTEXT           "About1",IDC_STATIC,40,12,100,8    CTEXT           "About Box Demo Program",IDC_STATIC,7,40,166,8    CTEXT           "(c) Charles Petzold, 1998",IDC_STATIC,7,52,166,8END/////////////////////////////////////////////////////////////////////////////// MenuABOUT1MENU DISCARDABLE BEGIN    POPUP "&Help"   BEGIN        MENUITEM "&About About1...",            IDM_APP_ABOUT  ENDEND/////////////////////////////////////////////////////////////////////////////// IconABOUT1 ICON    DISCARDABLE     "About1.ico"
 RESOURCE.H (摘录)// Microsoft Developer Studio generated include file.// Used by About1.rc#define IDM_APP_ABOUT        40001#define IDC_STATIC              -1
ABOUT1.ICO


 

Windows程式开发设计指南(十一)会话方块

藉由後面章节中介绍的方法,您还可以在程式中建立图示和功能表。图示和功能表的ID名均为「About1」。功能表有一个选项,它产生一条ID名为IDM_APP_ABOUT的WM_COMMAND讯息。这使得程式显示的图11-1所示的对话方块。


 

Windows程式开发设计指南(十一)会话方块

图11-1 程式ABOUT1的对话方块

对话方块及其模板
 

要把一个对话方块添加到Visual C++ Developer Studio会有的应用程式上,可以先从 Insert功能表中选择 Resource,然後选择 Dialog Box。现在一个对话方块出现在您的眼前,该对话方块带有标题列、标题—ialog)以及 OK Cancel按钮。 Controls工具列允许您在对话方块中插入不同的控制项。

Developer Studio将对话方块的ID设为标准的IDD_DIALOG1。您可以在此名称上(或者在对话方块本身)单击右键,然後从功能表中选择 Properties 。在本程式中,将ID改为「AboutBox」(带有引号)。为了与我建立的对话方块保持一致,请将 X Pos Y Pos栏位改为32。这表示对话方块相对於程式视窗显示区域左上角的显示位置待会会有关於对话方块座标的详细讨论)。

现在,继续在 Properties对话方块中选择 Styles页面标签。因为此对话方块没有标题列,所以不要选取 Title Bar核取方块。然後请单击 Properties对话方块的 关闭按钮。

现在可以设计对话方块了。因为不需要 Cancel按钮,所以先单击该按钮,然後按下键盘上的 Delete键。接著单击 OK按钮,将其移动到对话方块的底部。在Developer Studio视窗下面的工具列上有一个小点阵图,它可使控制项在视窗内水平居中对齐,请按下此钮。

如果您要让程式的图示出现在对话方块中,可以这样做:先在浮动的 Controls工具列中按下「 Pictures」按钮。将滑鼠移动到对话方块的表面,按下左键,然後拉出一个矩形。这就是图示将出现的位置。然後在次矩形上按下滑鼠右键,从功能表中选择 Properties。保持 ID IDC_STATIC。此识别字在RESOURCE.H中定义为-1,用於程式中不使用的所有ID。将 Type改为 Icon。您可以在 Image栏位输入程式图示的名称,或者,如果您已经建立了一个图示,那么您也可以从下拉式清单方块中选择一个名称(About1)。

对於对话方块中的三个静态字串,可以从 Controls工具列中选择 Static Text,然後确定文字在对话方块中的位置。右键单击控制项,然後从功能表中选择 Properties。在 Properties框的 Caption栏位中输入要显示的文字。选择 Styles页面标签,从 Align Text栏位选择 Center

在添加这些字串的时候,若希望对话方块可以更大一些,请先选中对话方块,然後拖曳边框。您也可以选择并缩放控制项。通常用键盘上的游标移动键完成此操作会更容易些。箭头键本身移动控制项,按下Shift键後按箭头键,可以改变控制项的大小。所选控制项的座标和大小显示在Developer Studio视窗的右下角。

如果您建立了一个应用程式,那么以後在查看资源描述档ABOUT1.RC时,您将发现Developer Studio建立的模板。我所设计的对话方块模板如下:

ABOUTBOX DIALOG DISCARDABLE  32, 32, 180, 100STYLE DS_MODALFRAME | WS_POPUPFONT 8, "MS Sans Serif"BEGIN  DEFPUSHBUTTON   "OK",IDOK,66,80,50,14    ICON            "ABOUT1",IDC_STATIC,7,7,21,20    CTEXT           "About1",IDC_STATIC,40,12,100,8    CTEXT       "About Box Demo Program",IDC_STATIC,7,40,166,8    CTEXT       "(c) Charles Petzold, 1998",IDC_STATIC,7,52,166,8END

第一行给出了对话方块的名称(这里为ABOUTBOX)。如同其他资源,您也可以使用数字作为对话方块的名称。名称後面是关键字DIALOG和DISCARDABLE以及四个数字。前两个数字是对话方块左上角的x、y座标,该座标在程式呼叫对话方块时,是相对於父视窗显示区域的。後两个数字是对话方块的宽度和高度。

这些座标和大小的单位都不是图素。它们实际上依据一种特殊的座标系统,该系统只用於对话方块模板。数字依据对话方块使用字体的大小而定(这里是8点的MS Sans Serif字体):x座标和宽度的单位是字元平均宽度的1/4;y座标和高度的单位是字元高度的1/8。因此,对这个对话方块来说,对话方块左上角距离主视窗显示区域的左边是5个字元,距离顶边是2-1/2个字元。对话方块本身宽40个字元,高10个字元。

这样的座标系使得程式写作者可以使用座标和大小来大致勾勒对话方块的尺寸和外观,而不管视讯显示器的解析度是多少。由於系统字体字元的高度大致为其宽度的两倍,所以,x轴和y轴的量度差不多相等。

模板中的STYLE叙述类似於CreateWindow呼叫中的style栏位。对於模态对话方块,通常使用WS_POPUP和DS_MODALFRAME,我们将在稍後介绍其他的选项。

在BEGIN和END叙述(或者是左右大括弧,手工设计对话方块模板时,您可能会使用)之间,定义出现在对话方块中的子视窗控制项。这个对话方块使用了三种型态的子视窗控制项,它们分别是DEFPUSHBUTTON(内定按键)、ICON(图示)和CTEXT(文字居中)。这些叙述的格式为:

control-type "text" id, xPos, yPos, xWidth, yHeight, iStyle

其中,後面的iStyle项是可选的,它使用Windows表头档案中定义的识别字来指定其他视窗样式。

DEFPUSHBUTTON、ICON和CTEXT等识别字只可以在对话方块中使用,它们是某种特定视窗类别和视窗样式的缩写。例如,CTEXT指示这个子视窗控制项类别是「静态的」,其样式为:

WS_CHILD | SS_CENTER | WS_VISIBLE | WS_GROUP

虽然前面没有出现过WS_GROUP识别字,但是在第九章的COLORS1程式中已经出现过WS_CHILD、SS_CENTER和WS_VISIBLE视窗样式,我们在建立静态子视窗文字控制项时已经用到了它们。

对於图示,文字栏位是程式的图示资源名称,它也在ABOUT1资源描述档中定义。对於按键,文字栏位是出现在按键里的文字,这个文字相同於在程式中建立子视窗控制项时呼叫CreateWindow所指定的第二个参数。

id栏位是子视窗在向其父视窗发送讯息(通常为WM_COMMMAND讯息)时用来标示它自身的值。这些子视窗控制项的父视窗就是对话方块本身,它将这些讯息发送给Windows的一个视窗讯息处理程式。不过,这个视窗讯息处理程式也将这些讯息发送给您在程式中给出的对话方块程序。ID值相同於我们在第九章建立子视窗时,在CreateWindow函式中使用的子视窗ID。由於文字和图示控制项不向父视窗回送讯息,所以这些值被设定为IDC_STATIC,它在RESOURCE.H中定义为-1。按键的ID值为IDOK,它在WINUSER.H中定义为1。

接下来的四个数字设定子视窗的位置(相对於对话方块显示区域的左上角)和大小,它们是以系统字体平均宽度的1/4和平均高度的1/8为单位来表示的。对於ICON叙述,宽度和高度将被忽略。

对话方块模板中的DEFPUSHBUTTON叙述,除了包含DEFPUSHBUTTON关键字所隐含的视窗样式,还包含视窗样式WS_GROUP。稍後讨论该程式的第二个版本ABOUT2时,还会详细说明WS_GROUP(以及相关的WS_TABSTOP样式)。

对话方块程序
 

您程式内的对话方块程序处理传送给对话方块的讯息。尽管看起来很像是视窗讯息处理程式,但是它并不是真实的视窗讯息处理程式。对话方块的视窗讯息处理程式在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(callback)函式。尽管我使用了hDlg作为对话方块视窗的代号,但是您也可以按照您自己的意思使用hwnd。首先,让我们来看一下这个函式与视窗讯息处理程式的区别:

读书人网 >windows

热点推荐