2012-06-25 5 views
5

Devo scrivere un componente che si registrerà in altri componenti e rileverà se uno dei componenti registrati riceve attenzione.Metodo flessibile per rilevare il cambio di controllo focalizzato

Ad esempio per il mio componente TFocusObserver Sto registrando tre oggetti.

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

E ora, se uno di questi componenti viene attivato quindi FocusObserver sta sparando un po 'evento di notifica.

Stavo cercando come rilevare un cambiamento di messa a fuoco e ho trovato che TScreen.OnActiveControlChange è esattamente quello che mi serve. Quindi il mio componente potrebbe collegarsi a questo evento. Il problema è che più di uno TFocusObserver potrebbe esistere o più tardi in un futuro somoene altrimenti potrebbe voler usare OnActiveControlChange.

Questo è il momento in cui vorrei trarre vantaggio dall'evento multicast: risolverebbe il mio problema immediatamente.

Stavo pensando come risolvere questo e ho attualmente due idee:

  1. Estensione in qualche modo TScreen avrebbe fornito un altro evento per me.
  2. Introdurre un oggetto intermedio che si collega a OnActiveControlChange ed esporre un evento multicast per altri oggetti.

Dopo un breve sguardo alle fonti che non hanno un'idea chiara di come risolverlo utilizzando prima idea e la seconda idea ha lo svantaggio che qualcuno può semplicemente assegnare un altro metodo per OnActiveControlChange e tutto fill cadere a pezzi.

Sarà grato per alcuni suggerimenti.

+0

Se hai detto * potrebbe esistere più di un TFocusObserver * in una sola volta, suppongo, volevi dire che tutti avrebbero dovuto essere informati su quei cambiamenti di messa a fuoco o solo su uno di essi? – TLama

+0

@TLama tutti loro. – Wodzu

+0

Qual è la classe base di TFocusObserver? – balazs

risposta

8

Se la classe focusObserver può essere un discendente di TWinControl, di quanto si può fare questo:

TFocusObserver = class(TWinControl) 

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

e

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; 

Qui l'idea principale è quello di guardare CM_FOCUSCHANGED.

Secondo approccio:

Quando si registra il controllo si sostituisce è WindowProc.Ecco un piccolo frammento di codice:

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; 

e in via di attuazione:

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; 

non solo registrare il controllo che si desidera vedere, ad esempio:

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

come suona?

+0

Grazie, questa soluzione ha due svantaggi: 1 - Devo posizionare questo componente su un modulo in modo che possa catturare il cambiamento di messa a fuoco. 2- Rileverà il cambio di messa a fuoco solo sulla forma su cui si trova. Tuttavia, se non succede nulla di meglio, lo accetterò come risposta. – Wodzu

+0

secondo approccio aggiunto. – balazs

+0

Apprezzo il tuo tempo e il secondo approccio funziona per me. Grazie. – Wodzu

0

Si potrebbe ricordare il valore di Screen.OnActiveControlChange appena prima che il componente lo sostituisca.

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

Poi nel xxx.MyOnActiveControlChange

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

    if Assigned(FOnActiveControlChange) then begin 

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

Ma questo funziona solo se sei in controllo della domanda, se qualcun altro usa il componente e lui/lei ha altri componenti che utilizzano anche OnActiveControlChange le cose potrebbero andare storte.

+0

Grazie Marck, come ho affermato nella mia domanda - qualcun altro potrebbe cambiare l'incarico per TScreen.OnActiveControlChange. Quindi questo non funzionerà ... – Wodzu

+0

Bene, un'altra opzione è ascoltare i messaggi CM_FOCUSCHANGED. – Marck