TWinControl.DefaultHandler里的CallWindowProc还挺有深意的,TButton对WM_PAINT消息的处理就是靠它来处理的(以前不明白为什么总是要调用inherited,其实就是没有明白TWinControl.DefaultHandler的真正用处,而且还很有用)

我忽然发现:TButton既没有处理WM_PAINT,又没有Paint()或者PaintWindow(),那么它是什么时候被绘制的?

Form1上放2个TButton,然后设置代码:

procedure TForm1.Button1Click(Sender: TObject);
begin
  button2.Repaint;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  ShowMessage(‘good‘);
end;

在Form1第一次显示时,应该会让这两个Button显示。这两个Button应该会处理WM_PAINT并显示。可是完全找不到相关代码啊。

后来用脑子想了想,TButton是TWinControl,而且是没有图形子控件的,所以它收到WM_PAINT时应该会依次执行:

procedure TButtonControl.WndProc(var Message: TMessage);
begin
  case Message.Msg of
    WM_LBUTTONDOWN, WM_LBUTTONDBLCLK:
      if not (csDesigning in ComponentState) and not Focused then
      begin
        FClicksDisabled := True;
        Windows.SetFocus(Handle);
        FClicksDisabled := False;
        if not Focused then Exit;
      end;
    CN_COMMAND:
      if FClicksDisabled then Exit;
  end;
  inherited WndProc(Message);
end;

procedure TWinControl.WndProc(var Message: TMessage);
var
  Form: TCustomForm;
begin
  case Message.Msg of
    WM_SETFOCUS:
      begin
        Form := GetParentForm(Self);
        if (Form <> nil) and not Form.SetFocusedControl(Self) then Exit;
      end;
    WM_KILLFOCUS:
      if csFocusing in ControlState then Exit;
    WM_NCHITTEST:
      begin
        inherited WndProc(Message);
        if (Message.Result = HTTRANSPARENT) and (ControlAtPos(ScreenToClient(
          SmallPointToPoint(TWMNCHitTest(Message).Pos)), False) <> nil) then
          Message.Result := HTCLIENT;
        Exit;
      end;
    WM_MOUSEFIRST..WM_MOUSELAST:
      if IsControlMouseMsg(TWMMouse(Message)) then
      begin
        { Check HandleAllocated because IsControlMouseMsg might have freed the
          window if user code executed something like Parent := nil. }
        if (Message.Result = 0) and HandleAllocated then
          DefWindowProc(Handle, Message.Msg, Message.wParam, Message.lParam);
        Exit;
      end;
    WM_KEYFIRST..WM_KEYLAST:
      if Dragging then Exit;
    WM_CANCELMODE:
      if (GetCapture = Handle) and (CaptureControl <> nil) and
        (CaptureControl.Parent = Self) then
        CaptureControl.Perform(WM_CANCELMODE, 0, 0);
  end;
  inherited WndProc(Message);
end;

procedure TControl.WndProc(var Message: TMessage);
var
  Form: TCustomForm;
  KeyState: TKeyboardState;
  WheelMsg: TCMMouseWheel;
begin
  if (csDesigning in ComponentState) then
  begin
    Form := GetParentForm(Self);
    if (Form <> nil) and (Form.Designer <> nil) and
      Form.Designer.IsDesignMsg(Self, Message) then Exit
  end;
  if (Message.Msg >= WM_KEYFIRST) and (Message.Msg <= WM_KEYLAST) then
  begin
    Form := GetParentForm(Self);
    if (Form <> nil) and Form.WantChildKey(Self, Message) then Exit;
  end
  else if (Message.Msg >= WM_MOUSEFIRST) and (Message.Msg <= WM_MOUSELAST) then
  begin
    if not (csDoubleClicks in ControlStyle) then
      case Message.Msg of
        WM_LBUTTONDBLCLK, WM_RBUTTONDBLCLK, WM_MBUTTONDBLCLK:
          Dec(Message.Msg, WM_LBUTTONDBLCLK - WM_LBUTTONDOWN);
      end;
    case Message.Msg of
      WM_MOUSEMOVE: Application.HintMouseMessage(Self, Message);
      WM_LBUTTONDOWN, WM_LBUTTONDBLCLK:
        begin
          if FDragMode = dmAutomatic then
          begin
            BeginAutoDrag;
            Exit;
          end;
          Include(FControlState, csLButtonDown);
        end;
      WM_LBUTTONUP:
        Exclude(FControlState, csLButtonDown);
    else
      with Mouse do
        if WheelPresent and (RegWheelMessage <> 0) and
          (Message.Msg = RegWheelMessage) then
        begin
          GetKeyboardState(KeyState);
          with WheelMsg do
          begin
            Msg := Message.Msg;
            ShiftState := KeyboardStateToShiftState(KeyState);
            WheelDelta := Message.WParam;
            Pos := TSmallPoint(Message.LParam);
          end;
          MouseWheelHandler(TMessage(WheelMsg));
          Exit;
        end;
    end;
  end
  else if Message.Msg = CM_VISIBLECHANGED then
    with Message do
      SendDockNotification(Msg, WParam, LParam);
  Dispatch(Message);
end;

procedure TWinControl.WMPaint(var Message: TWMPaint);
var
  DC, MemDC: HDC;
  MemBitmap, OldBitmap: HBITMAP;
  PS: TPaintStruct;
begin
  if not FDoubleBuffered or (Message.DC <> 0) then
  begin
    if not (csCustomPaint in ControlState) and (ControlCount = 0) then
      inherited // 执行这里,会执行TWinControl.DefaultHandler,因为TButton和TButtonControl都没有覆盖DefaultHandler函数
    else
      PaintHandler(Message);
  end
  else
  begin
    DC := GetDC(0);
    MemBitmap := CreateCompatibleBitmap(DC, ClientRect.Right, ClientRect.Bottom);
    ReleaseDC(0, DC);
    MemDC := CreateCompatibleDC(0);
    OldBitmap := SelectObject(MemDC, MemBitmap);
    try
      DC := BeginPaint(Handle, PS);
      Perform(WM_ERASEBKGND, MemDC, MemDC);
      Message.DC := MemDC;
      WMPaint(Message);
      Message.DC := 0;
      BitBlt(DC, 0, 0, ClientRect.Right, ClientRect.Bottom, MemDC, 0, 0, SRCCOPY);
      EndPaint(Handle, PS);
    finally
      SelectObject(MemDC, OldBitmap);
      DeleteDC(MemDC);
      DeleteObject(MemBitmap);
    end;
  end;
end;

所以就真的没办法,只能执行:

procedure TWinControl.DefaultHandler(var Message);
begin
  if FHandle <> 0 then
  begin
    with TMessage(Message) do
    begin
      if (Msg = WM_CONTEXTMENU) and (Parent <> nil) then
      begin
        Result := Parent.Perform(Msg, WParam, LParam);
        if Result <> 0 then Exit;
      end;
      case Msg of
        WM_CTLCOLORMSGBOX..WM_CTLCOLORSTATIC:
          Result := SendMessage(LParam, CN_BASE + Msg, WParam, LParam);
        CN_CTLCOLORMSGBOX..CN_CTLCOLORSTATIC:
          begin
            SetTextColor(WParam, ColorToRGB(FFont.Color));
            SetBkColor(WParam, ColorToRGB(FBrush.Color));
            Result := FBrush.Handle;
          end;
      else
        if Msg = RM_GetObjectInstance then
          Result := Integer(Self)
        else
        begin
          if Msg <> WM_PAINT then // 稍微改造一下,否则程序执行会出错
          Result := CallWindowProc(FDefWndProc, FHandle, Msg, WParam, LParam); // 会执行到这里!
        end;
      end;
      if Msg = WM_SETTEXT then
        SendDockNotification(Msg, WParam, LParam);
    end;
  end
  else
    inherited DefaultHandler(Message);
