终于懂了:TWinControl与TCustomControl真正区别之处,TWinControl系统自绘,TCustomControl是Delphi自绘

关键在于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;