⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 见招拆招《windows程序设计》(八) .txt

📁 会变语言实现的一些程序
💻 TXT
📖 第 1 页 / 共 5 页
字号:

复选框最常用的两种样式是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) ;
        
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.ASM
        
该程序在其显示区域的中央包含了两个按钮,如图9-2所示。左边的按钮有四个三角形指向按钮的中央,按下该按钮时,窗口的尺寸将缩小10%。右边的按钮有四个向外指的三角形,按下此按钮时,窗口的尺寸将增大10%。

如果您只需要在按钮中显示图标或位图,您可以用BS_ICON或BS_BITMAP样式,并用BM_SETIMAGE消息设定位图。但是,对于BS_OWNERDRAW样式的按钮,它允许完全自由地绘制按钮。


 



图9-2 OWNDRAW的屏幕显示
 

在处理WM_CREATE消息处理期间,OWNDRAW建立了两个BS_OWNERDRAW样式的按钮;按钮的宽度是系统字体的8倍,高度是系统字体的4倍(在使用预先定义好的位图绘制按钮时,这些尺寸在VGA上建立的按钮为64图素宽64图素高,知道这些数据将非常有用)。这些按钮尚未就定位,在处理WM_SIZE消息处理期间,通过呼叫MoveWindow函数,OWNDRAW将按钮位置放在显示区域的中心。

按下这些按钮时,它们就会产生WM_COMMAND消息。为了处理这些WM_COMMAND消息,OWNDRAW呼叫GetWindowRect,将整个窗口(不只是显示区域)的位置和尺寸存放在RECT(矩形)结构中,这个位置是相对于屏幕的。然后,根据按下的是左边还是右边的按钮,OWNDRAW调节这个矩形结构的各个字段值。程序再通过呼叫MoveWindow来重新确定位置和尺寸。这将产生另一个WM_SIZE消息,按钮被重新定位在显示区域的中央。

如果这是程序所做的全部处理,那么这完全可以,只不过按钮是不可见的。使用BS_OWNERDRAW样式建立的按钮会在需要重新着色的任何时候都向它的父窗口发送一个WM_DRAWITEM消息。这出现在以下几种情况中:当按钮被建立时,当按钮被按下或被放开时,当按钮得到或者失去输入焦点时,以及当按钮需要重新着色的任何时候。

在处理WM_DRAWITEM消息处理期间,lParam消息参数是指向型态DRAWITEMSTRUCT结构的指针,OWNDRAW程序将这个指针储存在pdis变量中,这个结构包含了画该按钮时程序所必需的消息(这个结构也可以让自绘清单方块和菜单使用)。对按钮而言非常重要的结构字段有hDC (按钮的设备内容)、rcItem(提供按钮尺寸的RECT结构)、CtlID(控件窗口ID)和itemState (它说明按钮是否被按下,或者按钮是否拥有输入焦点)。

呼叫FillRect用白色画刷抹掉按钮的内面,呼叫FrameRect在按钮的周围画上黑框,由此OWNDRAW便启动了WM_DRAWITEM处理过程。然后,通过呼叫Polygon,OWNDRAW在按钮上画出4个黑色实心的三角形。这是一般的情形。

如果按钮目前被按下,那么DRAWITEMSTRUCT的itemState字段中的某位将被设为1。您可以使用ODS_SELECTED常数来测试这些位。如果这些位被设立,那么OWNDRAW将通过呼叫InvertRect将按钮翻转为相反的颜色。如果按钮拥有输入焦点,那么itemState的ODS_FOCUS位将被设立。在这种情况下,OWNDRAW通过呼叫DrawFocusRect,在按钮的边界内画一个虚线的矩形。

在使用拥有者绘制按钮时,应该注意以下几个方面:Windows获得设备内容并将其作为DRAWITEMSTRUCT结构的一个字段。保持设备内容处于您找到它时所处的状态,任何被选进设备内容的GDI对象都必需被释放。另外,当心不要在定义按钮边界的矩形外面进行绘制。

静态类别


在CreateWindow函数中指定窗口类别为「static」,您就可以建立静态文字的子窗口控件。这些子窗口非常「文静」。它既不接收鼠标或键盘输入,也不向父窗口发送WM_COMMAND消息。

当您在静态子窗口上移动或者按下鼠标时,这个子窗口将拦截WM_NCHITTEST消息并将HTTRANSPARENT的值传回给Windows,这将使Windows向其下层窗口,通常是它的父窗口,发送相同的WM_NCHITTEST消息。父窗口常常将该消息传递给DefWindowProc,在这里,它被转换为显示区域的鼠标消息。

前六个静态窗口样式只简单地在子窗口的显示区域内画一个矩形或者边框。在下表的上部,「RECT」静态样式(左列)是填入图样的矩形样式;三个「FRAME」样式(右列)是没有填入图样的矩形轮廓:

SS_BLACKRECT
SS_GRAYRECT

SS_WHITERECT
 SS_BLACKFRAME
SS_GRAYFRAME

SS_WHITEFRAME
 

「BLACK」、「GRAY」、「WHITE」并不意味着黑、灰和白色,这些颜色是由系统颜色决定的,如表9-4所示。

表9-4
 

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -