见招拆招《Windows程序设计》(十)
相关的例子:下载>>> 作者:Zoologist 于2008-8-17上传 

对话框

如果有很多输入超出了菜单可以处理的程度,那么我们可以使用对话框来取得输入信息。程序写作者可以通过在某选项后面加上省略号(…)来表示该菜单项将启动一个对话框。

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

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

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

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

模态对话框

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

建立「About」对话框

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

程序11-1 ABOUT1

        
ABOUT1.ASM
;MASMPlus 代码模板 - 普通的 Windows 程序代码

.386
.Model Flat, StdCall
Option Casemap :None

Include windows.inc
Include user32.inc
Include kernel32.inc
Include gdi32.inc

includelib gdi32.lib
IncludeLib user32.lib
IncludeLib kernel32.lib
include macro.asm
	
	WinMain PROTO :DWORD,:DWORD,:DWORD,:DWORD
	IDM_APP_ABOUT     equ        40001
.DATA
	szAppName	TCHAR	"About1",0
.DATA?
	hInstance	HINSTANCE	?
.CODE
START:
	invoke GetModuleHandle,NULL
	invoke WinMain,eax,NULL,NULL,SW_SHOWDEFAULT
	invoke ExitProcess,0
WinMain proc hInst:DWORD,hPrevInst:DWORD,szCmdLine:DWORD,iCmdShow:DWORD
	LOCAL wndclass   :WNDCLASSEX
	LOCAL msg  :MSG
	LOCAL hWnd :HWND

	mov wndclass.cbSize,sizeof WNDCLASSEX	
	mov wndclass.style,CS_HREDRAW or CS_VREDRAW	
	mov wndclass.lpfnWndProc,offset WndProc

	mov wndclass.cbClsExtra,0
	mov wndclass.cbWndExtra,0
	
	push hInst
	pop wndclass.hInstance
	
	invoke LoadIcon,hInst,addr szAppName
	mov wndclass.hIcon,eax	
	
	invoke LoadCursor,NULL,IDC_ARROW
	mov wndclass.hCursor,eax	
	
	invoke GetStockObject,WHITE_BRUSH
	mov wndclass.hbrBackground,EAX
	
	lea eax,szAppName
	mov wndclass.lpszMenuName,eax
	mov wndclass.lpszClassName,eax

	mov wndclass.hIconSm,0
	
	invoke RegisterClassEx, ADDR wndclass
	.if (eax==0)
	   invoke MessageBox,NULL,CTXT("This program requires Windows NT!"),szAppName, MB_ICONERROR
           ret
	.endif
        
   
	invoke CreateWindowEx,NULL,
			ADDR szAppName, 					;window class name
			CTXT("No-Popup Nested Menu Demonstration"), 
			WS_OVERLAPPEDWINDOW,					;window style
			CW_USEDEFAULT,						;initial x position
			CW_USEDEFAULT,						;initial y position
			CW_USEDEFAULT, 						;initial x size
			CW_USEDEFAULT,						;initial y size
			NULL,							;parent window handle
			NULL,							;window menu handle
			hInst,							;program instance handle
			NULL										;creation parameters
	mov hWnd,eax


  	invoke ShowWindow,hWnd,iCmdShow
	invoke UpdateWindow,hWnd
	

	StartLoop:
		invoke GetMessage,ADDR msg,NULL,0,0
			cmp eax, 0
			je ExitLoop
				invoke TranslateMessage, ADDR msg
				invoke DispatchMessage,  ADDR msg
			jmp StartLoop
	ExitLoop:
	
	mov eax,msg.wParam
	ret
WinMain endp

AboutDlgProc	proc hDlg:HWND,message:UINT,wParam:WPARAM,lParam:LPARAM
	.if	message == WM_INITDIALOG
		mov	eax,TRUE
	.elseif	message == WM_COMMAND
		mov	eax,wParam
		and	eax,0FFFFh
		.if	(eax==IDOK)||(eax==IDCANCEL)
			invoke	EndDialog,hDlg, 0
			mov	eax,TRUE
		.endif	
	.else
		mov	eax,FALSE
	.endif	
  	ret
AboutDlgProc	endp

WndProc proc hwnd:DWORD,uMsg:DWORD,wParam :DWORD,lParam :DWORD
	.if	uMsg == WM_CREATE
		mov	esi,lParam
		mov	eax,[esi+4]
		mov	hInstance,eax

		xor	eax,eax
		ret			
	.elseif uMsg == WM_COMMAND
		mov	eax,wParam
		and	eax,0FFFFh
		.if	eax == IDM_APP_ABOUT
			invoke	DialogBoxParamA,hInstance, CTXT ("AboutBox"), hwnd,addr AboutDlgProc,0
		.endif
	        xor	eax,eax
	        ret
	.elseif uMsg == WM_DESTROY
	        invoke 	PostQuitMessage,NULL
	        xor	eax,eax
	        ret
	.endif

	invoke DefWindowProc,hwnd,uMsg,wParam,lParam
	ret
WndProc endp
END START

ABOUT1.RC (摘录)

#include "resource.h"
#define DS_MODALFRAME					0x80L
#define IDM_APP_ABOUT                   40001

ABOUTBOX DIALOG DISCARDABLE  32, 32, 180, 102
STYLE DS_MODALFRAME | WS_POPUP
FONT 8, "MS Sans Serif"
BEGIN
    DEFPUSHBUTTON   "OK",IDOK,66,81,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,8
END



ABOUT1 MENU DISCARDABLE 
BEGIN
    POPUP "&Help"
    BEGIN
        MENUITEM "&About About1...",            IDM_APP_ABOUT
    END
END

ABOUT1                  ICON    DISCARDABLE     "About1.ico"

ABOUT1.ICO

wpeA.jpg (7734 字节)


 

 

藉由后面章节中介绍的方法,您还可以在程序中建立图标和菜单。图示和菜单的ID名均为「About1」。菜单有一个选项,它产生一条ID名为IDM_APP_ABOUT的WM_COMMAND消息。这使得程序显示的图11-1所示的对话框。


 

wpe9.jpg (22127 字节)

图11-1 程序ABOUT1的对话框

对话框及其模板

要把一个对话框添加到Visual C++ Developer Studio会有的应用程序上,可以先从Insert菜单中选择 Resource,然后选择Dialog Box。现在一个对话框出现在您的眼前,该对话框带有标题列、标题(Dialog)以及 OKCancel按钮。Controls工具列允许您在对话框中插入不同的控件。

Developer Studio将对话框的ID设为标准的IDD_DIALOG1。您可以在此名称上(或者在对话框本身)单击右键,然后从菜单中选择 Properties。在本程序中,将ID改为「AboutBox」(带有引号)。为了与我建立的对话框保持一致,请将 X PosY Pos字段改为32。这表示对话框相对于程序窗口显示区域左上角的显示位置待会会有关于对话框坐标的详细讨论)。

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

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

如果您要让程序的图标出现在对话框中,可以这样做:先在浮动的Controls工具列中按下「 Pictures」按钮。将鼠标移动到对话框的表面,按下左键,然后拉出一个矩形。这就是图标将出现的位置。然后在次矩形上按下鼠标右键,从菜单中选择 Properties。保持IDIDC_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, 100
        
STYLE DS_MODALFRAME | WS_POPUP
        
FONT 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,8
        
END
        

第一行给出了对话框的名称(这里为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。首先,让我们来看一下这个函数与窗口消息处理程序的区别:

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),  . . . ) ;
        

不同的主题

虽然Visual C++ Developer Studio中的对话框编辑器和其它资源编辑器,使我们几乎不用考虑资源描述的写作问题,但是学习一些资源描述的语法还是有用的。尤其对于对话框模板来说,知道了语法,您就可以近一步了解对话框的范围和限制。甚至当它不能满足您的需要时,您还可以自己建立一个对话框模板(就像本期后面的HEXCALC程序)。资源编译器和资源描述语法的文件位于/Platform SDK/Windows Programming Guidelines/Platform SDK Tools/Compiling/Using the Resource Compiler。

在Developer Studio的「Properties」对话框中指定了对话框的窗口样式,它翻译成对话框模板中的STYLE叙述。对于ABOUT1,我们使用模态对话框最常用的样式;

