Flexible method for detecting focused control changes

I need to write a component that will register in its own other components and will detect that one of the registered components receives focus.

For example, for my component, TFocusObserverI register three objects.

FocusObserver.Register(MyMemo);
FocusObserver.Register(MyButton);
FocusObserver.Register(MyEdit);

And now, if one of these components receives focus, then it FocusObserverfires some kind of notification event.

I was looking for how to detect a change in focus, and found that TScreen.OnActiveControlChange- this is exactly what I need. Therefore, my component could connect to this event. The problem is that there may be more than one TFocusObserveror later, in the future, when others may use OnActiveControlChange.

This is the time at which I benefit from the multicast event - it will solve my problem right away.

I was thinking how to solve this, and I currently have two ideas:

  • The extension is somehow TScreen, so it will provide me with another event.
  • Imagine an intermediate object that connects to OnActiveControlChangeand outputs one multicast event for other objects.

After a brief look at the sources, I don’t have a clear idea of ​​how to solve this problem using the first idea, and the second idea has the disadvantage that someone can simply assign another method OnActiveControlChange, and the whole filling falls apart.

We will be grateful for some suggestions.

+5
source share
2 answers

focusObserver TWinControl, :

TFocusObserver = class( TWinControl )

  procedure CMFocusChanged(var Message: TCMFocusChanged); message CM_FOCUSCHANGED;
end;

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;

- CM_FOCUSCHANGED.

:

WindowProc. :

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;

:

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;

, , :

procedure TForm1.FormCreate(Sender: TObject);
var
  i: Integer;
begin
  fo := TFocusObserver.Create;
  for i := 0 to ControlCount - 1 do
    fo.reg( Controls[i] );
end;

?

+8

Screen.OnActiveControlChange , .

FOnActiveControlChange := Screen.OnActiveControlChange;
Screen.OnActiveControlChange = MyOnActiveControlChange;

xxx.MyOnActiveControlChange

begin
  // what you wanted to do here
  ...

  if Assigned( FOnActiveControlChange) then begin

    // Forward to previous subscriber.
    FOnActiveControlChange( Sender, ...);
end;

, , - , , OnActiveControlChange. .

0

All Articles