6

I need to create my own panel which will be derived from TCustomPanel - Here I will be doing my own custom painting on the panel such as drawing a header at the top.

One of the requirements I need is that I need to be able to draw two different colors depending on whether my panel has focus or not, I also need a way of knowing whether or not a child control inside my panel has focus too - of course though there is no knowing of what the child contents could be.

So for example, if the panel (or any child controls inside the panel) has focus, then the color could be clSkyBlue. If the panel (or none of the child controls inside the panel) does not have focus, then the color could be clWhite.

Here is a quick layout of the custom panel: (to keep it simple for the example there is no header drawing, just basic color changing)

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;

The first problem I ran into is making the panel focusable, I'm sure there will be a proper solution that I have overlooked but in this case I used a dirty trick by intercepting the WM_LBUTTONDOWN message and making the panel focused. This of course also denies the other requirement I mentioned before where child controls also decide what color the panel should be depending on whether or not the panel or it's child controls are focused.

This is what happens when I click on my panel:

enter image description here

This is what happens when clicking on the child control:

enter image description here

As you can see the panel turned white, but I need it to turn to clSkyBlue to indicate that the panel or it's child contents has focus, which should look like:

enter image description here

So, what is the solution to correctly identify whether or not my custom control or it's child controls has the focus or not?

I had thought about iterating through each child control inside the panel to determine if it had focus or not but I am not sure how I would fire such an event in the first place as clicking directly on a child control would surely ignore any messages that the custom panel is waiting to intercept.

Craig
  • 1,874
  • 13
  • 41

2 Answers2

8

All TWinControl descendants are already focusable and so is your TCustomPanel descendant, too. There's no need to do any additional work in this regard.

What is not done automatically for you (and I think you want to do) is to show the focus state of your component visually. One possible way to do this is by handling CM_ENTER and CM_EXIT messages:

  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;
Ondrej Kelle
  • 36,941
  • 2
  • 65
  • 128
  • 1
    This is the right way to detect focus changes on the panel itself. However, the panel loses focus when the edit gains focus. Only one windowed control can be focused at a time. – Remy Lebeau Jul 17 '15 at 16:14
  • @Remy Of course, obviously. Do you think that's a problem? – Ondrej Kelle Jul 17 '15 at 16:17
  • 1
    Yes, because that is the problem Craig is trying to solve - to show the panel in a focused state when itself is not focused but a child control is. – Remy Lebeau Jul 17 '15 at 16:19
  • Maybe I'm missing something but I think that's exactly what CM_ENTER and CM_EXIT represent. When focus moves from non-child to child the parent receives CM_ENTER. When the focus moves to a non-child control it receives CM_EXIT. – Ondrej Kelle Jul 17 '15 at 16:22
  • 2
    @TOndrej: yes, that is true. I just tried it and it worked. If some other window has focus, thus neither the panel nor a child has focus yet, and then the child gains focus directly, the panel DOES receive `CM_ENTER`. And when the child loses focus to a non-child, the panel DOES receive `CM_EXIT`. I did not think those messages were bubbled up the child/parent chain, but it looks like they are. – Remy Lebeau Jul 17 '15 at 17:10
  • @TOndrej this is excellent, works like a charm thank you very much. – Craig Jul 17 '15 at 17:48
0

An easy way is to create your own Edit instead of you TPanel.

Your derived Edit may control the OnEnter(); and OnExit() events and chance its Parent colour.

As a can see you know how to intercept events so it will be an easier approach.

Leo Melo
  • 196
  • 5
  • If you want to allow the user to drop any control onto the panel, deriving a new edit is not an option. However, the panel could detect when a new control is added to it and then subclass the control to intercept its focus messages. – Remy Lebeau Jul 17 '15 at 16:16
  • As Remy said I have no idea what control the user could potentially add to the panel as a child so creating my own TEdit is not feasible, I actually only used TEdit as an example to illustrate my problem. – Craig Jul 17 '15 at 16:43
  • So @TOndrej solution is what you are searching for. – Leo Melo Jul 17 '15 at 17:28