📄 见招拆招《windows程序设计》(十) .txt
字号:
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。首先,让我们来看一下这个函数与窗口消息处理程序的区别:
窗口消息处理程序传回一个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), . . . ) ;
不同的主题
虽然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样式的系统菜单按钮。此样式允许使用者从系统菜单中选择 Move或Close。
从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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -