此文章可能包含在发布时有效的网络连接,但是现在可能连接到不存在的网站或网页。为保持文章的完整性,我们保留连接的文字,但是禁用连接。
此文章的代码: C++0597.exe (22KB)
Paul DiLascia 是一个自由软件顾问,特长是C++和Windows的训练和软件开发 。他是Windows ++: Writing Reusable Code in C++ (Addison-Wesley, 1992)一书的作者。
Q我如何写一个像Microsoft® Encarta®、 Bookshelf®、和 MoneyHow那样绘制有纹理的背景的应用程序?我试过处理WM_ ERASEBKGND但是我不能使它工作。
Robert H. Mowery III
A
WM_ERASEBKGND是一个正确的想法,但是绘制有纹理的背景可能使你头疼,如果你懂我的意思的话。我写过一个叫做Pform (着色的表单或漂亮的表单,随你选择)的应用程序演示怎么完成它。Pform是一个标准MFC应用程序,使用CFormView将一个对话框显示为一个表单。它允许你显示对话框时显示多种不同的背景:无格式、凹痕、青色石雕背景控件,或全部石雕背景(见图1). 我选择一个基于表单的应用程序,因为对话框在Windows®的绘制工作上的控制最少;如果你使用自己的主窗口和自定义控件,你可以干你自己想要干的任何事;绘制背景就不是一个挑战了。图2 显示CMyFormView的代码.Pform的剩余部分是MFC常规工作,你可以在本文开始的连接处下载。 图 1: Pform—无格式最简单和最可靠的建立背景纹理的方法是使用一个叫做图案刷东西。基本思想是处理WM_CTLCOLOR 消息,使你可以告诉Windows在画各种控件的背景时用什么刷子。在Windows要画控件的时候,它发送WM_CTLCOLOR给控件的父窗口。这是你改变控件背景的好机会。 要关于设置控件颜色的更多信息,请查看我的1996年7月的专栏) 在以前有一个单独的消息,WM_CTLCOLOR,包含子码比如按钮背景使用的CTLCOLOR_BTN和整个对话框使用的CTLCOLOR_DLG。在新的Win32®领域, WM_CTLCOLOR被替换为单独的WM_CTLCOLORXXX 消息. 图3 演示所有新的消息。为了向后兼容,MFC将新的消息映射到老的,技术上过时的WM_CTLCOLOR,在有些时候更加方便。特别的, Pform使用一个简单的OnCtlColor处理函数为所有WM_CTLCOLORXXX 消息设置相同的背景。对于凹下的背景,它看起来像这样: HBRUSH CMyFormView::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
pDC->SetBkMode(TRANSPARENT);
pDC->SetTextColor(
GetSysColor(COLOR_WINDOWTEXT));
return m_brDents;
}
我设置背景模式为透明,使所有文本被透明地画出来(例如,单选框、静态控件或文本框),不用改变背景。
图 4否则,Windows 将使用当前的文本颜色。这里你也可以调用pDC->SetTextColor 或SetBkColor ,要是你需要修改本本的背景颜色。
背景模式设完之后,OnCtlColor返回一个 刷子的句柄m_brDents。 注意MFC转换函数CBrush::operator HBRUSH 将CBrush 转换为一个HBRUSH。通过返回一个HBRUSH,你告诉Windows用它来画控件的背景。要建立一个图案刷,你要现载入位图,然后调用—剩下的是什么?—CreatePatternBrush. // In CMyFormView::CMyFormViewm_bmDents.LoadBitmap(IDB_DENTS);
m_brDents.CreatePatternBrush(&m_bmDents);
IDB_DENTS确定位图资源,CMyFormView::CMyFormView载入CBitmap m_bmDents,然后调用CBrush::CreatePatternBrush从这个位图创建刷子。图 4 演示这个位图,一个画凹痕的8 X 8 像素的小方块。当Windows使用一个图案刷画一个区域的时候,它重复使用这个图案,提供你看到的如图 5的效果。 图 5: 凹痕视图最后,你必须设置文本颜色为COLOR_ WINDOWTEXT因为不这么干的话文本默认为黑色,甚至当用户自定义了他的桌面颜色方案时也是这样。当你为WM_ONCTLCOLOR,返回一个非NULL值的时候,Windows并未设置文本颜色,你只能自力更生。到目前为止还不错。只有一个图案刷的小问题:在Windows 95中,它们不能大于8 X 8 像素 (在Windows NT® 中它们可以是你喜欢的大小。)如果你要在Windows 95中使用一个更大的位图—比如图 6 中的石雕图案,你不能使用图案刷和OnCtlColor,相应的,你必须处理WM_ERASEBKGND。一种特性,两种实现方法—为什么你不只喜欢Windows? Windows在紧接WM_PAINT之前发送WM_ERASEBKGND,如果某人调用了InvalidateWindow且fErase参数为 TRUE。默认消息处理程序 (DefWndProc)使用系统颜色GetSysColor(COLOR_WINDOW)画窗口,但是,你可以处理WM_ ERASEBKGND做你自己的工作。Pform 中的CMyFormView::OnEraseBkgnd 使用石雕位图平铺窗口。换句话说,它重复绘制这个位图—从左到右,从下到上—直到整个窗口画完。 Figure 6:石雕图案
但是等一下!OnEraseBkgnd 只对父窗口工作,而不对控件工作。你怎么能使选项框、单选框和静态文本框显示石雕背景?
你必须狡猾一点。在Pform中,我为OnCtlColor返回一个空的刷子。一个空的或者无效的.刷子是一个不画任何东西的刷子。i你可以用 CGdiObject::CreateStockObject(NULL_BRUSH)建立一个。用空的刷子像使用透明方式显示文本一样:控件不管是不是在屏幕上都绘制自己。所以现在对话框使用石雕位图平铺背景, So now the dialog tiles its background with the marble bitmap (响应WM_ERASEBKGND),且控件使用空的刷子画自己,造成保留父窗口的石雕背景的效果。漂亮,嗯?他只对两类空间不起作用:文本框和静态图标。 文本框的问题是他们可被编辑。如果用户键入abc并按下Backspace 删除c,文本框通过重画开始的ab来更新自己,然后用OnCtlColor返回的刷子更新余下的空白。通常,这将擦去c,但是因为我重载了OnCtlColor,使用一个空的刷子,c没有被擦去,唉。要是你下载并编译这个程序,选择View | Marble,不选中View | Paint Edit Controls Too 以使用空的刷子,然后在文本框中键入然后试图删除,你可以看到这个有问题的行为。删除了的字符没有消失—你必须调整编辑空间的大小以摆脱它们。 空刷技术在应用到透明色的静态图标上也遭到失败。图标是16 X 16 或 32 X 32 位图,允许使用透明色显示下面的东西。这是图标看起来不是矩形的原因。如果你返回一个有色或图案刷子, 图标工作得很好;如果你给OnCtlColor返回一个空的刷子, Windows没有适当地绘制图标。我个人认为这是一个bug,但是很容易猜到它的原因:静态控件可能分配了一个内存设备环境,使用背景刷子绘制它,然后在它上面透明地画图标,最后复制到屏幕。如果背景刷子是空的刷子, 背景保留Windows分配内存设备环境时留下的随机内存数据。那么你怎么解决这两个问题?对于文本框,我简单地把皮球踢出去:HBRUSH CMyFormView::OnCtlColor(...)
{
if (nCtlColor==CTLCOLOR_EDIT)
// normal processing:
// don't do anything
return CFormView::OnCtlColor(pDC,
pWnd, nCtlColor);
•
•
•
}
图 7 白文本框现在文本框正常时显示为白色(实际上的 WINDOW_COLOR) (见 图 7)。我个人认为不动文本框是上策。如果你确实要你的文本框有一个位图背景, 你将要做很多工作,因为文本框做个很多自己的绘制工作,这个过程超出了一个专栏的范围,但是一个好的建议是使用和你的位图配合的纯色,像我在图8中使用的青色刷子一样。
图 8 青色背景对静态图标,你有两个选择。你可以在设计图标的时候选择不透明的颜色,或者你使用一个自绘图标,Windows95的新东西。对于Pform,我选择后者,我将在一分钟内讲完。首先,我将指出当完成纹理背景时我要考虑的一些问题。特别的,你必须考虑文字在你的背景对比下的清晰程度。像图 7 演示的一样,要是你的位图包含暗色,文字看起来就很烂。最好的解决方案是使用和你的位图配合的纯色 ,像图 8。另一方案是选择一个使用明色的位图。当然—即使你从不知道为什么拉链头的文本色是黄色— 你可能想强制使用黑色的文本—但是你开始偏离了用户界面的准则—最理想的是你允许用户选择文本色。最后,我必须解释按钮如何和背景混合。OnCtlColor使你可以控制 按钮背景颜色,但不是它的外观。这种方法很笨,因为按钮并不是像看起来那样真有一个背景。为了给外观着色,你必须实现一个自绘按钮并处理WM_DRAWITEM消息。 Pform 演示了如何做;这基本上只是一个乏味的技巧。 CMyFormView::OnDrawItem就是做这个工作的。它用COLOR_BTNTEXT画一个纯青或石雕按钮的文字和边框,要是按钮被按下的话,就向下和右偏移。同一函数同样考虑Pform使用空的刷子画石雕时的静态图标:在用石雕画完图标背景之后,它调用DrawIcon手动绘制这个图标。挑刺的家伙将注意到我在两种情况下都混了过去:我没有对齐石雕位图。技术上,我应该计算位图的位置以设置绘图的原点,但是在截稿的凌晨一点的时候它对我的可怜的大脑来说是不可容忍的,所以我简单地从(0,0)开始。如果你注意看。你将看见在图 7中石雕位图在按钮和静态图标中稍稍偏了一点。但是不从这么近和不用望远镜来看,它工作的很好。konga津周围有一个边界会有帮助,因此我使静态图标凹下去。对图案刷子来说(比如凹痕示例), 你必须用SetBrushOrgEx 设置刷子的原点,这个,我说过,对过度疲劳的大脑来说太多了。我使按钮在凹痕视图中保持原状,也就是纯灰色。我的对于按钮的OnDrawItem 也没有全部完成—我没有画焦点矩形—留给你在晚上一个人的无聊时候干干 (技巧:使用DrawFocusRect.) 我留下了砂纸刷子的原材料作为练习,给那些高级的或者晚上一个人没其他事好干的读者。Windows 95 和 Windows NT 的奇妙的区别之一是Windows NT 自动为你对齐图案刷子,所以你不都需要调用SetBrushOrgEx。现在,你得到它了—很多凌乱的活。像通常在Windows中一样,外观看起来漂亮的里面实际上是恐怖的。如果你想要像Encarta 和其他公司有的富有想象力的用户界面,没有容易的快捷方式。你只能磨你的牙,然后写一些代码—也要比我给你看到的更多,如果你要显示文本框。至少,要是你为C++类集合来写代码,你可以在多于一个的工程里面重用它。你是否跳进这个陷阱取决于你的用户。最后一个警告:要是你使用位图,选择一个只包含20个标准系统调色板颜色的,否则你将有另一个噩梦:实现调色板。有关于C 或C++编程的问题? 发送它到askpd@pobox.com
来自于1997年五月Microsoft Systems Journal的文章。可以从杂志摊获得它,或者,一个更好的方式是,订阅。
© 1997 Microsoft Corporation. All rights reserved. Legal Notices.