end;

执行完TWinControl.DefaultHandler后,程序对TButton的WM_PAINT消息就算处理完毕了,会一路返回。

百思不得其解的情况下,忽然灵机一动,TButton是封装了微软的Button,虽然其代码不可见,但也应该是正确处理了WM_PAINT消息的,那么只要把WM_PAINT消息发给这个Button的句柄,不就可以正确显示了?这一切,还多亏了CallWindowProc(FDefWndProc, FHandle, Msg, WParam, LParam);的调用。我想完全屏蔽这句试试效果,结果程序运行出错,因为这样一来许多消息都无法得到处理。那么就加个条件吧:if Msg <> WM_PAINT then 结果发现,两个Button果然灰蒙蒙一片,无法正确显示!但是点击执行事件还是没问题的,而且点击之后,就可以正确显示了,经过研究,又有一番滋味:

procedure TButtonControl.WndProc(var Message: TMessage);
begin
  case Message.Msg of
    WM_LBUTTONDOWN, WM_LBUTTONDBLCLK:
      if not (csDesigning in ComponentState) and not Focused then
      begin
        FClicksDisabled := True;
        Windows.SetFocus(Handle);
        FClicksDisabled := False;
        if not Focused then Exit;
      end;
    CN_COMMAND:
      if FClicksDisabled then Exit;
  end;
  inherited WndProc(Message);
end;

procedure TControl.WMLButtonDown(var Message: TWMLButtonDown);
begin
  SendCancelMode(Self);
  inherited; // 这里,也会调用TWinControl.DefaultHandler,而且传递的消息是WM_LBUTTONDOWN
  if csCaptureMouse in ControlStyle then MouseCapture := True;
  if csClickEvents in ControlStyle then Include(FControlState, csClicked);
  DoMouseDown(Message, mbLeft, []);
end;

procedure TWinControl.DefaultHandler(var Message);
begin
  if FHandle <> 0 then
  begin
    with TMessage(Message) do
    begin
      if (Msg = WM_CONTEXTMENU) and (Parent <> nil) then
      begin
        Result := Parent.Perform(Msg, WParam, LParam);
        if Result <> 0 then Exit;
      end;
      case Msg of
        WM_CTLCOLORMSGBOX..WM_CTLCOLORSTATIC:
          Result := SendMessage(LParam, CN_BASE + Msg, WParam, LParam);
        CN_CTLCOLORMSGBOX..CN_CTLCOLORSTATIC:
          begin
            SetTextColor(WParam, ColorToRGB(FFont.Color));
            SetBkColor(WParam, ColorToRGB(FBrush.Color));
            Result := FBrush.Handle;
          end;
      else
        if Msg = RM_GetObjectInstance then
          Result := Integer(Self)
        else
        begin
          if Msg <> WM_PAINT then // 因为是WM_LBUTTONDOWN,所以照样会传进去
          Result := CallWindowProc(FDefWndProc, FHandle, Msg, WParam, LParam);
        end;
      end;
      if Msg = WM_SETTEXT then
        SendDockNotification(Msg, WParam, LParam);
    end;
  end
  else
    inherited DefaultHandler(Message);
end;

之所以点击以后,会重新出现完整的Button,是因为点击以后,Button的显示情况要变,趁此机会,微软的Button就把它重绘了一遍。真是好复杂呀,好多都是靠猜的。

时间: 2024-12-20 01:07:53

TWinControl.DefaultHandler里的CallWindowProc还挺有深意的,TButton对WM_PAINT消息的处理就是靠它来处理的(以前不明白为什么总是要调用inherited,其实就是没有明白TWinControl.DefaultHandler的真正用处,而且还很有用)的相关文章

java里遇到的两个错误,还挺常见的,也比较麻烦