STYLE WS_POPUP | DS_MODALFRAME
        

然而,您也可以尝试其它样式。有些对话框有标题列,标题列用于指出对话框的用途,并允许使用者通过鼠标在显示屏上移动对话框。此样式为WS_CAPTION。如果您使用WS_CAPTION,那么DIALOG叙述中所指定的x坐标和y坐标是对话框显示区域的坐标,并相对于父窗口显示区域的左上角。标题列将在y坐标之上显示。

如果使用了标题列,那么您可以用CAPTION叙述将文字放入标题中。在对话框模板中,CAPTION叙述在STYLE叙述的后面:

CAPTION "Dialog Box Caption"
        

另外,在对话框程序处理WM_INITDIALOG消息处理期间,您还可以呼叫:

SetWindowText (hDlg, TEXT ("Dialog Box Caption")) ;
        

如果您使用WS_CAPTION样式,也可以添加一个WS_SYSMENU样式的系统菜单按钮。此样式允许使用者从系统菜单中选择 MoveClose

Properties对话框的Border清单方块中选择 Resizing(相同于样式WS_THICKFRAME),允许使用者缩放对话框,仅管此操作并不常用。如果您不介意更特殊一点的话,还可以着为此对话框样式添加最大化方块。

您甚至可以给对话框添加一个菜单。这时对话框模板将包括下面的叙述:

MENU menu-name
        

其参数不是菜单的名称,就是资源描述中的菜单号。模态对话框很少使用菜单。如果使用了菜单,那么您必须确保菜单和对话框控件中的所有ID都是唯一的;或者不是唯一的,却表达了相同的命令。

FONT叙述使您可以设定非系统字体,以供对话框文字使用。这在过去的对话框中不常用,但现在却非常普遍。事实上,在内定情况下,Developer Studio为您建立的每一个对话框都选用8点的MS Sans Serif字体。一个Windows程序能把自己外观打点得非常与众不同,这只需为程序的对话框及其它文字输出单独准备一种字体即可。

尽管对话框窗口消息处理程序通常位于Windows内部,但是您也可以使用自己编写的窗口消息处理程序来处理对话框消息。要这样做,您必须在对话框模板中指定一个窗口类别名:

CLASS "class-name"
        

这种用法很少见,但是在本章后面所示的HEXCALC程序中我们将用到它。

当您使用对话框模板的名称来呼叫DialogBox时,Windows通过呼叫普通的CreateWindow函数来完成建立弹出式窗口所需要完成的一切操作。Windows从对话框模板中取得窗口的坐标、大小、窗口样式、标题和菜单,从DialogBox的参数中获得执行实体句柄和父窗口句柄。它所需要的唯一其它信息是一个窗口类别(假设对话框模板不指定窗口类别的话)。Windows为对话框注册一个专用的窗口类别,这个窗口类别的窗口消息处理程序可以存取对话框程序地址(该地址是您在DialogBox呼叫中指定的),所以它可以使程序获得该弹出式窗口所接收的消息。当然,您可以通过自己建立弹出式窗口来建立和维护自己的对话框。不过,使用DialogBox则更简单。

也许您希望受益于Windows对话框管理器,但不希望(或者能够)在资源描述中定义对话框模板,也可能您希望程序在执行时可以动态地建立对话框。这时可以完成这种功能的函数是DialogBoxIndirect,此函数用数据结构来定义模板。

在ABOUT1.RC的对话框模板中,我们使用缩写CTEXT、ICON和DEFPUSHBUTTON来定义对话框所需要的三种型态的子窗口控件。您还可以使用其它型态,每种型态都隐含一个特定的预先定义窗口类别和一种窗口样式。下表显示了与一些控件型态相同的窗口类别和窗口样式:

表 11-1

控件型态

窗口类别

窗口样式

