代码之家  ›  专栏  ›  技术社区  ›  zig

为什么CM\U CONTROLLISTCHANGE对间接父控件执行?

  •  3
  • zig  · 技术社区  · 7 年前

    我注意到如果我有一个主容器/父容器( MainPanel ),向其中添加子面板( ChildPanel CM_CONTROLLISTCHANGE 在…上 (英寸 TWinControl.InsertControl()

    但是如果我插入一个子控件( ChildButton )至 主面板 !

    CM\U控制器更改 只为…开火 子面板 进入 子面板 .

    unit Unit1;
    
    interface
    
    uses
      Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
      ExtCtrls, StdCtrls;
    
    type
      TMainPanel = class(ExtCtrls.TCustomPanel)
      private
        procedure CMControlListChange(var Message: TCMControlListChange); message CM_CONTROLLISTCHANGE;
      end;
    
      TForm1 = class(TForm)
        Button1: TButton;
        Memo1: TMemo;
        procedure Button1Click(Sender: TObject);
      private
      public
        MainPanel: TMainPanel;
      end;
    
    var
      Form1: TForm1;
    
    implementation
    
    {$R *.DFM}
    
    procedure TMainPanel.CMControlListChange(var Message: TCMControlListChange);
    begin
      if Message.Inserting then
      begin
        Form1.Memo1.Lines.Add('TMainPanel.CMControlListChange: Inserting ' + Message.Control.ClassName);
        // Parent is always nil
        if Message.Control.Parent = nil then Form1.Memo1.Lines.Add('*** Parent=nil');
      end;
      inherited;
    end;
    
    procedure TForm1.Button1Click(Sender: TObject);
    var
      ChildPanel: TPanel;
      ChildButton: TButton;
    begin
      FreeAndNil(MainPanel);
    
      MainPanel := TMainPanel.Create(Self);
      MainPanel.SetBounds(0, 0, 200, 200);
      MainPanel.Parent := Self;
    
      ChildPanel := TPanel.Create(Self);
      ChildPanel.Parent := MainPanel;
    
      ChildButton := TButton.Create(Self);
      ChildButton.Parent := ChildPanel; // Why do I get CM_CONTROLLISTCHANGE in "MainPanel"?
    end;
    
    end.
    

    DFM公司

    object Form1: TForm1
      Left = 192
      Top = 114
      Width = 685
      Height = 275
      Caption = 'Form1'
      Color = clBtnFace
      Font.Charset = DEFAULT_CHARSET
      Font.Color = clWindowText
      Font.Height = -11
      Font.Name = 'MS Shell Dlg 2'
      Font.Style = []
      OldCreateOrder = False
      PixelsPerInch = 96
      TextHeight = 13
      object Button1: TButton
        Left = 592
        Top = 8
        Width = 75
        Height = 25
        Caption = 'Button1'
        TabOrder = 0
        OnClick = Button1Click
      end
      object Memo1: TMemo
        Left = 456
        Top = 40
        Width = 209
        Height = 193
        TabOrder = 1
      end
    end
    

    P、 S:我不知道这是否重要,但我在Delphi 5上。

    1 回复  |  直到 7 年前
        1
  •  4
  •   David Heffernan    7 年前

    使用调试器实际上很容易回答这个问题。你本可以自己做的。启用调试DCU并在 if TMainPanel.CMControlListChange .

    TMainPanel.CMControlListChange((45100, $22420EC, True, 0))
    TControl.WndProc((45100, 35922156, 1, 0, 8428, 548, 1, 0, 0, 0))
    TWinControl.WndProc((45100, 35922156, 1, 0, 8428, 548, 1, 0, 0, 0))
    TWinControl.CMControlListChange((45100, 35922156, 1, 0, 8428, 548, 1, 0, 0, 0))
    TControl.WndProc((45100, 35922156, 1, 0, 8428, 548, 1, 0, 0, 0))
    TWinControl.WndProc((45100, 35922156, 1, 0, 8428, 548, 1, 0, 0, 0))
    TControl.Perform(45100,35922156,1)
    TWinControl.InsertControl($22420EC)
    TControl.SetParent($2243DD4)
    TForm1.Button1Click(???)
    

    此时,我们可以通过双击每个项来简单地检查调用堆栈。我会从 TForm1.Button1Click 这证实了我们确实在回应 ChildButton.Parent := ChildPanel .你的工作一直在上升。

    我们有两个问题 TWinControl.InsertControl

    Perform(CM_CONTROLLISTCHANGE, Integer(AControl), Integer(True));
    

    AControl Self 是子面板。让我们继续,直到 TWinControl.CMControlListChange .现在,这是处理信息的地方,我们仍然有

    procedure TWinControl.CMControlListChange(var Message: TMessage);
    begin
      if FParent <> nil then FParent.WindowProc(Message);
    end;
    

    这就是谜题的答案。VCL将消息传播到父链上。然后,该调用将到达调用堆栈的顶部, TMainPanel.CMControlListChange 哪里 现在是主面板 FParent TWinControl.CMControlListChange

    我知道我可以简单地指出 这将直接回答这个问题。但我真的想指出,通过相对简单的调试,这些问题很容易解决。