如何正确响应自定义控件中的焦点消息?

Cra*_*aig 3 delphi delphi-xe7

我需要创建自己的面板,这个面板将派生自TCustomPanel- 在这里,我将在面板上进行自己的自定义绘画,例如在顶部绘制标题.

我需要的一个要求是,我需要能够根据我的面板是否有焦点来绘制两种不同的颜色,我还需要一种方法来了解我的面板中的儿童控件是否也有焦点 - 当然虽然不知道孩子的内容是什么.

因此,例如,如果面板(或面板内的任何子控件)具有焦点,则颜色可以是clSkyBlue.如果面板(或面板内没有任何子控件)没有焦点,那么颜色可能是clWhite.

以下是自定义面板的快速布局:(为了保持简单,示例中没有标题绘图,只是基本的颜色变化)

type
  TMyPanel = class(TCustomPanel)
  private
    //
  protected
    // procedure Paint; override;

    procedure WMMouseDown(var Message: TWMLButtonDown); message WM_LBUTTONDOWN;        
    procedure WMKillFocus(var Message: TWMKillFocus); message WM_KILLFOCUS;
    procedure WMSetFocus(var Message: TWMSetFocus); message WM_SETFOCUS;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  end;

constructor TMyPanel.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);

  Self.ParentBackground := False;
  Self.ParentColor := False;
end;

destructor TMyPanel.Destroy;
begin
  inherited Destroy;
end;

procedure TMyPanel.WMMouseDown(var Message: TWMLButtonDown);
begin
  Self.SetFocus;
end;

procedure TMyPanel.WMKillFocus(var Message: TWMKillFocus);
begin
  Self.Color := clWhite;
  Invalidate;
end;

procedure TMyPanel.WMSetFocus(var Message: TWMSetFocus);
begin
  Self.Color := clSkyBlue;
  Invalidate;
end;
Run Code Online (Sandbox Code Playgroud)

我遇到的第一个问题是使面板可以集中,我确信会有一个我忽略的正确解决方案,但在这种情况下,我使用了一个肮脏的技巧,拦截WM_LBUTTONDOWN消息并使面板集中.这当然也否定了我之前提到的其他要求,其中儿童控制也决定了面板应该是什么颜色,这取决于面板或它的儿童控件是否集中.

当我点击我的面板时会发生这种情况:

在此输入图像描述

这是在单击子控件时发生的情况:

在此输入图像描述

正如您所看到的那样,面板变为白色,但我需要转向clSkyBlue以指示面板或其子内容具有焦点,这应该如下所示:

在此输入图像描述

那么,正确识别我的自定义控件或它的子控件是否具有焦点的解决方案是什么?

我曾想过迭代遍历面板内的每个子控件以确定它是否有焦点但是我不知道如何在第一时间触发这样的事件,因为直接点击子控件肯定会忽略任何消息自定义面板正在等待拦截.

Ond*_*lle 5

所有的TWinControl后代都已经成为焦点,你的TCustomPanel后代也是如此.在这方面没有必要做任何额外的工作.

你没有自动完成的工作(我想你想做的)是直观地显示组件的焦点状态.一种可能的方法是通过处理CM_ENTERCM_EXIT消息:

  TMyPanel = class(TCustomPanel)
  private
    procedure FocusChanged(Value: Boolean);
  protected
    procedure CMEnter(var Message: TCMEnter); message CM_ENTER;
    procedure CMExit(var Message: TCMExit); message CM_EXIT;
  end;

procedure TMyPanel.CMEnter(var Message: TCMEnter);
begin
  FocusChanged(True);
end;

procedure TMyPanel.CMExit(var Message: TCMExit);
begin
  FocusChanged(False);
end;

procedure TMyPanel.FocusChanged(Value: Boolean);
const
  Colors: array[Boolean] of TColor = (clWhite, clSkyBlue);
begin
  Color := Colors[Value];
end;
Run Code Online (Sandbox Code Playgroud)

  • @TOndrej:是的,确实如此。我刚刚尝试过并且成功了。如果其他某个窗口具有焦点,因此面板和子窗口都没有焦点,然后子窗口直接获得焦点,面板确实会收到“CM_ENTER”。当孩子失去焦点到非孩子时,面板会收到“CM_EXIT”。我不认为这些消息是在子/父链中冒泡的,但看起来确实如此。 (2认同)