PUSHBUTTON 按钮 BS_PUSHBUTTON | WS_TABSTOP
DEFPUSHBUTTON 按钮 BS_DEFPUSHBUTTON | WS_TABSTOP
CHECKBOX 按钮 BS_CHECKBOX | WS_TABSTOP
RADIOBUTTON 按钮 BS_RADIOBUTTON | WS_TABSTOP
GROUPBOX 按钮 BS_GROUPBOX | WS_TABSTOP
LTEXT 静态文字 SS_LEFT | WS_GROUP
CTEXT 静态文字 SS_CENTER | WS_GROUP
RTEXT 静态文字 SS_RIGHT | WS_GROUP
ICON 静态图标 SS_ICON
EDITTEXT 编辑 ES_LEFT | WS_BORDER | WS_TABSTOP
SCROLLBAR 滚动条 SBS_HORZ
LISTBOX 清单方块 LBS_NOTIFY | WS_BORDER | WS_VSCROLL
COMBOBOX 下拉式清单方块 CBS_SIMPLE | WS_TABSTOP

资源编译器是唯一能够识别这些缩写的程序。除了表中所示的窗口样式外,每个控件还具有下面的样式:

WS_CHILD | WS_VISIBLE
        

对于这些控件型态,除了EDITTEXT、SCROLLBAR、LISTBOX和COMBOBOX之外,控件叙述的格式为:

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

对于EDITTEXT、SCROLLBAR、LISTBOX和COMBOBOX,其格式为:

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

其中没有文字字段。在这两种叙述中,iStyle参数都是选择性的。

在前面,我讨论过确定预先定义子窗口的宽度和高度的规则。您可能需要查看前面参考这些规则,这时请记住:对话框模板中指定大小的单位为平均字符宽度的1/4,及平均字符高度的1/8。

控件叙述的style字段是可选的。它允许您包含其它窗口样式标识符。例如,如果您想建立在正方形框左边包含文字的复选框,那么可以使用:

CHECKBOX "text", id, xPos, yPos, xWidth, yHeight, BS_LEFTTEXT
        

注意,控件型态EDITTEXT会自动添加一个边框。如果您想建立一个没有边框的子窗口编辑控件,您可以使用:

EDITTEXT id, xPos, yPos, xWidth, yHeight, NOT WS_BORDER
        

资源编译器也承认与下面叙述类似的专用控件叙述:

CONTROL "text", id, "class", iStyle, xPos, yPos, xWidth, yHeight
        

此叙述允许您通过指定窗口类别和完整的窗口样式,来建立任意型态的子窗口控件。例如,要取代:

PUSHBUTTON "OK", IDOK, 10, 20, 32, 14
        

您可以使用:

CONTROL  "OK", IDOK, "button", WS_CHILD | WS_VISIBLE |
        
                  BS_PUSHBUTTON | WS_TABSTOP, 10, 20, 32, 14
        

当编译资源描述档时,这两条叙述在.RES和.EXE文件中的编码是相同的。在Developer Studio中,您可以使用Controls工具列中的Custom Control选项来建立此叙述。在ABOUT3程序中,我向您展示了如何用此选项建立一个控件,且在您的程序中已定义了该控件的窗口类别。

当您在对话框模板中使用CONTROL叙述时,不必包含WS_CHILD和WS_VISIBLE样式。在建立子窗口时,Windows已经包含了这些窗口样式。CONTROL叙述的格式也说明Windows对话框管理器在建立对话框时就完成了此项操作。首先,就像我前面所讨论的,它建立一个弹出式窗口,其父窗口句柄在DialogBox函数中提供。然后,对话框管理器为对话框模板中的每个控件建立一个子窗口。所有这些控件的父窗口均是这个弹出式对话框。上面给出的CONTROL叙述被转换成一个CreateWindow呼叫,形式如下所示:

hCtrl       =CreateWindow (TEXT ("button"), TEXT ("OK"),
        
                                         WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
        
                                                        10 * cxChar / 4, 20 * cyChar / 8,
        
                                                        32 * cxChar / 4, 14 * cyChar / 8,
        
                                                         hDlg, IDOK, hInstance, NULL) ;
        

其中,cxChar和cyChar是系统字体字符的宽度和高度,以图素为单位。hDlg参数是从建立该对话框窗口的CreateWindow呼叫传回的值;hInstance参数是从DialogBox呼叫获得的。

