I need to write a component which will register in self other components and will detect if one of the registered components receive focus.
For example for my component TFocusObserver
I am registering three objects.
FocusObserver.Register(MyMemo);
FocusObserver.Register(MyButton);
FocusObserver.Register(MyEdit);
And now if one of this components receives focus then FocusObserver
is firing up some notification event.
I was looking how to detect a focus change and have found that TScreen.OnActiveControlChange
is exactly what I need. So my component could hook up to this event. The problem is that more than one TFocusObserver
might exists or later in a future somoene else might want to use OnActiveControlChange
.
This is the time in which I would benefit from multicast event - it would solve my problem right away.
I was thinking how to solve this and I have currently two ideas:
TScreen
so it would provide one more event for me.OnActiveControlChange
and expose one multicast event for other objects.After a brief look at the sources I have no clear idea how to solve it by using first idea and the second idea has the drawback that someone can simply assign another method to OnActiveControlChange
and everything fill fall apart.
Will be grateful for some suggestions.
If your focusObserver class can be a descendant of TWinControl, than you can do this:
TFocusObserver = class( TWinControl )
procedure CMFocusChanged(var Message: TCMFocusChanged); message CM_FOCUSCHANGED;
end;
and
procedure TFocusObserver.CMFocusChanged(var Message: TCMFocusChanged);
var
LControl: TWinControl;
begin
LControl := TWinControl(Message.Sender);
if LControl <> nil then
begin
form1.Caption := lControl.Name;
end;
end;
Here the main idea is to watch CM_FOCUSCHANGED
.
Second approach:
When registering the Control you replace it's WindowProc
. Here's a little code snippet:
TRegisteredComp = class
private
fControl: TControl;
fowndproc: TWndMethod;
procedure HookWndProc(var Message: TMessage);
public
constructor Create( c: TControl );
destructor Destroy; override;
end;
TFocusObserver = class
private
l: TList;
public
constructor Create;
destructor Destroy; override;
procedure reg( c: TControl );
end;
and under implementation:
constructor TFocusObserver.Create;
begin
l := TList.Create;
end;
destructor TFocusObserver.Destroy;
var i: integer;
begin
for i := 0 to l.Count - 1 do
TRegisteredComp(l[i]).Free;
l.Free;
inherited;
end;
procedure TFocusObserver.reg( c: TControl );
var
rc: TRegisteredComp;
begin
rc := TRegisteredComp.Create( c );
l.Add( rc );
end;
constructor TRegisteredComp.Create(c: TControl);
begin
fControl := c;
fowndproc := c.WindowProc;
c.WindowProc := HookWndProc;
end;
destructor TRegisteredComp.Destroy;
begin
fControl.WindowProc := fowndproc;
inherited;
end;
procedure TRegisteredComp.HookWndProc(var Message: TMessage);
begin
if ( Message.Msg = CM_FOCUSCHANGED ) and
( TControl(Message.LParam) = fControl ) then
form1.ListBox1.Items.Add( 'focused: ' + fControl.Name );
fowndproc( Message );
end;
than just register the control you want to watch, example:
procedure TForm1.FormCreate(Sender: TObject);
var
i: Integer;
begin
fo := TFocusObserver.Create;
for i := 0 to ControlCount - 1 do
fo.reg( Controls[i] );
end;
How does it sound?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With