As the title says, I'd like a component (say, a label
) to be notified when it's parent (say, a panel
) receives and loses focus. I wandered a bit in Delphi source, in hope of using TControl.Notify
, but it's only used to notify child controls of some property changes like font and color. Any suggestions?
Asked
Active
Viewed 2,473 times
6

iMan Biglari
- 4,674
- 1
- 38
- 83
-
3Is this for your own control (label)? On a form you can simply write OnEnter, OnExit event handlers for the parent (panel). – Ondrej Kelle Sep 15 '12 at 15:24
-
What are you trying to actually DO? It sounds like you want to hack a kludge around a bug. But if you elucidate someone might be able to help you. – Warren P Sep 15 '12 at 15:29
-
@WarrenP I'm using `JvGradientHeader` on a lot of my panels as captions, and I'd like to highlight the focused panel's title. – iMan Biglari Sep 15 '12 at 15:32
-
2Do panels actually receive focus? – David Heffernan Sep 15 '12 at 16:55
-
2Assign a single set of OnEnter and OnExit event handlers to all of the panels. Inside of the handlers, you can loop through the Sender's children controls looking for a JvGradientHeader object and then update it as needed. – Remy Lebeau Sep 15 '12 at 18:07
-
2Another option would be to subclass each panel to catch the CM_FOCUSCHANGED message and then broadcast a custom-defined CM_PARENTFOCUSCHANGED message to the sender's children controls. You can then subclass the JvGradientHeader controls to catch that message. – Remy Lebeau Sep 15 '12 at 18:15
-
2This sounds like a good time to create a composite control that inherits from TPanel, and owns a JvGradientHeader. Then the Tpanel control exit and enter virtual methods can be overriden. Object oriented programming is your friend. – Warren P Sep 15 '12 at 19:11
-
@WarrenP Unfortunately not all my _containers_ are the same component. I found a component in the *TMS* component pack which highlights currently selected control. I'm gonna take a look at its source – iMan Biglari Sep 16 '12 at 07:17
-
@iManBiglari: Why not write a function that works the opposite of `TObject.Dispatch`? i.e.: send to child controls only. – Jay Sep 16 '12 at 07:53
-
@Jay because I don't want to modify the _container_ classes. As Remy and Warren mentioned above, I could easily create a descendant of `TPanel` and get the job done. But I'd rather have a _smart_ `JvGradientHeader` which can respond automatically. – iMan Biglari Sep 16 '12 at 08:30
-
1@David Panels receive focus if `TabStop` is true or when manually set, but that's not a requisite here. I think OP means that whenever a (nested) child control on the panel receives focus, his control should be signalled. (Note that `Panel.OnEnter` ís fired in that case, but `Panel.Focussed` remains false.) – NGLN Sep 16 '12 at 10:29
1 Answers
8
Whenever the active control in an application changes, a CM_FOCUSCHANGED
message is broadcast to all controls. Simply intercept it, and act accordingly.
Also, I assumed that by when it's parent (say, a panel) receives and loses focus you mean whenever a (nested) child control on that parent/panel receives or loses focus.
type
TLabel = class(StdCtrls.TLabel)
private
function HasCommonParent(AControl: TWinControl): Boolean;
procedure CMFocusChanged(var Message: TCMFocusChanged);
message CM_FOCUSCHANGED;
end;
procedure TLabel.CMFocusChanged(var Message: TCMFocusChanged);
const
FontStyles: array[Boolean] of TFontStyles = ([], [fsBold]);
begin
inherited;
Font.Style := FontStyles[HasCommonParent(Message.Sender)];
end;
function TLabel.HasCommonParent(AControl: TWinControl): Boolean;
begin
Result := False;
while AControl <> nil do
begin
if AControl = Parent then
begin
Result := True;
Break;
end;
AControl := AControl.Parent;
end;
end;
If you don't like to subclass TJvGradientHeader
, then it is possible to design this generically by the use of Screen.OnActiveControlChange
:
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
FHeaders: TList;
procedure ActiveControlChanged(Sender: TObject);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
FHeaders := TList.Create;
FHeaders.Add(Label1);
FHeaders.Add(Label2);
Screen.OnActiveControlChange := ActiveControlChanged;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
FHeaders.Free;
end;
function HasCommonParent(AControl: TWinControl; AMatch: TControl): Boolean;
begin
Result := False;
while AControl <> nil do
begin
if AControl = AMatch.Parent then
begin
Result := True;
Break;
end;
AControl := AControl.Parent;
end;
end;
procedure TForm1.ActiveControlChanged(Sender: TObject);
const
FontStyles: array[Boolean] of TFontStyles = ([], [fsBold]);
var
I: Integer;
begin
for I := 0 to FHeaders.Count - 1 do
TLabel(FHeaders[I]).Font.Style :=
FontStyles[HasCommonParent(Screen.ActiveControl, TLabel(FHeaders[I]))];
end;
Note that I chose TLabel
to demonstrate this works also for TControl
derivatives.

NGLN
- 43,011
- 8
- 105
- 200
-
1I learned something nice. I always thought that only forms can have _message handlers_. Thanks a lot :-) – iMan Biglari Sep 16 '12 at 10:40