关键在于TWinControl都是系统自绘,而TCustomControl都是Delphi自绘
真正区别之处,我觉得是在这里:
procedure TWinControl.WMPaint(var Message: TWMPaint); var DC, MemDC: HDC; MemBitmap, OldBitmap: HBITMAP; PS: TPaintStruct; str: String; begin // 这里第一次处理WM_PAINT消息 // 注意,这里是重画句柄控件,重画图形控件不在这里 // fixme 有空检测一下这个消息的DC是否为0 即系统发来的 WM_PAINT消息的参数内容是什么?一般是当前控件的HDC句柄 // 不是双缓冲就立即绘制 // FDoubleBuffered一共就两处使用,还有一处在WMEraseBkgnd if not FDoubleBuffered or (Message.DC <> 0) then begin // 不支持自绘,且没有图形子控件的那些控件,执行这里。 // 主要指Windows自带控件,比如Button,Edit等等,整个StdCtrls里的标准控件都不自绘,但TForm自绘 // important7 TCustomControl与TWinControl实际分家的地方 if not (csCustomPaint in ControlState) and (ControlCount = 0) then // 注意csCustomPaint这个风格,即自绘。只有这里判断使用。 begin// super 其父类根本没有这个函数,所以这里会调用消息索引函数,如果还找不到,就调用子类或者TWinControl.DefaultHandler来处理消息(事实上就是如此)。 // fixme 这里超级复杂,执行Button1.Update会来到这里,会重绘TForm1。再次实验,确实如此。值得写一篇文章 // 最后执行CallWindowProc(FDefWndProc)会调用TButton的WndProc来处理消息。一共7个消息分别处理,把WM转成CN消息。最后会传递到 CNCtlColorBtn // http://hi.baidu.com/bakyman/item/2a426ba5c6251d37020a4d42 // TWinControl走这里(没有继承TCustomControl),比如TButton。它会调用 fixme2 好复杂哦 inherited // fixme 会调用子类的WM_PAINT的消息索引函数?不是很确定 end // 一般走这里(带canvas的控件) else PaintHandler(Message); // TCustomControl走这里,给所有子控件做剪裁并重画(挨个发送WM_PAINT消息) end else begin // 双缓冲,准备内存画板,此时还没有Canvas,所以用API旧方法 // fixme 不清楚,到底是在用谁的句柄绘制? 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擦除背景。fixme 一般来说,擦除背景应该发生在重绘之前 Message.DC := MemDC; // 构建一个消息,把MemDC传入,当前控件和子控件都在MemDC上画 // 注意是虚函数,图形控件和CustomControl都覆盖了它。CustomControl一定会调用这个父类虚函数,但图形控件一定不调用它。 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); // API,退回到旧图形 DeleteDC(MemDC); DeleteObject(MemBitmap); end; end; end;
另外还有
procedure TCustomControl.WMPaint(var Message: TWMPaint); begin // important TForm覆盖了它,因为多了一种情况:最小化时的画图只需画图标即可,与正常状态不是一回事 // 但好笑的是,那里用的不是Include语法,而是+-,难道这两个类不是同一个人写的? Include(FControlState, csCustomPaint); // 标准控件里,没有一处使用这个标记 inherited; // 因为TCustomControl可以包含子控件,因此必须发消息给所有子控件重画,整个调用框架已经在TWinControl里都准备好了 Exclude(FControlState, csCustomPaint); end;