2012-06-25 20 views
5

Necesito escribir un componente que se registrará en otros componentes y detectará si uno de los componentes registrados recibe el foco.Método flexible de detección de cambio de control enfocado

Por ejemplo para mi componente TFocusObserver Estoy registrando tres objetos.

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

Y ahora, si uno de estos componentes recibe el foco entonces FocusObserver está disparando un poco de evento de notificación.

Estaba buscando cómo detectar un cambio de foco y he descubierto que TScreen.OnActiveControlChange es exactamente lo que necesito. Entonces mi componente podría conectarse a este evento. El problema es que puede existir más de un TFocusObserver o más adelante en un futuro, de lo contrario, es posible que desee utilizar OnActiveControlChange.

Este es el momento en el que me beneficiaría del evento de multidifusión; esto resolvería mi problema de inmediato.

Estaba pensando cómo solucionar esto y tengo actualmente dos ideas:

  1. Ampliación de alguna manera TScreen por lo que proporcionaría un evento más para mí.
  2. Introduzca un objeto intermedio que se conectará a OnActiveControlChange y expondrá un evento de multidifusión para otros objetos.

Después de un breve vistazo a las fuentes que no tienen una idea clara de cómo resolver mediante el uso de la primera idea y la segunda idea tiene el inconveniente de que alguien simplemente puede asignar otro método para OnActiveControlChange y todo terraplén desmoronarse.

Estaremos agradecidos por algunas sugerencias.

+0

Si dijiste que * podría existir más de un TFocusObserver * al mismo tiempo, ¿quisiste decir que todos deberían haber sido notificados acerca de esos cambios de foco o solo uno de ellos? – TLama

+0

@TLama todos ellos. – Wodzu

+0

¿Cuál es la clase base de TFocusObserver? – balazs

Respuesta

8

Si su clase focusObserver puede ser un descendiente de TWinControl, de lo que puede hacer esto:

TFocusObserver = class(TWinControl) 

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

y

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; 

Aquí la idea principal es ver CM_FOCUSCHANGED.

Segundo enfoque:

Al registrar el control que se sustituya la WindowProc.He aquí un pequeño fragmento de código:

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; 

y en fase de ejecución:

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; 

que simplemente registrar el control que desea ver, ejemplo:

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

¿cómo suena?

+0

Gracias, esta solución tiene dos inconvenientes: 1 - Debo colocar este componente en un formulario para que pueda capturar el cambio de enfoque. 2- Detectará el cambio de enfoque solo en la forma en que se encuentra. Sin embargo, si no sale nada mejor, lo aceptaré como una respuesta. – Wodzu

+0

segundo enfoque agregado. – balazs

+0

Agradezco su tiempo y el segundo enfoque me funciona. Gracias. – Wodzu

0

Puede recordar el valor de Screen.OnActiveControlChange justo antes de que su componente lo reemplace.

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

Luego, en xxx.MyOnActiveControlChange

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

    if Assigned(FOnActiveControlChange) then begin 

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

Pero esto sólo funciona si usted tiene el control de la aplicación, si alguien usa su componente y que él/ella tiene otros componentes que también utilizan OnActiveControlChange las cosas pueden salir mal

+0

Gracias Marck, como dije en mi pregunta: alguien más podría cambiar la asignación para TScreen.OnActiveControlChange. Entonces esto no funcionará ... – Wodzu

+0

Bueno, otra opción es escuchar mensajes CM_FOCUSCHANGED. – Marck

Cuestiones relacionadas