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

How to correctly respond to focus messages in a custom control?

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

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

例如,如果面板(或面板内的任何 child 控件)有焦点,则颜色可以是 clSkyBlue。如果面板(或面板内 child 控件的 none)没有焦点,则颜色可能是 clWhite.

这里是自定义面板的快速布局:(为了简单起见,示例中没有 header 绘图,只有基本的颜色更改)

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;

我 运行 遇到的第一个问题是使面板可聚焦,我相信会有一个我忽略的正确解决方案,但在这种情况下,我使用了一个肮脏的技巧来拦截 WM_LBUTTONDOWN 消息并使面板聚焦。这当然也否定了我之前提到的其他要求,其中 child 控件还决定面板的颜色取决于面板或其 child 控件是否聚焦。

这是当我点击我的面板时发生的事情:

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

如您所见,面板变白了,但我需要它变成 clSkyBlue 以指示面板或其 child 内容具有焦点,它应该如下所示:

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

我曾考虑过遍历面板内的每个 child 控件以确定它是否具有焦点,但我不确定如何首先触发这样的事件,例如直接单击child 控件肯定会忽略自定义面板正在等待拦截的任何消息。

一个简单的方法是创建您自己的 Edit 而不是 TPanel。

您派生的 Edit 可能会控制 OnEnter();OnExit() 事件并有可能控制其父颜色。

正如您所见,您知道如何拦截事件,因此这将是一种更简单的方法。

所有 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;