VCL里为什么要用类函数代替API,为什么要用CM_消息代替虚函数

之所以要用类函数代替API,是因为VCL对它做了一些包装,好在API起作用之前和之后做一些额外的事情:通知和判断等等。
之所以类函数要包装一个CM_消息,是因为这样方便程序员截断和改写这个处理过程

举例:

procedure TWinControl.Invalidate;
begin
  // 注意,是简单执行函数,把消息当作参数,不是发送消息
  Perform(CM_INVALIDATE, 0, 0); // 注意,第二个参数即WParam是0,即要求API使自己失效,而不是仅仅做一个通知作用。
end;

procedure TWinControl.CMInvalidate(var Message: TMessage);
var
  I: Integer;
begin
  // 这个函数被所有Windows子控件所继承,只要没有被程序员(或者控件程序员)覆盖,那么就会使用API将整个客户区全部都声明为无效区域,其实过程很简单
  // 唯一要注意的是,子控件声明无效区域之前,会通知所有的祖先控件,最高到Form1为止,Form1不再有Parent(但是有父组件)
  // 其实最后就是简单调用API,但是就是被VCL库框架包装了一遍

  // 这是大前提,反正Win控件都应该有句柄。如果还没有句柄,那就不必通知,更不必调用API
  if HandleAllocated then
  begin
    // 相当于子控件要重画,必须通知一下父控件,而且是挨个通知,父控件想处理就处理,不处理就算了(大部分情况下不会处理)。
    if Parent <> nil then
      // Perform会先到控件自己的虚函数WndProc里去寻找消息处理,而不是直接发给消息索引函数处理。fixme 做这么多无用功,会不会效率很低啊
      // 注意第二个参数WParam等于1。父控件它们只是知道了一下,不会真的重画它们自己(除非它们覆盖了CM_INVALIDATE消息)。对比Invalidate函数的第二个参数就是0,那就是真的重绘。
      // fixme 问题:什么时候会用到这招?回答:程序员覆盖这个函数的时候会用到。
      // Win控件的处理一般自己处理就足够了,到此为止。但是也额外留了一个接口,StdCtrls单元和Forms单元里都没有处理这个函数,但TsAlphaListBox单元用到多。
      Parent.Perform(CM_INVALIDATE, 1, 0); // 递归,先通知父类。
      
    // 父控件通知完了,就该重画自己了。
    // 注意这个WParam参数等于0,只有在这种情况下,说明是Invalidate这里直接过来的,才会重画自己。
    if Message.WParam = 0 then
    begin
      // 仅仅是标识无效区域,并不真的重画,重绘还得等收到WM_PAINT消息以后靠调用PaintHandler函数,标识无效区域随时都可以
      // 等到系统闲的时候自动探测无效区域,如果有的话,就发送你WM_PAINT消息
      // csOpaque不在风格里则背景重绘。csOpaque在风格里面则背景不重绘(不透明,当前控件在父控件里所占的位置会被全部遮住,所以背景色不需要重绘)。(只有自己是透明的时候,当前控件的客户区才需要父控件来帮忙绘制)
      // fixme 背景应该不是指自己的客户区,而是其父控件的客户区?
      InvalidateRect(FHandle, nil, not (csOpaque in ControlStyle)); // API,第二个参数为NULL的话,则重画整个客户区;第三个参数TRUE则背景重绘。
      { Invalidate child windows which use the parentbackground when themed }
      // fixme 只有应用主题的时候才重画子控件。经过测试,classic 和 蓝天白云都不算应用主题呢?
      if ThemeServices.ThemesEnabled then
        for I := 0 to ControlCount - 1 do
          // 一旦父Win控件背景更新了,那么就要通知图形子控件,只要是需要父控件的背景的,那么就得重绘
          if csParentBackground in Controls[I].ControlStyle then // important
            Controls[I].Invalidate;
    end;
  end;
end;

 

VCL里为什么要用类函数代替API,为什么要用CM_消息代替虚函数

上一篇:C#设计模式(1)——单例模式


下一篇:WIN7或2008远程连接特别慢的解决方法 【转】