📄 9. 子窗口控件.txt
字号:
ReleaseDC (hwnd, hdc) ;
ValidateRect (hwnd, &rect) ;
break ;
case WM_DESTROY :
PostQuitMessage (0) ;
return 0 ;
}
return DefWindowProc (hwnd, message, wParam, lParam) ;
}
单击按钮时,按钮就给父窗口消息处理程序发送一个WM_COMMAND消息,也就是我们所熟悉的WndProc。BTNLOOK的WndProc将该消息的wParam参数和lParam参数显示在显示区域的右边,如图9-1所示。
具有BS_OWNERDRAW样式的按钮在窗口上显示为一个背景阴影,因为这种样式的按钮是由程序来负责绘制的。该按钮表示它需要由包含lParam消息参数的WM_DRAWITEM消息来绘制,而lParam消息参数是一个指向DRAWITEMSTRUCT型态结构的指针。在BTNLOOK中,这些消息也同样被显示。我将在本章的后面更详细地讨论这种拥有者绘制(owner draw)按钮。
图9-1 BTNLOOK的屏幕显示
建立子窗口
BTNLOOK定义了一个叫做button的结构,它包括了按钮窗口样式和描述性字符串,它们对应于10个按钮型态,所有按钮窗口样式都以字母「BS」开头,它表示「按钮样式」。10个按钮子窗口是在WndProc中处理WM_CREATE消息的过程中使用一个for循环建立的。CreateWindow呼叫使用下面这些参数:
Class name(类别名称)
Window text(窗口文字)
Window style(窗口样式)
x position(x位置)
y position(y位置)
Width(宽度)
Height(高度)
Parent window(父窗口)
Child window ID(子窗口ID)
Instance handle(执行实体句柄)
Extra parameters(附加参数)
TEXT ("button")
button[i].szText
WS_CHILD | WS_VISIBLE | button[i].iStyle
cxChar
cyChar * (1 + 2 * i)
20 * xChar
7 * yChar / 4
hwnd
(HMENU) i
((LPCREATESTRUCT) lParam) -> hInstance
NULL
类别名称参数是预先定义的名字。窗口样式使用WS_CHILD、WS_VISIBLE以及在button结构中定义的10个按钮样式之一(BS_PUSHBUTTON、BS_DEFPUSHBUTTON等等)。窗口文字参数(对于普通窗口来说,它是显示在标题列中的文字)将在每个按钮上显示出来。我简单地使用标识按钮样式文字的x位置和y位置参数,说明子窗口左上角相对于父窗口显示区域左上角的位置。宽度和高度参数规定了每个子窗口的宽度和高度。请注意,我用的是GetDialogBaseUnits函数来获得内定字体字符的宽度和高度。这是对话框用来获得文字尺寸的函数。此函数传回一个32位的值,其中低字组表示宽度,高字组表示高度。由于GetDialogBaseUnits传回的值与从GetTextMetrics获得的值大致上相同,但GetDialogBaseUnits有时使用起来会更方便些,而且能够与对话框控件更好地保持一致。
对每个子窗口,它的子窗口ID参数应该各不相同。在处理来自子窗口的WM_COMMAND消息时,ID帮助您的窗口消息处理程序识别出相应的子窗口。注意子窗口ID是作为CreateWindow的一个参数传递的,该参数通常用于指定程序的菜单,因此子窗口ID必须被强制转换为HMENU。
CreateWindow呼叫的执行实体句柄看起来有点奇怪,但是它利用了如下的事实,亦即在处理WM_CREATE消息的过程中,lParam实际上是指向CREATESTRUCT (「建立结构」)结构的指针,该结构有一个hInstance成员。所以将lParam转换成指向CREATESTRUCT结构的一个指针,并取出hInstance。
(有些Windows程序使用名为hInst的整体变量,使窗口消息处理程序能存取WinMain中的执行实体句柄。在WinMain中,您只需在建立主窗口之前设定:
hInst = hInstance ;
在第七章中的CHECKER3程序中,我们曾用GetWindowLong取得执行实体句柄:
GetWindowLong (hwnd, GWL_HINSTANCE)
这几种方法都是正确的。)
在呼叫CreateWindow之后,我们不必再为这些子窗口做任何事情,由Windows中的按钮窗口消息处理程序负责维护它们,并处理所有的重画工作(BS_OWNERDRAW样式的按钮例外,它要求程序绘制它,这些将在后面加以讨论)。在程序终止时,如果父窗口已经被清除,那么Windows将清除这些子窗口。
子窗口向父窗口发消息
当您执行BTNLOOK时,将看到在显示区域的左边会显示出不同的按钮型态。我在前面已经提到过,用鼠标单击按钮时,子窗口控件就向其父窗口发送一个WM_COMMAND消息。BTNLOOK拦截WM_COMMAND消息并显示wParam和lParam的值,它们的含义如下:
LOWORD (wParam)
HIWORD (wParam)
lParam
子窗口ID
通知码
子窗口句柄
如果您正在移植16位Windows程序,那么要注意改变这些消息参数以容纳32位的句柄。
子窗口ID是在建立子窗口时传递给CreateWindow的值。在BTNLOOK中,这些ID被显示在显示区域中,并使用0到9分别标识10个按钮。子窗口句柄是Windows从CreateWindow传回的值。
通知码更详细表示了消息的含义。按钮通知码的可能值在Windows表头文件中定义如下:
表9-1
按钮通知码标识符
值
BN_CLICKED
0
BN_PAINT
1
BN_HILITE or BN_PUSHED
2
BN_UNHILITE or BN_UNPUSHED
3
BN_DISABLE
4
BN_DOUBLECLICKED or BN_DBLCLK
5
BN_SETFOCUS
6
BN_KILLFOCUS
7
实际上,您不会看到这些按钮值中的大多数。从1到4的通知码是用于一种叫做BS_USERBUTTON的已不再使用的按钮的(它已经由BS_OWNERDRAW和另一种不同的通知方式所替换)。通知码6到7只有当按钮样式包括标识BS_NOTIFY才发送。通知码5只对BS_RADIOBUTTON、BS_AUTORADIOBUTTON和BS_OWNERDRAW按钮发送,或者当按钮样式中包括BS_NOTIFY时,也为其它按钮发送。
您会注意到,在用鼠标单击按钮时,该按钮文字的周围会有虚线。这表示该按钮拥有了输入焦点,所有键盘输入都将传送给子窗口按钮控件,而不是传送给主窗口。但是,当该按钮控件拥有输入焦点时,它将忽略所有的键盘输入,除了Spacebar键例外,此时Spacebar键与鼠标具有相同的效果。
父窗口向子窗口发送消息
虽然BTNLOOK中没有显示这一事实,但是父窗口消息处理程序也能向子窗口控件发送消息。这些消息包括以前缀WM开头的许多消息。另外,在WINUSER.H中还定义了8个按钮说明消息;前缀BM表示「按钮消息」。这些按钮消息如下表所示:
表9-2
按钮消息
值
BM_GETCHECK
0x00F0
BM_SETCHECK
0x00F1
BM_GETSTATE
0x00F2
BM_SETSTATE
0x00F3
BM_SETSTYLE
0x00F4
BM_CLICK
0x00F5
BM_GETIMAGE
0x00F6
BM_SETIMAGE
0x00F7
BM_GETCHECK和BM_SETCHECK消息由父窗口发送给子窗口控件,以取得或者设定复选框和单选按钮的选中标记。BM_GETSTATE和BM_SETSTATE消息表示按钮处于正常状态还是(鼠标或Spacebar键按下时的)「按下」状态。我们将在讨论按钮的每种型态时,看到这些消息是如何起作用的。BM_SETSTYLE消息允许您在按钮建立之后改变按钮样式。
每个子窗口控件都具有一个在其兄弟中唯一的窗口句柄和ID值。对于句柄和ID这两者,知道其中的一个您就可以获得另一个。如果您知道子窗口控件的窗口句柄,那么您可以用下面的叙述来获得ID:
id = GetWindowLong (hwndChild, GWL_ID) ;
第七章的CHECKER3程序曾用此函数(与SetWindowLong一起)来维护注册窗口类别时保留的特殊区域的数据。在建立子窗口时,Windows保留了GWL_ID标识符存取的数据。您也可以使用:
id = GetDlgCtrlID (hwndChild) ;
虽然函数中的「Dlg」部分指的是对话框,但实际上这是一个通用的函数。
知道ID和父窗口句柄,您就能获得子窗口句柄:
hwndChild = GetDlgItem (hwndParent, id) ;
按键
在BTNLOOK中显示的前两个按钮是「压入」按钮。按钮是一个矩形,包括了CreateWindow呼叫中窗口文字参数所指定的文字。该矩形占用了在CreateWindow或者MoveWindow呼叫中给出的全部高度和宽度,而文字在矩形的中心。
按键控件主要用来触发一个立即响应的动作,而不保留任何形式的开/关指示。两种型态的按钮控件有两种窗口样式,分别叫做BS_PUSHBUTTON和BS_DEFPUSHBUTTON,BS_DEFPUSHBUTTON中的「DEF」代表「内定」。当用来设计对话框时,BS_PUSHBUTTON控件和BS_DEFPUSHBUTTON控件的作用不同。但是当用作子窗口控件时,两种型态的按钮作用相同,尽管BS_DEFPUSHBUTTON的边框要粗一些。
当按钮的高度为文字字符高度的7/4倍时,按钮的外观看起来最好,其中文字字符由BTNLOOK使用;而按钮的宽度至少调节到文字的宽度再加上两个字符的宽度。
当鼠标光标在按钮中时,按下鼠标按键将使按钮用三维阴影重画自己,就好像真的被按下一样。放开鼠标按键时,就恢复按钮的原貌,并向父窗口发送一个WM_COMMAND消息和BN_CLICKED通知码。与其它按钮型态相似,当按钮拥有输入焦点时,在文字的周围就有虚线,按下及释放Spacebar键与按下及释放鼠标按键具有相同的效果。
您可以通过给窗口发送BM_SETSTATE消息来仿真按钮闪动。以下的操作将导致按钮被按下:
SendMessage (hwndButton, BM_SETSTATE, 1, 0) ;
下面的呼叫使按钮恢复正常:
SendMessage (hwndButton, BM_SETSTATE, 0, 0) ;
hwndButton窗口句柄是从CreateWindow呼叫传回的值。
您也可以向按键发送BM_GETSTATE消息,子窗口控件传回按钮目前的状态:如果按钮被按下,则传回TRUE;如果按钮处于正常状态,则传回FALSE。但是,绝大多数应用并不需要这一消息。因为按钮不保留任何开/关信息,所以BM_SETCHECK消息和BM_GETCHECK消息不会被用到。
复选框
复选框是一个文字方块,文字通常出现在复选框的右边(如果您在建立按钮时指定了BS_LEFTTEXT样式,那么文字会出现在左边;您也许将用BS_RIGHT直接调整文字来组合此样式)。复选框通常用于允许使用者对选项进行选择的应用程序中。复选框的常用功能如同一个开关:单击框一次将显示勾选标记,再次单击清除勾选标记。
复选框最常用的两种样式是BS_CHECKBOX和BS_AUTOCHECKBOX。在使用BS_CHECKBOX时,您需要自己向该控件发送BM_SETCHECK消息来设定勾选标记。wParam参数设1时设定勾选标记,设0时清除勾选标记。通过向该控件发送BM_GETCHECK消息,您可以得到该复选框的目前状态。在处理来自控件的WM_COMMAND消息时,您可以用如下的指令来翻转X标记:
SendMessage ((HWND) lParam, BM_SETCHECK, (WPARAM)
!SendMessage ((HWND) lParam, BM_GETCHECK, 0, 0), 0) ;
注意第二个SendMessage呼叫前面的运算子「!」,其中lParam是在WM_COMMAND消息中传给使用者窗口消息处理程序的子窗口句柄。如果您以后又想知道按钮的状态,那么可以向它发送另一条BM_GETCHECK消息;您也可以将目前状态储存在您的窗口消息处理程序中的一个静态变量里,或者向它发送BM_SETCHECK消息来初始化带勾选标记的BS_CHECKBOX复选框:
SendMessage (hwndButton, BM_SETCHECK, 1, 0) ;
对BS_AUTOCHECKBOX样式,按钮自己触发勾选标记的开和关,所以您的窗口消息处理程序可以忽略WM_COMMAND消息。当您需要按钮目前的状态时,可以向控件发送BM_GETCHECK消息:
iCheck = (int) SendMessage (hwndButton, BM_GETCHECK, 0, 0) ;
如果该按钮被选中,则iCheck的值为TRUE或者非零数;如果按钮末被选中,则iCheck的值为FALSE或0。
其余两种复选框样式是BS_3STATE和BS_AUTO3STATE,正如它们名字所暗示的,这两种样式能显示第三种状态-复选框内是灰色-它出现在向控件发送wParam等于2的WM_SETCHECK消息时。灰色是向使用者表示此框不能被选本章的或者禁止使用。
复选框沿矩形的左边框对齐,并集中在呼叫CreateWindow时规定的矩形的顶边和底边之间,在该矩形内的任何地方按下鼠标都会向其父窗口发送一个WM_COMMAND消息。复选框的最小高度是一个字符的高度,最小宽度是文字中的字符数加2。
单选按钮
单选按钮的名称在一列按钮的后面,这些按钮就像汽车上的收音机一样。汽车收音机上的每一个按钮都对应一种收音状态,而且一次只能有一个按钮被按下。在对话框中,单选按钮组常常用来表示相互排斥的选项。与复选框不同,单选按钮的工作与开关不一样,也就是说,当第二次按单选按钮时,它的状态会保持不变。
单选按钮的形状是一个圆圈,而不是方框,除此之外,它非常像复选框。圆圈内的加重圆点表示该单选按钮已经被选中。单选按钮有窗口样式BS_RADIOBUTTON或BS_AUTORADIOBUTTON两种,但是后者只用于对话框。
当您收到来自单选按钮的WM_COMMAND消息时,应该向它发送wParam等于1的BM_SETCHECK消息来显示其选中状态:
SendMessage (hwndButton, BM_SETCHECK, 1, 0) ;
对同组中的其它所有单选按钮,您可以通过向它们发送wParam等于0的BM_SETCHECK消息来显示其未选中状态:
SendMessage (hwndButton, BM_SETCHECK, 0, 0) ;
分组方块
分组方块即样式为BS_GROUPBOX的选择框,它是按钮类中的特例,既不处理鼠标输入和键盘输入,也不向其父窗口发送WM_COMMAND消息。分组方块是一个矩形框,分组方块标题在其顶部显示。分组方块常用来包含其它的按钮控件。
改变按钮文字
您可以通过SetWindowText来改变按钮(或者其它任何窗口)内的文字:
SetWindowText (hwnd, pszString) ;
其中hwnd是欲改变窗口的句柄,pszString是一个指向以null为终结的字符串指针。对于一般的窗口来说,这个文字是标题列的文字;对于按钮控件来说,它是随着该按钮显示的文字。
您也可以取得窗口目前的文字:
iLength = GetWindowText (hwnd, pszBuffer, iMaxLength) ;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -