建立子窗口
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 ;
可以用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) ;
用此函数(与SetWindowLong一起)来维护注册窗口类别时保留的特殊区域的数据。在建立子窗口时,Windows保留了GWL_ID标识符存取的数据。您也可以使用:
id = GetDlgCtrlID (hwndChild) ;
虽然函数中的「Dlg」部分指的是对话框,但实际上这是一个通用的函数。
知道ID和父窗口句柄,您就能获得子窗口句柄:
hwndChild = GetDlgItem (hwndParent, id) ;