77 子窗口控制概述
当子窗口的状态发生改变时,子窗口处理鼠标和键盘消息并通知父窗口。子窗口这时就变成了其父窗口高一级的输入设备。
可以建立自己的子窗口控制,也可以利用一些预定义的窗口类和窗口过程来建立标准的子窗口控制。
子窗口控制采用的形式有:按钮、复选框、编辑框、列表框、组合框、文本串、滚动条。
子窗口控制在对话框中最常用。
子窗口控制的位置和尺寸是在程序的资源描述文中的对话框模板中定义的。也可以使用预定义的、位于普通窗口客户区表面的子窗口控制。可以调用一次CreateWindow来建立一个子窗口,并通过调用MoveWindow来调整子窗口的位置和尺寸。父窗口过程向子窗口控制发送消息,子窗口控制向父窗口过程返回消息。
78 按钮类
按钮属于窗口。
按钮窗口风格都以字母BS开头,它代表“按钮风格”。按钮使用CreateWindow创建。
HWND hwndButton;
hwndButton = CreateWindow{
参数1 ClassName 类名,
参数2 Window text 按钮显示的文本,
参数3 Window Style 窗口风格 有WS_CHILD | WS_VISIBLE | 按钮种类
参数4 x位置
参数5 y位置 说明按钮左上角相对于父窗口客户区左上角的位置
参数6 Width 宽度 按钮宽度
参数7 Height 高度 按钮的高度
参数8 父窗口句柄
参数9 子窗口ID(强制HMENU类型)
参数10 实例句柄((LPCREATESTRUCT)lParam -> hInstance)
参数11 额外参数(一般为NULL)}
注:参数3中的按钮种类分为:
下压按钮:BS_PUSHBUTTON、BS_DEFPUSHBUTTON 下压按钮
复选框:BS_CHECKBOX、BS_AUTOCHECKBOX
三状态复选框:BS_3STATE、BS_AUTO3STATE
单选按钮:BS_RADIOBUTTON、BS_AUTORADIOBUTTON
组合框:BS_GROUPBOX
自定义按钮:BS_OWNEDRAW
参数9的子窗口ID对于每个子窗口都不同。在处理来自子窗口的WM_COMMAND消息时,ID帮助窗口过程识别出相应的子窗口。子窗口ID必须被强制转换为HMENU。
参数10的实例句柄利用了如下事实:在处理WM_CREATE消息的过程中,lParam实际上是指向CREATESTRUCT结构的指针,该结构有一个hInstance成员。
也可以使用GetWindowLong(hwnd, GWL_HINSTANCE)函数调用来获取实例句柄。
79 子窗口向父窗口发送消息
当用鼠标点击按钮时,子窗口控制就向其父窗口发送一个WM_COMMAND消息。父窗口过程捕获WM_COMMAND消息,其wParam和lParam消息参数含义如下:
LOWORD(wParam) 子窗口ID
HIWORD(wParam) 通知码
lParam 子窗口句柄
子窗口ID是在创建子窗口时传递给CreateWindow的值。
通知码详细表明了消息的含义。
当用鼠标单击按钮时,该按钮文本的周围会有虚线。这表明该按钮拥有了输入焦点,所有键盘输入都将传送给子窗口按钮控制。然后按钮即使拥有输入焦点,也只能处理空格键。
80 父窗口向子窗口发送消息
父窗口可以给子窗口发送消息,这些消息包括以前缀为WM开头的许多消息。另外,还有8个按钮说明消息,以BM开头。
BM_GETCHECK:获取复选框和单选按钮的选中标记
BM_SETCHECK:设置复选框和单选按钮的选中标记
BM_GETSTATE:获取按钮状态(正常还是按下)
BM_SETSTATE:设置按钮状态
BM_GETIMAGE
BM_SETIMAGE
BM_CLICK
BM_SETSTYLE:允许在按钮创建后改变按钮风格
每个子窗口控制都具有一个在其兄弟中唯一的窗口句柄和ID值,对于句柄和ID值这两者,知道其中一个就可以获得另一个。
id = GetWindowLong(hwndChild, GWL_ID);
hwndChild = GetDlgItem(hwndParent, id);
81 下压按钮
下压按钮控制主要用来触发一个立即响应的动作,而不保**何开关指示。有两种类型的按钮控制窗口风格。分别是BS_PUSHBUTTON和BS_DEFPUSHBUTTON。
当用来设计对话框时,两种风格作用不同,但当用作子窗口控制时,两种类型的按钮作用相同。
当按钮的高度为文本字符高度的7/4倍时,按钮的外观最好。
按钮的文本尺寸除了之前介绍的从TEXTMETRIC结构中获取之外,更简便的方法是通过GetDialogBaseUnits函数来获得默认字体字符的高度和宽度。
此函数返回一个32位的值,其中低字位表示宽度,高字位表示高度。
当鼠标在按钮中按下时,按钮使用三维阴影重画自己。
当鼠标放开时,就恢复按钮原因,并向父窗口发送一个WM_COMMAND消息和BN_CLICKED通知码。
82 复选框
复选框是一个文本框,文本通常出现在复选框的右边。复选框通常用于允许用户对选项进行选择的应用程序中。复选框的常用功能如同一个开关:单击一次显示复选标记,再次单击清除复选标记。
复选框最常用的2种风格是BS_CHECKBOX和BS_AUTOCHECKBOX。在使用BS_CHECKBOX时,需要自己向该控制发送BM_SETCHECK消息来设置复选标记。wParam参数置1时设置复选标记,置0时清除复选标记。通过向该控制发送BM_GETCHECK消息,可以得到该复选框的当前状态。
可以用如下指令来翻转复选标记:
SendMessage((HWND)lParam, BM_SETCHECK, (WPARAM)
!SendMessage((HWND)lParma, BM_GETCHECK, 0, 0), 0);
对于BS_AUTOCHECKBOX风格,按钮自己触发复选标记的开和关,窗口过程可以忽略WM_COMMAND消息。当需要知道按钮的当前状态时,可以向控制发送BM_GETCHECK消息:
iCheck = (int)SendMessage(hwndButton, BM_GETCHECK, 0, 0);
如果按钮被选中,则iCheck返回非0值。
其余两种复选框风格是BS_3STATE和BS_AUTO3STATE,这两种风格能显示第三种状态——复选框内是灰色的——它出现在向控制发送wParam = 2的WM_SETCHECK消息时。
83 单选按钮
单选按钮的形状是个圆圈。圆圈内的加重圆点表示该单选按钮已经被选中。单选按钮有窗口风格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);
84 分组框
分组框即风格为BS_GROUPBOX的选择框,它不处理鼠标输入和键盘输入,也不向其父窗口发送WM_COMMAND消息。分组框是一个矩形框,窗口文本在其顶部显示。分组框常用来包含其他的按钮控制。
85 更改按钮文本
可以通过调用SetWindowText来更改按钮内的文本:
SetWindowText(hwnd, pszString);
hwnd是窗口句柄,pszString是一个指向NULL终结串的指针。
对于一般的窗口,更改的是窗口的标题栏文本,对于按钮控制来说,更改的是按钮的显示文本。
可以获取窗口的当前文本:
iLength = GetWindowText(hwnd, pszBuffer, iMaxLength);
iMaxLength指定复制到pszBuffer指向的缓冲区中的最大字符数,该函数返回复制的字符数。
可以通过调用
iLength = GetWindowTextLength(hwnd);获取文本的长度。
86 可见和启用的按钮
为了接受鼠标和键盘输入,子窗口必须是可见的和被启用的。当窗口是可见的但是非启用时,窗口以灰色显示正文。
如果在建立子窗口时,没有将WS_VISIBLE包含在窗口类中,那么直到调用
ShowWindow(hwndChild, SW_SHOWNORMAL);
时子窗口才被显示出来。
调用ShowWindow(hwndChild, SW_HIDE);
将子窗口隐藏起来。
使用EnableWindow(hwndChild, TRUE)来启用窗口。
87 按钮和输入焦点
当Windows将输入焦点从一个窗口转换到另一个窗口时,首先给正在失去输入焦点的窗口发送一个WM_KILLFOCUS消息,wParam参数是接收输入焦点的窗口的句柄。然后,Windows向正在接收输入焦点的窗口发送一个WM_SETFOCUS消息,同时wParam参数是正在失去输入焦点的窗口的句柄。
可以通过调用SetFocus来恢复输入焦点,如:
case WM_KILLFOCUS:
if (hwnd == GetParent((HWND)wParam))
SetFoucs(hwnd);
return 0;
88 静态类
在CreateWindow函数中指定窗口类为“static”,就可以建立静态的子窗口控制。这些子窗口既不接收鼠标或键盘输入,也不向父窗口发送WM_COMMAND消息。
当在静态子窗口上移动或按下鼠标时,这个子窗口将捕获WM_NCHITTEST消息,并将HTTRANSPARENT的值返回给Windows,这将使Windows向其下层窗口发送相同的WM_NCHITTEST消息。
89 滚动条类
滚动条类是可以在父窗口的客户区的任何地方出现的子窗口。可以使用预先定义的窗口类“scrollbar”以及两个滚动条风格SBS_VERT和SBS_HORZ中的一个来建立子窗口滚动条控制。
与按钮控制不同,滚动条控制不向父窗口发送WM_COMMAND消息,而是像窗口滚动条一样发送WM_VSCROLL和WM_HSCROLL消息。在处理滚动条消息时,可以通过lParam参数来区分开窗口滚动条与滚动条控制。对于窗口滚动条,其值是0.对于滚动条控制,其值是滚动条窗口句柄。对于窗口滚动条和滚动条控制来说,wParam参数的高位字和低位字的含义相同。
窗口滚动条有固定的宽度,但滚动条控制可以自己修改尺寸。使用CreateWindow调用时,给出矩形尺寸来确定滚动条控制的尺寸。
如果想建立与窗口滚动条相同的滚动条控制,那么可以使用GetSystemMetrics获取水平滚动条的高度GetSystemMetrics(SM_CYHSCROLL)或者垂直滚动条的宽度GetSystemMetrics(SM_CXVSCROLL);
滚动条窗口风格标识符SBS_LEFTALIGN、SBS_RIGHTALIGN、SBS_TOPALIGN和SBS_BUTTOMALIGN给出滚动条的标准尺寸,但是这些风格只在对话框中对滚动条有效。
对于滚动条控制,可以使用如下调用来设置范围和位置:
SetScrollRange(hwndScroll, SB_CTL, iMin, iMax, bRedraw)
SetScrollPos(hwndScroll, SB_CTL,iPos, bRedraw)
SetScrollInfo(hwndScroll, SB_CTL, &si, bRedraw)
滚动条两端按钮之间较大的区域颜色是有COLOR_BTNFACE和COLOR_BTNHIGHLIGHT一起来确定的。
如果捕获了WM_CTLCOLORSCROLLBAR消息,那么可以在消息处理中返回画刷以取代该颜色。
90 窗口子类化
一般我们的消息都是传给Windows程序的WndProc窗口过程的,但是当窗口内有子窗口控制时,我们可以给这个子窗口设置一个新的窗口过程,这个技术叫做“窗口子类化”。它能让我们给现存的窗口过程(新的)设置“钩子”,以便在程序中处理一些消息,同时将其他所有消息传递给旧的窗口过程(WndProc)。
Win32的子类化的原理是靠拦截Windows系统中的某些消息来自己进行处理,而不是交给WndProc或DefWindowProc。
将GWL_WNDPROC标识符作为参数来调用GetWindowLong,可以得到这个窗口过程的地址。
可以调用SetWindowLong给子窗口设置一个新的窗口过程。
可以用函数指针的办法,将我们感兴趣的消息拦截下来,处理完之后再让预定义的窗口过程处理。这个过程大致如下:
WNDPROC OldProc;(用来保存旧的WndProc窗口过程)
OldProc = (WNDPROC)SetWindowsLong(hWnd, GWL_WNDPROC, (LONG)NewProc);
当然,这里的新窗口过程NewProc是预先由你实现好的。上述代码执行以后,系统在处理hwnd的窗口消息时,就会先进入你实现的NewProc回调过程,然后在处理过你感兴趣的消息之后,通过CallWindowProc函数和你预先保存的OldProc再次回到原来的回调过程中完成剩余的工作。
91 编辑类
当建立子窗口时,CreateWindow第一个参数,即类名使用“edit”,根据CreateWindow调用中的x位置、y位置、宽度、高度等这些参数定义了一个矩形。此矩形含有可编辑文本。当子窗口控制拥有输入焦点时,可以输入文本,移动光标,使用鼠标或者Shift键与一个光标键来选取部分文本,也可以删除、剪切、复制、粘帖文本。
92 编辑类风格
① 是WS_CHILD风格。
② 是编辑控制中的文本对齐方式,可以左对齐ES_LEFT、右对齐ES_RIGHT、居中ES_CENTER。
③ 编辑控制是单行文本还是多行文本,默认是单行,如果要处理回车键,需要增加风格ES_MULTILINE。
④ 滚动条功能,纵向是ES_AUTOVSCROLL,横向是ES_AUTOHSCROLL。
⑤ 边框,默认是没边框的,可以使用风格WS_BORDER。
93 编辑控制通知
编辑控制给父窗口过程发生WM_COMMAND消息,wParam和lParam参数和按钮控制一样。
LOWORD(wParam) 子窗口ID
HIWORD(wParam) 通知码(EN开头)
lParam 子窗口句柄
通知码如下所示:
EN_SETFOCUS 获得输入焦点
EN_KILLFOCUS 失去输入焦点
EN_CHANGE内容将改变
EN_UPDATE 内容已经改变
EN_ERRSPACE 输入的文本超过30000个字符
EN_MAXTEXT 插入之后的文本超过30000个字符
EN_HSCROLL 编辑控制的水平滚动条被单击
EN_VSCROLL 编辑控制的垂直滚动条被单击
94 发送给编辑控制的消息
发送给编辑控制的消息运行剪切、复制、清除当前的选择。用户使用鼠标或Shift键减少光标控制键选择文本并进行上面的操作。
剪切:SendMessage(hwndEdit, WM_CUT, 0, 0);
复制:SendMessage(hwndEdit, WM_COPY, 0, 0);
清除;SendMessage(hwndEdit, WM_CLEAR, 0, 0);
粘帖:SendMessage(hwndEdit, WM_PASTE, 0, 0);
获取当前选中文本的起始位置和末尾位置:
SendMessage(hwndEdit, EM_GETSEL, (WPARAM)&iStart, (LPARAM)&iEnd);
末尾位置实际是最后一个选择字符的位置加1。
选择文本:
SendMessage(hwndEdit, EM_SETSEL, iStart, iEnd);
文本置换:
SendMessage(hwndEdit, EM_REPLACESEL, 0, (LPARAM)szString);
获取多行文本的行数:
iCount = SendMessage(hwndEdit, EM_GETLINECOUNT, 0, 0);
对任何特定的行,可以获取距离编辑缓冲区文本开头的偏移量:
iOffset = SendMessage (hwndEdit, EM_LINEINDEX, iLine, 0);
其中,行数从0开始计算,iLine值为-1时返回包含光标所在行的偏移量。
获取行的长度:
iLength = SendMessage (hwndEdit, EM_LINELENGTH, iLine, 0);
将一行复制到一个缓冲区:
iLength = SendMessage (hwndEdit, EM_GETLINE, iLine, (LPARAM) szBuffer) ;
95 列表框类
列表框也属于子窗口控制。列表框是文本串的集合,这些文本串是一个矩形中可以滚动显示的列状列表。程序通过向列表框窗口过程发送消息,可以在列表中增加或者删除串。当列表框中的某项被选定时,列表框控制就向其父窗口发送WM_COMMAND消息,父窗口也就可以确定选定是哪一项。
列表框可以是单选的,也可以是多选的。选定的项被加亮显示,并且是反显的。
在单项选择的列表框中,用户按空格键就可以选定光标所在位置的项。方向键移动光标和当前选择指示,并且能够滚动列表框的内容。
96 列表框风格
当使用CreateWindow建立列表框子窗口时,应该将“listbox”作为窗口类,将WS_CHILD作为窗口风格。但是,这个默认列表框风格不向其父窗口发送WM_COMMAND消息。所以,一般都要包括列表框风格标识符LBS_NOTIFY。它允许父窗口接收来自列表框的WM_COMMAND消息。如果希望列表框对其中各项进行排序,那么可以使用另一个风格LBS_SORT。
如果想建立一个多选选择的列表框,那么可以使用风格LBS_MULTIPLESEL。
默认的列表框是无边界的,所以一般都要加上WS_BORDER来加上边界。
使用WS_VSCROLL来增加垂直滚动条。
有一个列表框风格,综合了上述各种风格,那就是LBS_STANDARD风格。
97 将文本串放入列表框
将文本串放入列表框可以通过调用SendMessage给列表框窗口过程发消息来实现这一点。文本串通常通过以0开始计数的下标数来引用,其中0对应于最顶上的项。
一般子窗口列表框控制的句柄定义为hwndList
下标值定义为iIndex
在使用SendMessage传递文本串的情况下,lParam参数是指向null结尾串的指针。
当窗口过程存储的列表框内容超过了可用内存空间时,SendMessage将返回LB_ERRSPACE(定义为-2)。如果是其他原因出错,那么将返回LB_ERR(-1).
如果采用LBS_SORT风格,那么填充列表框最简单的方法是借助LB_ADDSTRING消息:SendMessage(hwndList, LB_ADDSTRING, 0, (LPARAM)szString);
如果没有采用LBS_SORT,那么可以使用LB_INSERTSTRING指定一个下标值,将字符串插入到列表框中:
SendMessage (hwndList, LB_INSERTSTRING, iIndex, (LPARAM) szString) ;
如果iIndex等于4,那么szString将变为下标值为4的串——从顶头开始算起的第5个串。下标值为-1时,将串增加在最后。
可以在指定下标值的同时使用LB_DELETESTRING参数,这就可以从列表框中删除串:
SendMessage (hwndList, LB_DELETESTRING, iIndex, 0);
可以使用LB_RESETCONTENT清除列表框中的所有内容:
SendMessage (hwndList, LB_RESETCONTENT, 0, 0);
98 选择和获取项
获取列表框项数:(LB_GETCOUNT)
iCount = SendMessage (hwndList, LB_GETCOUNT, 0, 0);
加亮选中项:(LB_SETCURSEL)
SendMessage (hwndList, LB_SETCURSEL, iIndex, 0);
将lParam设置为-1,取消所有选择。
根据项的第一个字母来选择:(LB_SELSECTSTRING)
iIndex = SendMessage (hwndList, LB_SELECTSTRING, iIndex, (LPARAM) szSearchString);
iIndex等于-1时,从头开始搜索。
当得到来自列表框的WM_COMMAND消息时,可以通过使用LB_GETCURSEL来确定当前选项的下标:(LB_GETCURSEL)
iIndex = SendMessage (hwndList, LB_GETCURSEL, 0, 0);
可以确定列表框中串的长度:(LB_GETTEXTLEN)
iLength = SendMessage (hwndList, LB_GETTEXTLEN, iIndex, 0);
可以将某项复制到文本缓冲区:(LB_GETTEXT)
iLength = SendMessage (hwndList, LB_GETTEXT, iIndex, (LPARAM) szBuffer);
99 接收来自列表框的消息
当用户用鼠标单击列表框时,列表框将接收输入焦点。
列表框控制向其父窗口发送WM_COMMAND消息,对按钮和编辑控制来说wParam和lParam参数的含义是相同的。
LOWORD(wParam) 子窗口ID
HIWORD(wParam) 通知码(LBN开头)
lParam 子窗口句柄
通知码及其值如下所示:
LBN_ERRSPACE -2 表示列表框已经超出运行空间
LBN_SELCHANGE 1 表明当前选择已经被改变
LBN_DBLCLK 2 表明某项已经被鼠标双击
LBN_SELCANCEL 3
LBN_SETFOCUS 4 列表框获得焦点
LBN_KILLFOCUS 5 列表框失去焦点
只有列表框窗口风格包括LBS_NOTIFY时,列表框控制才向父窗口发送LBN_SELCHANGE和LBN_DBLCLK码
100 文件列表
LB_DIR是功能最强的列表框消息,它用文件目录表填充列表框,并且可以选择将子目录和有效的磁盘驱动器也包括进来:
SendMessage(hwndList, LB_DIR, iAttr, (LPARAM)szFileSpec);
① 使用文件属性码:
iAttr |
值 |
属性 |
DDL_READWRITE |
0x0000 |
普通文件 |
DDL_READONLY |
0x0001 |
只读文件 |
DDL_HIDDEN |
0x0002 |
隐藏文件 |
DDL_SYSTEM |
0x0004 |
系统文件 |
DDL_DIRECTORY |
0x0010 |
子目录 |
DDL_ARCHIVE |
0x0020 |
有归档位集的文件 |
DDL_DRIVES |
0x4000 |
包括驱动器的盘符 |
DDL_EXCLUSIVE |
0x8000 |
互斥搜索 |
当LB_DIR消息的iAttr值为DDL_READWRITE时,列表框列出普通文件、只读文件和具有归档位集的文件。
当值为DDL_DIRECTORY时,列表框除列出上述文件之外,还列出子目录,目录位于方括号之内。
当值为DDL_DRIVES | DDL_DIRECTORY时,那么列表将扩展到包括所有有效的驱动器,驱动器字母显示在虚线之间。
当值为DDL_EXCLUSIVE | DDL_ARCHIVE时,即将iAttr的最高位置位可以列出带标志的文件,而不包括普通文件。
② 文件列表的排序
lParam参数是指向文件说明串如“*.*”的指针,这个文件说明串不影响列表框中的子目录。
用户也许希望给列有文件清单的列表框使用LBS_SORT消息,列表框首先列出符合文件说明的文件,再列出子目录名。列出的第一个子目录名将采用下面的格式:
[..]
这种两个点的子目录项允许用户向根目录返回一级。
最后,具体的子目录名采用下面的形式:
[SUBDIR]
后面是以下面的形式列出的有效磁盘驱动器
[-A-]