更复杂的对话框

ABOUT1中的简单对话框展示了设计和执行一个对话框的要点,现在让我们来看一个稍微复杂的例子。程序11-2给出的ABOUT2程序展示了如何在对话框程序中管理控件(这里用单选按钮)以及如何在对话框的显示区域中绘图。

程序11-2 ABOUT2

        
ABOUT2.ASM
;MASMPlus 代码模板 - 普通的 Windows 程序代码

.386
.Model Flat, StdCall
Option Casemap :None

Include windows.inc
Include user32.inc
Include kernel32.inc
Include gdi32.inc

includelib gdi32.lib
IncludeLib user32.lib
IncludeLib kernel32.lib
include macro.asm
	
	WinMain PROTO :DWORD,:DWORD,:DWORD,:DWORD
	PaintTheBlock	PROTO :HWND,:DWORD,:DWORD
	
	 IDC_BLACK        equ               1000
	 IDC_BLUE         equ               1001
	 IDC_GREEN        equ               1002
	 IDC_CYAN         equ               1003
	 IDC_RED          equ               1004
	 IDC_MAGENTA      equ               1005
	 IDC_YELLOW       equ               1006
	 IDC_WHITE        equ               1007
	 IDC_RECT         equ               1008
	 IDC_ELLIPSE      equ               1009
	 IDC_PAINT        equ               1010
	 IDM_APP_ABOUT    equ		    40001
.DATA
	szAppName	TCHAR	"About2",0
	iCurrentColor   DWORD	 IDC_BLACK
	iCurrentFigure  DWORD    IDC_RECT
	crColor		COLORREF 0,0FFh,0FF00h,0FFFFh,0FF0000h,0FF00FFh,0FFFF00h,0FFFFFFh
.DATA?
	hInstance	HINSTANCE	?
	hCtrlBlock	HWND	?
	iColor		DWORD	?
	iFigure	DWORD	?
.CODE
START:
	invoke GetModuleHandle,NULL
	invoke WinMain,eax,NULL,NULL,SW_SHOWDEFAULT
	invoke ExitProcess,0
WinMain proc hInst:DWORD,hPrevInst:DWORD,szCmdLine:DWORD,iCmdShow:DWORD
	LOCAL wndclass   :WNDCLASSEX
	LOCAL msg  :MSG
	LOCAL hWnd :HWND

	mov wndclass.cbSize,sizeof WNDCLASSEX	
	mov wndclass.style,CS_HREDRAW or CS_VREDRAW	
	mov wndclass.lpfnWndProc,offset WndProc

	mov wndclass.cbClsExtra,0
	mov wndclass.cbWndExtra,0
	
	push hInst
	pop wndclass.hInstance
	
	invoke LoadIcon,hInst,addr szAppName
	mov wndclass.hIcon,eax	
	
	invoke LoadCursor,NULL,IDC_ARROW
	mov wndclass.hCursor,eax	
	
	invoke GetStockObject,WHITE_BRUSH
	mov wndclass.hbrBackground,EAX
	
	lea eax,szAppName
	mov wndclass.lpszMenuName,eax
	mov wndclass.lpszClassName,eax

	mov wndclass.hIconSm,0
	
	invoke RegisterClassEx, ADDR wndclass
	.if (eax==0)
	   invoke MessageBox,NULL,CTXT("This program requires Windows NT!"),szAppName, MB_ICONERROR
           ret
	.endif
        
   
	invoke CreateWindowEx,NULL,
			ADDR szAppName, 					;window class name
			CTXT("No-Popup Nested Menu Demonstration"), 
			WS_OVERLAPPEDWINDOW,					;window style
			CW_USEDEFAULT,						;initial x position
			CW_USEDEFAULT,						;initial y position
			CW_USEDEFAULT, 						;initial x size
			CW_USEDEFAULT,						;initial y size
			NULL,							;parent window handle
			NULL,							;window menu handle
			hInst,							;program instance handle
			NULL										;creation parameters
	mov hWnd,eax

  	invoke ShowWindow,hWnd,iCmdShow
	invoke UpdateWindow,hWnd

	StartLoop:
		invoke GetMessage,ADDR msg,NULL,0,0
			cmp eax, 0
			je ExitLoop
				invoke TranslateMessage, ADDR msg
				invoke DispatchMessage,  ADDR msg
			jmp StartLoop
	ExitLoop:
	
	mov eax,msg.wParam
	ret
WinMain endp

AboutDlgProc	proc hDlg:HWND,message:UINT,wParam:WPARAM,lParam:LPARAM
	.if	message == WM_INITDIALOG
		mov	eax,iCurrentColor
		mov	iColor,eax
		
		mov	eax,iCurrentFigure
		mov	iFigure ,eax
		
                invoke	CheckRadioButton,hDlg, IDC_BLACK, IDC_WHITE,   iColor
		invoke	CheckRadioButton,hDlg, IDC_RECT,  IDC_ELLIPSE, iFigure
                invoke	GetDlgItem,hDlg, IDC_PAINT
                mov	hCtrlBlock,eax
                invoke	GetDlgItem ,hDlg, iColor
 		invoke	SetFocus,eax
 		mov	eax,FALSE
 		ret
	.elseif	message == WM_COMMAND
		mov	eax,wParam
		and	eax,0FFFFh
		.if	eax == IDOK
			mov	eax,iColor
			mov	iCurrentColor,eax
		
			mov	eax,iFigure
			mov	iCurrentFigure ,eax			
			invoke	EndDialog,hDlg, TRUE
			mov	eax,TRUE
			ret
			
		.elseif	eax==IDCANCEL
			invoke	EndDialog,hDlg,FALSE
			mov	eax,TRUE
			ret
		.elseif	(eax==IDC_BLACK)|| \
			(eax==IDC_RED)|| \
			(eax==IDC_GREEN)|| \
			(eax==IDC_YELLOW)|| \
			(eax==IDC_BLUE)|| \
			(eax==IDC_MAGENTA)|| \
			(eax==IDC_CYAN)|| \			
			(eax==IDC_WHITE)
			mov	eax,wParam
			and	eax,0FFFFh
			mov	iColor,eax
			invoke  CheckRadioButton,hDlg, IDC_BLACK, IDC_WHITE, eax
			invoke  PaintTheBlock,hCtrlBlock,iColor,iFigure
        		mov	eax,TRUE
        		ret
		.elseif	(eax==IDC_RECT)|| \
			(eax==IDC_ELLIPSE)
			mov	eax,wParam
			and	eax,0FFFFh
			mov	iFigure ,eax
			invoke	CheckRadioButton,hDlg, IDC_RECT, IDC_ELLIPSE, eax
			invoke	PaintTheBlock,hCtrlBlock,iColor,iFigure
			mov	eax,TRUE
			ret
		.endif	
	.elseif	message == WM_PAINT
		invoke	PaintTheBlock,hCtrlBlock,iColor,iFigure
	.endif	
	mov	eax,FALSE	
  	ret
AboutDlgProc	endp

PaintWindow	proc	hwnd:HWND,iCl:DWORD,iPt:DWORD
       LOCAL	hBrush:HBRUSH
       LOCAL	hdc:HDC
       LOCAL	rect:RECT
       
       invoke	GetDC,hwnd
       mov      hdc,eax
       invoke	GetClientRect,hwnd,addr rect
       ; invoke	MessageBox,hwnd,CTXT("XXX"),NULL,MB_APPLMODAL
        
       mov	eax,iCl
       sub	eax,IDC_BLACK
       shl	eax,2
       mov	eax,crColor[eax]
       invoke	CreateSolidBrush,eax
       mov	hBrush,eax

       invoke	SelectObject,hdc, hBrush
       mov	hBrush,eax
       
       .if (iFigure == IDC_RECT)
                invoke	Rectangle,hdc, rect.left, rect.top, rect.right, rect.bottom
       .else
		invoke	Ellipse,hdc, rect.left, rect.top, rect.right, rect.bottom
	.endif	
	invoke	SelectObject,hdc, hBrush
	invoke	DeleteObject,eax
	invoke	ReleaseDC,hwnd, hdc
        ret
PaintWindow	endp

