VC设置视图背景颜色方法
视图的背景一般来说是白色的,在缺省情况下,它和系统定义的颜色COLOR_WINDOW是一致的。设计者一般会希望自己的程序可以让用户轻松地改变窗口背景颜色,或是用漂亮的图片来充填背景。我们可以用Windows函数SetSysColors来重新指定COLOR_WINDOW所对应的实际颜色,来达到改变视图背景颜色的目的。但这样会同时改变其他应用程序的视图窗口背景,使得整个Windows系统的颜色设置产生混乱。另外,我们可能会用以下方法来设置视图的背景颜色,即在CView的OnDraw函数中添写如下一段程序代码:
void CTestView::OnDraw(CDC* pDC) { CTestDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); CRect rectClient; CBrush brushBkColor; GetClientRect(rectClient); brushBkColor.CreateSolidBrush(RGB(255,0,0)); pDC->DPtoLP(rectClient); pDC->FillRect(rectClient,&brushBkColor); … }
这样可以达到改变当前应用程序的视图背景的目的,但同时也产生了一些不良影响,使得程序运行效果不尽如人意。
分析问题
我们知道,在VC++的文档、视结构中,CView的OnDraw函数用于实现绝大部分图形绘制的工作。如果用户改变窗口尺寸,或者显示隐藏的区域,OnDraw函数都将被调用来重画窗口。并且,当程序文档中的数据发生改变时,一般必须通过调用视图的Invalidate(或InvalidateRect)成员函数来通知Windows所发生的改变,对Invalidate的调用也会触发对OnDraw函数的调用。正因为OnDraw函数被频繁调用,所以在其执行时,每次都刷新填充一次视图客户区域,便会使屏幕不稳定,产生闪烁现象。
对VC++应用程序框架结构和Windows消息映射系统的仔细研究,找到另外一种改变视图背景的方法,其执行效果比上述两种方法都好。其实在程序调用OnDraw函数之前,会触发一个Windows消息:WM_ERASEBKGND,以擦除视图刷新区域。在缺省情况下,Windows系统使用视图窗口注册时窗口类中的成员hbrBackground所描述的画刷来擦除屏幕,这一般会将屏幕刷新成COLOR_WINDOW所对应的颜色。因此,在OnDraw函数中设置背景颜色的执行过程是这样的:先将屏幕刷新成COLOR_WINDOW所对应的颜色(白色),接着又在OnDraw函数中填充其他颜色(绿色刷背景,或者视图用别颜色画图),这正是产生屏幕闪烁的根本原因。
对WM_PAINT的处理几乎总是从一个BeginPaint调用开始:hdc = BeginPaint (hwnd, &ps) ;而以一个EndPaint调用结束,中间条用OnPaint()函数:在BeginPaint调用中,如果显示区域的背景还未被删除,则由Windows来删除。(即用默认白色画刷在刷一遍屏幕,即重绘背景色)它使用注册窗口类别的WNDCLASS结构的hbrBackground字段中指定的画刷来删除背景。一般, 这是一个白色备用画刷。这意味着,Windows将通过把窗口背景设定为白色来删除窗口背景。
解决问题
通过上述分析,我们应将视图背景颜色填充移到Windows消息:WM_ERASEBKGND所对应的消息映射函数中,而不是在OnDraw函数中。我们可以通过下列步骤实现这一过程:在文档类中增加一成员变量m_viewBkColor保存当前背景颜色,同时增加两个成员函数GetViewBkColor和SetViewBkColor对其进行读写操作。这样做的好处是可以对m_viewBkColor成员进行序列化,将其和文档联系在一起,打开某一文档时,其背景将和上一次程序操作该文档时的背景保持一致。在视图类中为视图的Windows消息WM_ERASEBKGND增加消息映射函数OnEraseBkgnd,代码如下:
BOOL CTestView::OnEraseBkgnd(CDC* pDC) { CRect rect; CBrush brush; brush.CreateSolidBrush(GetDocument()->GetViewBkColor()); pDC->GetClipBox(rect); pDC->FillRect(rect,&brush); return true; }