2013-01-15 3 views
8

Nel mio TComponent, c'è un punto in cui voglio ascoltare gli eventi dei tasti e intercettare la chiave ESC e gestirla nel mio componente, consumare/"mangiare" il tasto, in modo che ad esempio il modulo proprietario non lo gestisca a quel punto. Proprio come in TDragObject quando si avvia, trascinarlo e annullarlo premendo ESC.Come può il mio TComponent intercettare il tasto ESC e gestirlo?

Il problema è che TDragObject ha AllocateHWnd che viene notificato dal suo modulo proprietario con CN_KEYDOWN. Ma nessuno notifica il mio componente.

Devo sostituire il modulo WindowProc con il mio? Se sì, allora come farlo correttamente "dal libro" per così dire?


Giusto per essere chiari al 100%:

TMyComponent = class(TComponent) 

Ho fatto un piccolo test e sembra funzionare:

TMyComponent = class(TComponent) 
    private 
    FOldWindowProc: TWndMethod; 
    FParentForm: TCustomForm; 
    procedure FormWindowProc(var Message: TMessage); 
    public 
    constructor Create(AOwner: TComponent); override; 
    destructor Destroy; override;  
end; 

... 

constructor TMyComponent.Create(AOwner: TComponent); 
begin 
    if not (AOwner is TWinControl) then 
    raise Exception.Create('TMyComponent.Create: Owner must be a TWinControl'); 
    inherited Create(AOwner); 
    // hook parent form 
    FParentForm := GetParentForm(TWinControl(Owner)); 
    if Assigned(FParentForm) then 
    begin 
    FOldWindowProc := FParentForm.WindowProc; 
    FParentForm.WindowProc := FormWindowProc; 
    end; 
end; 

destructor TMyComponent.Destroy; 
begin 
    // unhook parent form 
    if Assigned(FParentForm) then 
    FParentForm.WindowProc := FOldWindowProc; 
    inherited; 
end; 

procedure TMyComponent.FormWindowProc(var Message: TMessage); 
begin 
    FOldWindowProc(Message); 
    if Message.Msg = CM_CHILDKEY then // CM_CHILDKEY -> CM_DIALOGKEY -> CM_DIALOGCHAR 
    begin 
    OutputDebugString('CM_CHILDKEY'); 
    if Message.WParam = VK_ESCAPE then 
    begin 
     Beep; 
     // do my stuff... 
     Message.Result := 1; // consume keystroke 
    end; 
    end; 
end; 

Mi chiedo se questo è il giusto/solo approccio.

+0

Il trascinamento implica un nuovo ciclo modale. Questa non è un'opzione per te. Difficile vedere come puoi farlo in modo pulito senza la collaborazione dell'host. –

+2

@DavidHeffernan, l'OP ha detto "come in TDragObject", è solo un esempio e presumo che l'OP voglia solo il tasto ESC, niente di più, niente di meno. ESC è un tasto di dialogo. Semplicemente non ho il minuto richiesto per cercare il codice/API/windows message adesso. –

+0

@Cosmin Un ciclo modale dell'operazione di trascinamento possiede e pompa la coda. E così può entrare in possesso delle pressioni dei tasti. Ma un componente su un modulo non ha quel lusso. Come proponi di entrare nel ciclo dei messaggi dell'app? –

risposta

4

Un modo potrebbe essere quello di creare un oggetto TApplicationEvents all'interno del componente, e quindi utilizzare il suo evento OnMessage a sbirciare i messaggi dalla coda principale messaggio di discussione, come sequenze di tasti, prima che il resto del VCL li elabora.

+1

I componenti possono farlo realisticamente? Cosa succede se l'app assegna OnMessage? Non sarà un conflitto? –

+3

'TApplicationEvents' è progettato come una classe multicasting. Più istanze ricevono gli stessi eventi. IOW, quando un singolo messaggio arriva in coda, tutti i gestori assegnati a 'TApplicationEvents.OnMessage' ottengono dibs, se tutti usano' TApplicationEvents', cioè. Se qualcuno usa direttamente "TApplication.OnMessage", allora "TApplicationEvents.OnMessage" può rompersi, e viceversa, sì. Non è un sistema perfetto, ma è meglio di niente. –

+1

Gli eventi 'OnMessage' di più componenti' TApplicationEvents' verranno attivati ​​nell'ordine inverso al momento della creazione di tali componenti. – NGLN