PaintTheBlock	proc hCtrl:HWND,iCl:DWORD,iFg:DWORD
        invoke	InvalidateRect,hCtrl, NULL, TRUE
        invoke	UpdateWindow,hCtrl
        invoke	PaintWindow,hCtrl,iCl,iFg
        ret
PaintTheBlock	endp

WndProc proc hwnd:DWORD,uMsg:DWORD,wParam :DWORD,lParam :DWORD
	LOCAL	ps:PAINTSTRUCT
	
	.if	uMsg == WM_CREATE
		mov	esi,lParam
		mov	eax,[esi+4]
		mov	hInstance,eax

		xor	eax,eax
		ret			
	.elseif uMsg == WM_COMMAND
		mov	eax,wParam
		and	eax,0FFFFh
		.if	eax == IDM_APP_ABOUT
			invoke	DialogBoxParamA,hInstance, CTXT ("AboutBox"), hwnd,addr AboutDlgProc,0
			.if (eax !=0)
                        	invoke	InvalidateRect,hwnd, NULL, TRUE
                        .endif	
                        xor	eax,eax
                        ret
		.endif
	.elseif uMsg == WM_PAINT
		invoke	BeginPaint,hwnd,addr ps
		invoke	EndPaint,hwnd,addr ps
		invoke	PaintWindow,hwnd, iCurrentColor,iCurrentFigure
	        xor	eax,eax
	        ret	        
	.elseif uMsg == WM_DESTROY
	        invoke 	PostQuitMessage,NULL
	        xor	eax,eax
	        ret
	.endif

	invoke DefWindowProc,hwnd,uMsg,wParam,lParam
	ret
WndProc endp
END START

ABOUT2.RC


#include "resource.h"

#define IDC_BLACK 1000
#define IDC_BLUE 1001
#define IDC_GREEN 1002
#define IDC_CYAN 1003
#define IDC_RED 1004
#define IDC_MAGENTA 1005
#define IDC_YELLOW 1006
#define IDC_WHITE 1007
#define IDC_RECT 1008
#define IDC_ELLIPSE 1009
#define IDC_PAINT 1010
#define IDM_APP_ABOUT 40001

ABOUTBOX DIALOG DISCARDABLE 32, 32, 200, 234
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION
FONT 8, "MS Sans Serif"
BEGIN
ICON "ABOUT2",IDC_STATIC,7,7,20,20
CTEXT "About2",IDC_STATIC,57,12,86,8
CTEXT "About Box Demo Program",IDC_STATIC,7,40,186,8
LTEXT "",IDC_PAINT,114,67,72,72
GROUPBOX "&Color",IDC_STATIC,7,60,84,143
RADIOBUTTON "&Black",IDC_BLACK,16,76,64,8,WS_GROUP | WS_TABSTOP
RADIOBUTTON "B&lue",IDC_BLUE,16,92,64,8
RADIOBUTTON "&Green",IDC_GREEN,16,108,64,8
RADIOBUTTON "Cya&n",IDC_CYAN,16,124,64,8
RADIOBUTTON "&Red",IDC_RED,16,140,64,8
RADIOBUTTON "&Magenta",IDC_MAGENTA,16,156,64,8
RADIOBUTTON "&Yellow",IDC_YELLOW,16,172,64,8
RADIOBUTTON "&White",IDC_WHITE,16,188,64,8
GROUPBOX "&Figure",IDC_STATIC,109,156,84,46,WS_GROUP
RADIOBUTTON "Rec&tangle",IDC_RECT,116,172,65,8,WS_GROUP | WS_TABSTOP
RADIOBUTTON "&Ellipse",IDC_ELLIPSE,116,188,64,8
DEFPUSHBUTTON "OK",IDOK,35,212,50,14,WS_GROUP
PUSHBUTTON "Cancel",IDCANCEL,113,212,50,14,WS_GROUP
END



ABOUT2 ICON DISCARDABLE "About2.ico"


ABOUT2 MENU DISCARDABLE
BEGIN
POPUP "&Help"
BEGIN
MENUITEM "&About", IDM_APP_ABOUT
END
END

ABOUT2.ICO