How to properly respond to focus messages in a custom control?
I need to create my own panel to be derived from TCustomPanel
. Here I will be doing my own painting in the panel, for example drawing the title 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 is focused or not, I also need a way to find out if a child control inside my panel also has focus - Of course although it is not known what the contents of the child might be.
So, for example, if the panel (or any child controls within the panel) has focus, then the color might be clSkyBlue
. If the panel (or none of the child controls inside the panel) has focus, then the color might be clWhite
.
Here's a quick layout for a custom panel: (to keep it simple, e.g. no header drawing, just a basic color change)
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 makes the panel attractive, I am sure there will be a proper solution that I missed, but in this case I used a dirty trick, intercepting the message WM_LBUTTONDOWN
and making the panel centered.This of course also negates another requirement. which I mentioned earlier, when the child controls also determine what color the panel should have, depends on whether the panel or child controls are focused.
This is what happens when I click on my panel:
This is what happens when the child control is clicked:
As you can see the panel turned white, but I need it to turn into clSkyBlue
to indicate that the panel or its child content has focus, which should look like this:
So what is the solution to correctly identify if my custom control or its child controls have focus or not?
I was thinking about iterating through each child control inside the panel to determine if it has focus or not, but I'm not sure how I could trigger an event like this in the first place, since clicking directly on the child control would certainly ignore would be any messages that the custom panel is expecting to be intercepted.
source to share
All descendants are TWinControl
already configurable, as well as your descendant TCustomPanel
. There is no need to do additional work in this regard.
What is not automatically done for you (and I think you want to do) is to visually display the focus state of your component. One possible way to do this is to process messages CM_ENTER
and CM_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;
source to share