Num outro documento falei dos Class Helpers, uma maneira de adicionar novas propriedade e métodos a uma classe já existente.
Estes permitem que todos os componentes de uma classe passem a ter essas novas propriedade e métodos sem ser necessário registar um novo componente.
No entanto, os Class Helpers não permitem que se adicionem novas variáveis à classe, visto que as variáveis têm de ser sempre definidas antes das propriedade e métodos, e quando criamos um Helper, o nosso código é adicionado ao fim das declarações da classe mãe.
Para isto, temos de criar uma sub-classe da primeira.
Mas isto trás-nos de novo ao problema de, ou se regista esta sub-classe como um novo componente para o podermos usar em DesignTime, ou criamos o componente em RunTime, perdendo a vantagem do RAD.
E aqui entram os Class Interceptors: Sub-Classes de uma classe já existente mas que usa o mesmo nome da classe mãe, para “enganar” o compilador.
A primeira regra de um Interceptor é então que tenha o mesmo nome da classe que queremos derivar.
A segunda regra é que a definição do Interceptor venha depois da definição da classe principal. Nomeadamente, se colocarmos o Interceptor na sua própria unit, devemos chamar essa unit depois de chamar a unit que contém o componente original.
Para exemplificar, vamos criar um componente derivado de um TButton, mas que tenha dois estados: Ligado e Desligado.
Ao clicar uma vez, o botão fica no estado Ligado. Ao clicar uma segunda vez, o botão volta ao estado normal.
Precisamos ainda de de uma propriedade que nos permita ligar ou desligar esta opção, por forma a podermos usar o TButton da forma tradicional sempre que quisermos.
unit ToogleButton; interface uses StdCtrls, Controls; type TButton = Class (StdCtrls.TButton) Private State: Boolean; // Memoriza o estado do botão (ligado ou desligado). O Default é desligado Toogle: Boolean; // Memoriza o tipo de botão: True = ToogleButton False = TButton tradicional procedure SwitchState (CheckedState:Boolean); // Alterna entre os estados Ligado e Desligado Public property Checked: Boolean read State write SwitchState; // Define em RunTime o estado do botão property ToogleButton: Boolean read Toogle write Toogle; // Define o tipo de botão, atribuindo um valor à variável Toogle procedure Click; Override; // Captura o evento OnClick do botão, e adiciona a alternancia entre estados ao código já exitente End; implementation procedure TButton.SwitchState(CheckedState:Boolean); begin State:=Not State; // Alterna o estado do botão // Define o aspecto visual do botão para o estado seleccionado if State then begin BevelKind:=bkFlat; BevelInner:=bvLowered; end else begin BevelKind:=bkNone; BevelInner:=bvRaised; end; end; procedure TButton.Click; begin if Toogle then SwitchState(Not State); // Se for um ToogleButton, alterna o estado actual inherited; // Resume o código existente no evento OnClick end; end.
Para utilizar, basta definir um TButton na nossa form, adicionar a unit ToogleButton à cláusula Uses e definir a propriedade ToogleButton do novo botão para True no OnCreate da form.
unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ToogleButton; // A unit ToogleButton é declarada depois da unit StdCtrls, que é a unit onde // está definida a classe TButton // Se fosse colocado ao contrário, o compilador iria dar erro, pois a classe // TButton assumida seria a original não o Interceptor type TForm1 = class(TForm) Button1: TButton; // Vai assumir a última instância encontrada da TButton, neste caso, o Interceptor Edit1: TEdit; procedure Button1Click(Sender: TObject); procedure FormCreate(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.Button1Click(Sender: TObject); // Ao executar o evento OnClick, o estado irá mudar automáticamente begin if Button1.Checked // Mostra o estado actual do botão, com um texto inserido no TEdit (apenas para teste) then Edit1.Text:='Ligado' else Edit1.Text:='Desligado'; end; procedure TForm1.FormCreate(Sender: TObject); begin Button1.ToogleButton:=True; // Activa a função ToogleButton do Interceptor end; end.