java.lang.NoClassDefFoundError 这个是说,你的一个类找不到. 1.你的路径可能有问题: 2.我新增加一个package,里面有一个类A在java文件B中使用了,而我现在编译的是java文件C,C里用了B中的类:这时,也会出现这个错误.也就是:A--->B--->C. 此时,应该先编译B,再编译A. java.lang.NullPointerException 空指针,可能出现在读取文件的时候,文件出现空的情况,回头检查之前使用的文件即可. java里遇到的两个错误

现在觉得IT还挺有意思

前两天刚刚接触编程,用的是C#.开始确实枯燥,但是今天的感觉就好多了,还挺有意思.根据老师讲的课程自己编写了小程序,运行起来还不错.在这里分享下. 关于时间安排的小程序: int sj; int aa; string noon; string jg; Console.WriteLine("我是时间小助手"); Console.WriteLine("当前时间是:"); sj = Convert.ToInt32(Console.ReadLine()); noon = s

java android布局里的控件值 反射绑定给实体类,实体类绑定给控件,表单提交绑定很有用

注意了:根据实际情况,添加实体里字段的类型,控件类型的判断才可使用.这里控件只有TextView EditText 实体类字段只有String int类型,带值的控件添加tag ,值和实体类的字段值一致 package ice.ui.service; import java.lang.reflect.Field;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import jav

突然觉得我懂的还挺多,嘎嘎~

今天设计一个预算控制的流程,并设计数据模型,进行数据试算.初稿完成之后,我邀产品经理zhangll和wangxuedong.小组里的技术人员一起评审.评审完之后,两位产品经理对我的设计比较赞同,说,和你合作我们很放心.我说,跟我合作,你们会很累,我也累.一段聊天后,产品经理问,现在咱们在做的这个项目还有哪些不明确(包括需求方面)的事项? 接着就说说下面的聊天内容吧.首先,公司的项目由产品经理充当项目经理,至少,这个是在一次小众会议上,产品部老大说的.那么问题来了,既然你产品经理是项目经理,怎么反

丫的一个三线手机还挺火

前天看见一条有关联想移动的新闻.联想在杨元庆领导下,继拿下IBM的PC全部业务之后的又一个大手笔,是去年并购了谷歌旗下的Moto,并入联想移动.而在日前,联想集团执行副总裁.移动业务集团总裁.摩托罗拉移动管理委员会主席刘军离职,是继先前多个重量级中高层管理离去的又一个超重量人物离去,据说是为联想移动连年亏损负责. 联想手机甚至连年市场占有率下降已经不是负面新闻了,这一点也是不争的历史.令我最不能相信的是,按说还算年轻的杨元庆,他那个脑子怎么就是那么的僵硬,就像一个PROM一样,刷了一次就固化了,

解决了一个博问里的问题,挺简单的,记下来满足自己的小小成就感

先贴问题:http://q.cnblogs.com/q/77610/ public static string[] Stops = new string[] { "AB", "BC", "CD", "DC", "DE", "AD", "CE", "EB", "AE" }; 算法:列举数组中包含的任意2个字母之间的路径集合. 比如

色个人的个人还挺好的

http://www.iqiyi.com/playlist383730202.html http://www.iqiyi.com/playlist383730502.html http://www.iqiyi.com/playlist383730702.html http://www.iqiyi.com/playlist383730902.html http://www.iqiyi.com/playlist383731102.html http://www.iqiyi.com/playlist3

PHP常用函数,和一些乱起八糟的玩意儿,还挺实用的,善用php内置函数很重要

//权限控制必须文件属主正确 on linux <?php //自己写的一个调试的 class debug {     public function put_String($result)     {         $file = '/usr/share/nginx/html/test/www/debug.html';         $date = date('Y:m:d H:i:s');         file_put_contents($file, '');         if (

QInputDialog还挺好用的呵呵

QStringList items; items << QObject::tr("Spring") << QObject::tr("Summer") << QObject::tr("Fall") <<QObject::tr("Winter"); //bool ok; QString item = QInputDialog::getItem(NULL, QObject::tr(&q