http://www.cnblogs.com/acuier 整整十几篇,省得我自己研究,学一下就可以了。
测试颜色:
// 找到了WM_PAINT消息处理,既然找到了,那么对WM_PAINT消息也算有个交代了,于是上面所有的WndProc全部正常结束。 // 除非TWinControl.WMPaint里面再继续执行复杂的调用,否则Button1.Update就算执行结束了。 procedure TWinControl.WMPaint(var Message: TWMPaint); var DC, MemDC: HDC; MemBitmap, OldBitmap: HBITMAP; PS: TPaintStruct; begin // 注意,这里是重画句柄控件,重画图形控件不在这里 // fixme 不知道什么时候是双缓冲 if not FDoubleBuffered or (Message.DC <> 0) then begin // 自己不把自己算做子控件,所以这个函数是用来重画子控件的。 // 想画自己,就得另外覆盖Paint;函数,但是发消息给子控件重画这件事情,这里已经帮助程序员写好了(子控件自己还是要覆盖Paint;才能保证正确画自己) // 注意csCustomPaint这个风格 if not (csCustomPaint in ControlState) and (ControlCount = 0) then inherited // 假牙,父类根本就没有相关函数,什么都不做。fixme,好像是通过父类来重画自己 else PaintHandler(Message); // 一般走这里,给所有子控件做剪裁并重画(挨个发送WM_PAINT消息) end else begin // 准备内存画板,此时还没有Canvas,所以用API旧方法 DC := GetDC(0); // 参数0代表取得整个屏幕的DC MemBitmap := CreateCompatibleBitmap(DC, ClientRect.Right, ClientRect.Bottom); // 创建当前DC的画板(大概是为了保留除了当前窗口以外的显示内容,跟画板的底板一样) ReleaseDC(0, DC); // 留下MemBitmap,然后释放整个屏幕的DC MemDC := CreateCompatibleDC(0); // 创建当前DC的兼容DC OldBitmap := SelectObject(MemDC, MemBitmap); // 把MemBitmap画板放到MemDC里去,就可以准备在MemDC里画了 try DC := BeginPaint(Handle, PS); // 返回值是指定Window的DC // 双缓冲工作真正开始 Perform(WM_ERASEBKGND, MemDC, MemDC); // 当前控件使用MemDC擦除背景 Message.DC := MemDC; // 构建一个消息,把MemDC传入,当前控件和子控件都在MemDC上画 WMPaint(Message); // 递归调用函数(构建了一个消息,但不是发生消息),而且此时的DC不等于0,因此条件成立,进入块执行PaintHandler Message.DC := 0; // 消息使用完毕,消息参数复位,但是通过消息得到的MemDC所有数据都在 // 画完了内存画板,准备切换 BitBlt(DC, 0, 0, ClientRect.Right, ClientRect.Bottom, MemDC, 0, 0, SRCCOPY); // 把画好所有控件的MemDC一次性拷贝到指定Window的DC EndPaint(Handle, PS); // 结束画图过程 finally SelectObject(MemDC, OldBitmap); DeleteDC(MemDC); DeleteObject(MemBitmap); end; end; end;