📄 9. 子窗口控件.txt
字号:
iMaxLength指定复制到pszBuffer指向的缓冲区中的最大字符数。该函数传回复制的字符数。您可以首先通过下面的呼叫来获得特定文字的长度:
iLength = GetWindowTextLength (hwnd) ;
可见的和启用的按钮
为了接收鼠标和键盘输入,子窗口必须是可见的(被显示)和被启用的。当窗口是可见的而未被启用时,那么窗口将以灰色而非黑色显示文字。
如果在建立子窗口时,您没有将WS_VISIBLE包含在窗口类别中,那么直到呼叫ShowWindow时子窗口才会被显示出来:
ShowWindow (hwndChild, SW_SHOWNORMAL) ;
如果您将WS_VISIBLE包含在窗口类别中,就没有必要呼叫ShowWindow。但是,您可以通过呼叫ShowWindow将子窗口隐藏起来:
ShowWindow (hwndChild, SW_HIDE) ;
您可以通过下面的呼叫来确定子窗口是否可见:
IsWindowVisible (hwndChild) ;
您也可以使子窗口被启用或者不被启用。在内定情况下,窗口是被启用的。您可以通过下面的呼叫使窗口不被启用:
EnableWindow (hwndChild, FALSE) ;
对于按钮控件,这具有使按钮字符串变成灰色的作用。按钮将不再对鼠标输入和键盘输入做出响应,这是表示按钮选项目前不可用的最好方法。
您可以通过下面的呼叫使子窗口再次被启用:
EnableWindow (hwndChild, TRUE) ;
您还可以使用下面的呼叫来确定子窗口是否被启用:
IsWindowEnabled (hwndChild) ;
按钮和输入焦点
我在本章前面已经提到过,当用鼠标单击按钮、复选框、单选框和拥有者绘制按钮时,它们接收到输入焦点。这些控件使用文字周围的虚线来表示它拥有了输入焦点。当子窗口控件得到输入焦点时,其父窗口就失去了输入焦点;所有的键盘输入都进入子窗口控件,而不会进入父窗口中。但是,子窗口控件只对Spacebar键作出回应,此时Spacebar键的作用就如同鼠标按键一样。这种情形导致了一个明显的问题:您的程序失去了对键盘处理的控件。让我们看看我们对此能做一些什么。
我在第六章中已经提到过,当Windows将输入焦点从一个窗口(例如一个父窗口)转换到另一个窗口(例如一个子窗口控件)时,它首先给正在失去输入焦点的窗口发送一个WM_KILLFOCUS消息,wParam参数是接收输入焦点的窗口的句柄。然后,Windows向正在接收输入焦点的窗口发送一个WM_SETFOCUS消息,同时wParam是还在失去输入焦点的窗口的句柄(在这两种情况中,wParam值可能为NULL,它表示没有窗口拥有或者正在接收输入焦点)。
通过处理WM_KILLFOCUS消息,父窗口可以阻止子窗口控件获得输入焦点。假定数组hwndChild包含了所有子窗口的窗口句柄(它们是在呼叫CreateWindow来建立窗口的时候储存到数组中的)。 NUM是子窗口的数目:
case WM_KILLFOCUS :
for ( i = 0 ; i < NUM ; i++)
if (hwndChild [i] == (HWND) wParam)
{
SetFocus (hwnd) ;
break ;
}
return 0 ;
在这段程序代码中,当父窗口获知它正在失去输入焦点,而让它的某个子窗口得到输入焦点时,它将呼叫SetFocus来重新取得输入焦点。
下面是可达到相同目的、但更为简单(但不太直观)的方法:
case WM_KILLFOCUS :
if (hwnd == GetParent ((HWND) wParam))
SetFocus (hwnd) ;
return 0 ;
但是,这两种方法都有缺点:它们阻止按钮对Spacebar键作出响应,因为该按钮总是得不到输入焦点。一个更好的方法是使按钮得到输入焦点,也能让使用者用Tab键从一个按钮转移到另一个按钮。这听起来似乎不太可能,在本章的后面,我们将要说明在COLORS1程序中如何用「窗口子类别化」技术来实作这种方法。
控件与颜色
您可以在图9-1中看到,许多按钮的显示看起来并不正确。按键还好,但是其它按钮却带有一个本不应该在那里的一个矩形灰色背景。这是因为这些按钮本来是为对话框中的显示而设计的,而在Windows 98中,对话框有一个灰色的表面。我们的窗口有一个白色的表面,这是因为我们在WNDCLASS结构中就是这样定义的。
wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
我们已经这么做了,因为我们经常在显示区域中显示文字,而GDI使用在内定设备内容中定义的文字颜色和背景颜色,它们总是黑色和白色。为了使这些按钮更加美观一些,我们必须要改变显示区域的颜色使之和按钮的背景颜色一致,所以要以某种方法将按钮的背景颜色改为白色。
解决此问题的第一步,是理解Windows对「系统颜色」的使用。
系统颜色
Windows保留了29种系统颜色以供各种显示使用。您可以使用GetSysColor和SetSysColors来获得和设定这些颜色。在Windows表头文件中定义的标识符规定了系统颜色。使用SetSysColors设定的系统颜色只在目前Windows对话过程中有效。
借助Windows「控制台」程序的「显示器」部分,您可以改变一些(但不是全部)系统颜色。若是Microsoft Windows NT,选中的颜色会储存在系统登录中;若是Microsoft Windows 98,则储存在WIN.INI文件中。系统登录和WIN.INI文件都为这29种系统颜色使用了关键词(与GetSysColor和SetSysColors的标识符不同),在系统颜色的后面跟着红、绿、蓝三种颜色的值,该值的变化范围是0到255。下表说明了这29种系统颜色是如何在GetSysColor、SetSysColors以及WIN.INI关键词中用常数来标识的。这张表是按照COLOR_ 常数值(从0开始到28结束)顺序排列的:
表9-3
GetSysColor和SetSysColors
系统登录键或WIN.INI标识符
内定的RGB值
COLOR_SCROLLBAR
Scrollbar
C0-C0-C0
COLOR_BACKGROUND
Background
00-80-80
COLOR_ACTIVECAPTION
ActiveTitle
00-00-80
COLOR_INACTIVECAPTION
InactiveTitle
80-80-80
COLOR_MENU
Menu
C0-C0-C0
COLOR_WINDOW
Window
FF-FF-FF
COLOR_WINDOWFRAME
WindowFrame
00-00-00
COLOR_MENUTEXT
MenuText
C0-C0-C0
COLOR_WINDOWTEXT
WindowText
00-00-00
COLOR_CAPTIONTEXT
TitleText
FF-FF-FF
COLOR_ACTIVEBORDER
ActiveBorder
C0-C0-C0
COLOR_INACTIVEBORDER
InactiveBorder
C0-C0-C0
COLOR_APPWORKSPACE
AppWorkspace
80-80-80
COLOR_HIGHLIGHT
Highlight
00-00-80
COLOR_HIGHLIGHTTEXT
HighlightText
FF-FF-FF
COLOR_BTNFACE
ButtonFace
C0-C0-C0
COLOR_BTNSHADOW
ButtonShadow
80-80-80
COLOR_GRAYTEXT
GrayText
80-80-80
COLOR_BTNTEXT
ButtonText
00-00-00
COLOR_INACTIVECAPTIONTEXT
InactiveTitleText
C0-C0-C0
COLOR_BTNHIGHLIGHT
ButtonHighlight
FF-FF-FF
COLOR_3DDKSHADOW
ButtonDkShadow
00-00-00
COLOR_3DLIGHT
ButtonLight
C0-C0-C0
COLOR_INFOTEXT
InfoText
00-00-00
COLOR_INFOBK
InfoWindow
FF-FF-FF
[no identifier; use value 25]
ButtonAlternateFace
B8-B4-B8
COLOR_HOTLIGHT
HotTrackingColor
00-00-FF
COLOR_GRADIENTACTIVECAPTION
GradientActiveTitle
00-00-80
COLOR_GRADIENTINACTIVECAPTION
GradientInactiveTitle
80-80-80
这29种颜色的默认值是由显示驱动程序提供的,在不同的机器上可能略有不同。
坏消息:虽然这些颜色中有许多似乎都可以从颜色常数名称上了解其代表意义(例如,COLOR_BACKGROUND是所有窗口后面的桌面区域颜色),在最近版本的Windows中系统颜色的使用变得非常混乱。以前,Windows在视觉上要比今天简单得多。实际上,在Windows 3.0以前,只定义了前13种系统颜色。但随着使用看起来越来越难以控制的立体外观,相对应地也需要更多的系统颜色。
按钮颜色
对需要多种颜色的每一个按钮来说,这个问题更加地明显。COLOR_BTNFACE被用于按键主要的表面颜色,以及其它按钮主要的背景颜色(这也是用于对话框和消息框的系统颜色)。COLOR_BTNSHADOW被建议用作按键右下边、以及复选框内部和单选按钮圆点的阴影。对于按键,COLOR_BTNTEXT被用作文字颜色;而对于其它的按钮,则使用COLOR_WINDOWTEXT作为文字颜色。还有其它几种系统颜色用于按钮设计的各个部分。
因此,如果您想在我们的显示区域表面显示按钮,那么一种避免颜色冲突的方法便是屈服于这些系统颜色。首先,在定义窗口类别时使用COLOR_BTNFACE作为您显示区域的背景颜色:
wndclass.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1) ;
您可以在BTNLOOK程序中尝试这种方法。当WNDCLASS结构中的hbrBackground值是这个值时,Windows会明白这实际上指的是一种系统颜色而非一个实际的句柄。Windows要求当您在WNDCLASS结构的hbrBackground栏中指定这些标识符时加上1,这样做的目的是防止其值为NULL,而没有任何其它目的。如果您的在程序执行过程中,系统颜色恰好发生了变化,那么显示区域将变得无效,而Windows将使用新的COLOR_BTNFACE值。但是现在我们又引发了另一个问题。当您使用TextOut显示文字时,Windows使用的是在设备内容中为背景颜色(它擦除文字后的背景)和文字颜色定义的值,其默认值为白色(背景)和黑色(文字),而不管系统颜色和窗口类别结构中的hbrBackground字段为何值。所以,您需要使用SetTextColor和SetBkColor将文字和文字背景的颜色改变为系统颜色。您可以在获得设备内容句柄之后这么做:
SetBkColor (hdc, GetSysColor (COLOR_BTNFACE)) ;
SetTextColor (hdc, GetSysColor (COLOR_WINDOWTEXT)) ;
这样,显示区域背景、文字背景和文字的颜色都与按钮的颜色一致了。但是,如果当您的程序执行时,使用者改变了系统颜色,您可能要改变文字背景颜色和文字颜色。这时您可以使用下面的程序代码:
case WM_SYSCOLORCHANGE:
InvalidateRect (hwnd, NULL, TRUE) ;
break ;
WM_CTLCOLORBTN消息
在这边已经看到了如何将显示区域的颜色和文字颜色调节成按钮的背景颜色。我们是否可以将程序中按钮的颜色调节为我们喜欢的颜色呢?理论上没有问题,但在实际中请别这样做。用SetSysColors来改变按钮的外观可能不是您想做的,这会影响目前在Windows下执行的所有程序,这也是使用者不太喜欢的。
更好的方法(同样也只是理论上)是处理WM_CTLCOLORBTN消息,这是当子窗口即将为其显示区域着色时,由按钮控件发送给其父窗口消息处理程序的一个消息。父窗口可以利用这个机会来改变子窗口消息处理程序将用来着色的颜色(在Windows的16位版本中,一个称为WM_CTLCOLOR的消息被用于所有的控件,现在针对每种型态的标准控件,分别代之以不同的消息)。
当父窗口消息处理程序收到WM_CTLCOLORBTN消息时,wParam消息参数是按钮的设备内容句柄,lParam是按钮的窗口句柄。当父窗口消息处理程序得到这个消息时,按钮控件已经获得了它的设备内容。当您的窗口消息处理程序处理一个WM_CTLCOLORBTN消息时,您必须完成以下三个动作:
使用SetTextColor选择设定一种文字颜色。
使用SetBkColor选择设定一种文字背景颜色。
将一个画刷句柄传回给子窗口。
理论上,子窗口使用该画刷来着色背景。当不再需要这个画刷时,您应该负责清除它。
下面是使用WM_CTLCOLORBTN的问题所在:只有按键和拥有者绘制按钮才给其父窗口发送WM_CTLCOLORBTN,而只有拥有者绘制按钮才会响应父窗口消息处理程序对消息的处理,而使用画刷来着色背景。这基本上是没有意义的,因为无论怎样都是由父窗口来负责绘制拥有者绘制按钮。
在本章后面,我们将说明,在某些情况下,一些类似于WM_CTLCOLORBTN但适用于其它型态控件的消息将更为有用。
拥有者绘制按钮
如果您想对按钮的所有可见部分实行全面控制,而不想被键盘和鼠标消息处理所干扰,那么您可以建立BS_OWNERDRAW样式的按钮,如程序9-2所展示的那样。
程序9-2 OWNDRAW
OWNDRAW.C
/*------------------------------------------------------------------------
OWNDRAW.C -- Owner-Draw Button Demo Program
(c) Charles Petzold, 1996
-------------------------------------------------------------------------*/
#include <windows.h>
#define ID_SMALLER 1
#define ID_LARGER 2
#define BTN_WIDTH ( 8 * cxChar)
#define BTN_HEIGHT ( 4 * cyChar)
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -