Construção de Componentes II
Neste artigo vamos trabalhar num componente que possa atender esse tipo de requisito.
Dominar o conceito de polimorfismo do paradigma OO, também discutido em artigos anteriores (meus e do Felipe).
Relembrando, o que colocamos no artigo Construção de Componentes I, devemos construir um componente sempre a partir de um outro pré-existente, seja ele extendido direto da VCL ou extendido de outro construído por você (recursivamente). Uma consideração é extremamente importante: Qual a classe hierarquicamente mais alta (mais abstrata), possuidora das características essenciais do objeto que pretendo construir? Essa será a classe base, da qual herdará o seu componente. Quando você souber responder essa pergunta com precisão, estará um passo na frente para ser um bom construtor de componentes. Um pré-requisito fundamental para isso, é conhecer bem a VCL e ter sempre por perto uma boa referência para consultar. O Help do Delphi não pode faltar neste contexto (O Diagrama da VCL também é bem legal).
O que posso adiantar que invariavelmente, num universo de coisas mais simples, corriqueiras, o feijão com arroz do dia-a-dia, o componente que você pretende construir estará dentro de três grupos bases de classes:
1- TWinControl:
Classe base para todo componente baseado em janela. Ou seja, a idéia de janelas que o
SO Windows transmite. As características mais marcantes dessa classe são:
1.1 Pode receber foco.
1.2 Obter mensagens do Windows.
1.3 Pode trabalhar com Handles de janelas. Em suma, você pode ficar moito próximo as
APIS do Windows. Finalizando, para trabalhar com essa categoria de classes você
deverá herdar seu componente de TCustomControl.
2- TGraphicControl:
Classe base para os componentes visuais que não possuem handle do Windows. Característica que
pode ser bastante preferível quando você deseja economizar recursos do SO. Em contraste com a
classe TWinControl , TGraphicControl não recebe foco, tão pouco pode trabalhar com mensagens do
Windows. Para você criar um componente gráfico desta categoria de classes deverá extender seu
componente de TCustomControl.
3 – TComponent:
Esta é a superclasse de todos os componentes. Inclusive os “controles” (“TGenecosControls”, digamos) .
Entretanto, quando o componente que você desejar criar não possuir características visuais essa classe
poderá ser ideal para atender sua necessidade. TComponent é a classe mais alta da VCL a possuir propriedades
(Properties). Nela surge a propriedade “Name”, por exemplo. Outro ponto importante é que nela surge o conceito
de Owner, cujo a importância falaremos um pouco adiante. A classe TComponent extende de TPersistent e apresentam
importantes características:
3.1 Integração com IDE:
A habilidade para serem adicionados em uma paleta de componentes da IDE.
Capaz de serem manipulados em tempo de projeto (design time).
3.2 Proprietariedade (Ownership):
A habilidade para administrar outros componentes. Se um componente A possui
o componente B, então A é responsável por destruir B, quando A é destruído.
Isso é muito importe e nos remete ao que foi discutido no artigo sobre o uso
do método “Free” ou “Destroy”. Lembre-se que quando estamos trabalhando com
uma variável cujo tipo é uma classe, obrigatoriamente estaremos alocando memória
dinamicamente. Portanto, somos responsáveis por gerenciar a liberação da mesma.
3.3 Streaming e Filing:
características de persistência herdadas de TPersistent.
3.4 Suporte a COM:
Podem ser convertidos em controles de ActiveX, objetos COM do Windows.
Os TComponents podem ser encapsulados por objetos COM.

Como no mundo real, os componentes existentes, em muitos casos, são objetos compostos por um conjunto de outros objetos. Da mesma forma, posso aproveitar essa idéia para montar o meu temporizador com display. O qual chamarei de ZNDisplayTimer. Sendo fiel a minha linha de raciocínio, vou compor o ZNDisplayTimer por dois componentes, inicialmente: Um Timer (TTimer), e um Label (TLabel).
Vou aproveitar este artigo para experimentarmos esse efeito na prática, o nosso componente será capaz de, em tempo de projeto exibir o tempo restante para ele executar uma ação.

type
TZNDisplayTimerUltimate = class(TCustomLabel)
private
{ Private declarations }
protected
{ Protected declarations }
public
{ Public declarations }
published
property Align;
property Alignment;
property Color;
property Font;
property ParentColor;
property ParentFont;
property ParentShowHint;
property PopupMenu;
property ShowHint;
property Transparent;
property Visible;
end;
type
TZNDisplayTimerUltimate = class(TCustomLabel)
private
{ Private declarations }
protected
{ Protected declarations }
public
{ Public declarations }
published
property Align;
property Alignment;
property Color;
property Font;
property ParentColor;
property ParentFont;
property ParentShowHint;
property PopupMenu;
property ShowHint;
property Transparent;
property Visible;
(* Acrescente as linhas abaixo e pressione “Ctrl + Shift + C *)
property ResetTimeValue: Integer;
property CurrentTimeValue: Integer FCurrentTimeValue default 0;
end
O Delphi vai auto-completar a codificação das propriedades novas.
.
.
.
property ShowHint;
property Transparent;
property Visible;
property ResetTimeValue: Integer read FResetTimeValue write SetResetTimeValue;
property CurrentTimeValue: Integer read FCurrentTimeValue default 0;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('EstacaoZn', [TZNDisplayTimerUltimate]);
end;
{ TZNDisplayTimerUltimate }
procedure TZNDisplayTimerUltimate.SetResetTimeValue(const Value: Integer);
begin
FResetTimeValue := Value;
end;
Esse encapsulamento é muito importante, pois somente mediante a manipulação do componente da forma que planejei, conseguirei garantir, em qualquer circunstancia, não importando o programador, o comportamento adequado para o nosso componente. Entenda que embora este componente tenha características de um temporizador, ou melhor, conforme especifiquei no inicio do artigo, seja de fato um, não quero deixar sombra de dúvida que ele não é, em essência, igual ao TTimer da VCL. Ele especializa um TTimer, e possui uma finalidade específica, bem diferente do seu progenitor. Veja o trecho abaixo:
type
TZNDisplayTimerUltimate = class(TCustomLabel)
private
{ Private declarations }
FResetTimeValue: Integer;
FCurrentTimeValue: Integer;
procedure SetResetTimeValue(const Value: Integer);
protected
FTimer: TTimer;
public
{ Public declarations }
published
property Align;
property Alignment;
property Color;
property Font;
property ParentColor;
property ParentFont;
property ParentShowHint;
property PopupMenu;
property ShowHint;
property Transparent;
property Visible;
property ResetTimeValue: Integer read FResetTimeValue write SetResetTimeValue;
property CurrentTimeValue: Integer read FCurrentTimeValue default 0;
(*As linhas abaixo implementam a interface com o componente FTimer. Após digitar pressione “Ctrl + Shift + C” *)
property EnabledTimer: Boolean read GetEnabledTimer write SetEnabledTimer default False;
property Interval: Integer read GetInterval write SetInterval;
end;
type
TZNDisplayTimerUltimate = class(TCustomLabel)
private FResetTimeValue: Integer;
FCurrentTimeValue: Integer;
procedure SetResetTimeValue(const Value: Integer);
{ Private declarations }
protected
FTimer: TTimer;
function GetEnabledTimer: Boolean; virtual;
function GetInterval: Integer; virtual;
procedure SetEnabledTimer(const Value: Boolean); virtual;
procedure SetInterval(const Value: Integer); virtual;
public
{ Public declarations }
published
property Align;
property Alignment;
property Color;
property Font;
property ParentColor;
property ParentFont;
property ParentShowHint;
property PopupMenu;
property ShowHint;
property Transparent;
property Visible;
property ResetTimeValue: Integer read FResetTimeValue write SetResetTimeValue;
property CurrentTimeValue: Integer read FCurrentTimeValue default 0;
property EnabledTimer: Boolean read GetEnabledTimer write SetEnabledTimer default False;
property Interval: Integer read GetInterval write SetInterval;
end;
Em seguida vamos codificar os corpo dos métodos que acabamos de criar.
1 - GetEnabledTimer
function TZNDisplayTimerUltimate.GetEnabledTimer: Boolean;
begin
Result := FTimer.Enabled;
end;
2 - GetInterval
function TZNDisplayTimerUltimate.GetInterval: Integer;
begin
Result := FTimer.Interval;
end;
3 - SetEnabledTimer
procedure TZNDisplayTimerUltimate.SetEnabledTimer(const Value: Boolean);
begin
FTimer.Enabled := Value;
end;
4 - SetInterval
procedure TZNDisplayTimerUltimate.SetInterval(const Value: Integer);
begin
FTimer.Interval := Value;
end;
protected
procedure UpdateClock(Sender: TObject);
procedure UpdateCurrentTime(const Multiplicador: Integer);
public
(* Digite o construtor e pressione “Ctrl + Shift + C” *)
constructor Create(AOwner: TComponent); override;
constructor TZNDisplayTimerUltimate.Create(AOwner: TComponent);
begin
(* evocando o método do ancestral *)
inherited Create(AOwner);
(* Instanciando o componente interno*)
FTimer := TTimer.Create(Self);
(* Configurando a Classe com valores default *)
FTimer.Name := 'ZnDisplayTimer';
FTimer.Enabled := False;
FCurrentTimeValue := 0;
FResetTimeValue := 5;
end;
O componente deverá possuir duas funcionalidades. Elas deverão ser sincronizadas e dependentes:
1° Ação- De tempos em tempos disparar uma ação, sendo que, o intervalo entre as ações deve ser constante podendo ser, dinamicamente, definido pelo usuário do sistema. Implementaremos ela em forma de evento e a propriedade que definirá esse intervalo é a “ResetTimeValue”.
2° Ação – Exibir o tempo restante para a próxima ação disparada. A propriedade “Caption” que vamos absorver da classe TCustomLabel.
type
(* Primeiro codifique o evento *)
TExecuteEvent = procedure(Sender: TObject) of object;
TZNDisplayTimerUltimate = class(TCustomLabel)
private
FTimer: TTimer;
FResetTimeValue: Integer;
FCurrentTimeValue: Integer;
FResetMod: TResetMod;
.
.
.
constructor Create(AOwner: TComponent); override;
published
.
.
.
property Align;
property Alignment;
(*Em seguida, digite a propriedade aqui, assim ela seja listada no
Object Inspector *)
property OnExecute: TExecuteEvent read FOnExecute write FOnExecute;
Ao pressionar “Ctrl + Shift + C” será criado o campo privado “FOnExecute”, veja abaixo:
type
TExecuteEvent = procedure(Sender: TObject) of object;
TZNDisplayTimerUltimate = class(TCustomLabel)
private
FResetTimeValue: Integer;
FCurrentTimeValue: Integer;
FOnExecute: TExecuteEvent;
procedure SetResetTimeValue(const Value: Integer);
{ Private declarations }
protected
FTimer: TTimer;
.
.
O Evento “OnExecute” esta pronto, a primeira funcionalidade está implementada. Agora, vamos tratar da exibição e do sincronismo ente a contagem do tempo e a execução do evento.
type
TExecuteEvent = procedure(Sender: TObject) of object;
TZNDisplayTimerUltimate = class(TCustomLabel)
private
FResetTimeValue: Integer;
FCurrentTimeValue: Integer;
FOnExecute: TExecuteEvent;
procedure SetResetTimeValue(const Value: Integer);
protected
FTimer: TTimer;
function GetEnabledTimer: Boolean; virtual;
function GetInterval: Integer; virtual;
procedure SetEnabledTimer(const Value: Boolean); virtual;
procedure SetInterval(const Value: Integer); virtual;
procedure UpdateClock(Sender: TObject); virtual;
procedure UpdateCurrentTime(const Multiplicador: Integer); virtual;
public
constructor Create(AOwner: TComponent); override;
published
.
.
.
implementation
uses
DateUtils;
Por falar em uses, veja como deve estar a seção “uses” da “interface” .
interface
uses
SysUtils, Classes, Controls, StdCtrls, ExtCtrls;
.
.
.
Codificando o método UpdateCurrentTime.
procedure TZNDisplayTimerUltimate.UpdateCurrentTime(
const Multiplicador: Integer);
var
AuxSecond: Integer;
begin
AuxSecond := SecondOf(Time);
if ((AuxSecond * Multiplicador) mod FResetTimeValue = 0) then
FCurrentTimeValue := 1
else
FCurrentTimeValue := FCurrentTimeValue + 1;
end;
No método seguinte faremos a chamada ao “UpdateCurrentTime”, atualizaremos o “Caption” para assim exibir o valor de “FCurrentTimeValue” em seguida. A cada vez que ele for resetado dispararemos o evento “OnExecute”.
procedure TZNDisplayTimerUltimate.UpdateClock(Sender: TObject);
begin
(*Atualizando o tempo corrente*)
UpdateCurrentTime(1);
(*Atribuindo o valor atualizado na propriedade de output do componente*)
Self.Caption := IntToStr(FCurrentTimeValue);
(*Caso o campo tenha seido resetado dispara o evento OnExecute*)
if (FCurrentTimeValue = 1) then
if Assigned(FOnExecute) then FOnExecute(Self);
end;
Agora voltaremos ao construtor para a última configuração.
constructor TZNDisplayTimerUltimate.Create(AOwner: TComponent);
begin
(* evocando o método do ancestral *)
inherited Create(AOwner);
(* Instanciando o componente interno*)
FTimer := TTimer.Create(Self);
(* Configurando a Classe com valores default *)
FTimer.Name := 'ZnDisplayTimer';
FTimer.Enabled := False;
FCurrentTimeValue := 0;
FResetTimeValue := 5;
(*Ao evento onTimer atribuimos o método que possui
a principal funcionalidade do nosso componente*)
FTimer.OnTimer := UpdateClock;
end;
Agora que nosso componente está pronto o adicione a package que criamos no artigo anterior sobre construção de componentes. Compile a package e re-instale.

Clik em "Browse..." para selecionar a unit do seu component (ZNDisplayTimerUltimate.pas), em seguida click "OK"

É chegado o momento ... vamos fazer um pequeno programa para testarmos nossa criação.

Testando o componente que criamos
Inicie uma nova aplicação, antes de qualquer coisa feche o projeto da package. No menu do Delphi: File ►Close All. Em seguida, File ► New ►Application.
Adicione o componente ZNDisplayTimerUltimate

Configure a propriedade fonte do ZNDisplayTimerUltimate para um tamanho maior e set sua propriedade “Enabled”, no Object Inspector para True.
e um BitBtn (palheta additional).

No Evento OnExecute do ZNDisplayTimerUltimate

digite .....
procedure TForm1.ZNDisplayTimerUltimate1Execute(Sender: TObject);
begin
ShowMessage('Bla!!!! ... Estação ZN ... Boladão')
end;
Adicione um SpinEdit (palheta Samples), no evento OnCreate do Form1 atribua valor para ele.
procedure TForm1.FormCreate(Sender: TObject);
begin
(*Vamos iniciar a aplicação com o Display desativado*)
ZNDisplayTimerUltimate1.EnabledTimer := False;
(*setando um valor inicial para o SpinEdit1*)
SpinEdit1.Value := 7;
end;
No evento OnClick do BitBtn1 vamos ativar o componente em tempo de execução.
procedure TForm1.BitBtn1Click(Sender: TObject);
begin
( * definindo um novo valor para o intervalo *)
ZNDisplayTimerUltimate1.ResetTimeValue := SpinEdit1.Value;
(*Controlando o ZNDisplay ...*)
ZNDisplayTimerUltimate1.EnabledTimer :=
not (ZNDisplayTimerUltimate1.EnabledTimer);
end;
Execute a aplicação e teste .....
Segue o código completo da aplicação exemplo.
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Buttons, ZNDisplayTimerUltimate, Spin;
type
TForm1 = class(TForm)
ZNDisplayTimerUltimate1: TZNDisplayTimerUltimate;
BitBtn1: TBitBtn;
SpinEdit1: TSpinEdit;
procedure ZNDisplayTimerUltimate1Execute(Sender: TObject);
procedure BitBtn1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.ZNDisplayTimerUltimate1Execute(Sender: TObject);
begin
ShowMessage('Bla!!!! ... Estação ZN ... Boladão')
end;
procedure TForm1.BitBtn1Click(Sender: TObject);
begin
ZNDisplayTimerUltimate1.ResetTimeValue := SpinEdit1.Value;
ZNDisplayTimerUltimate1.EnabledTimer :=
not (ZNDisplayTimerUltimate1.EnabledTimer);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
(*Vamos iniciar a aplicação com o Display desativado*)
ZNDisplayTimerUltimate1.EnabledTimer := False;
(*setando um valor inicial para o SpinEdit1*)
SpinEdit1.Value := 7;
end;
end.
O Código completo do componente ...
unit ZNDisplayTimerUltimate;
interface
uses
SysUtils, Classes, Controls, StdCtrls, ExtCtrls;
type
TExecuteEvent = procedure(Sender: TObject) of object;
TZNDisplayTimerUltimate = class(TCustomLabel)
private
FResetTimeValue: Integer;
FCurrentTimeValue: Integer;
FOnExecute: TExecuteEvent;
procedure SetResetTimeValue(const Value: Integer);
{ Private declarations }
protected
FTimer: TTimer;
function GetEnabledTimer: Boolean; virtual;
function GetInterval: Integer; virtual;
procedure SetEnabledTimer(const Value: Boolean); virtual;
procedure SetInterval(const Value: Integer); virtual;
procedure UpdateClock(Sender: TObject); virtual;
procedure UpdateCurrentTime(const Multiplicador: Integer); virtual;
public
constructor Create(AOwner: TComponent); override;
published
property Align;
property Alignment;
property Color;
property Font;
property ParentColor;
property ParentFont;
property ParentShowHint;
property PopupMenu;
property ShowHint;
property Transparent;
property Visible;
property ResetTimeValue: Integer read FResetTimeValue write SetResetTimeValue;
property CurrentTimeValue: Integer read FCurrentTimeValue default 0;
property EnabledTimer: Boolean read GetEnabledTimer write SetEnabledTimer default False;
property Interval: Integer read GetInterval write SetInterval;
property OnExecute: TExecuteEvent read FOnExecute write FOnExecute;
end;
procedure Register;
implementation
uses
DateUtils;
procedure Register;
begin
RegisterComponents('EstacaoZn', [TZNDisplayTimerUltimate]);
end;
{ TZNDisplayTimerUltimate }
constructor TZNDisplayTimerUltimate.Create(AOwner: TComponent);
begin
(* evocando o método do ancestral *)
inherited Create(AOwner);
(* Instanciando o componente interno*)
FTimer := TTimer.Create(Self);
(* Configurando a Classe com valores default *)
FTimer.Name := 'ZnDisplayTimer';
FTimer.Enabled := False;
FCurrentTimeValue := 0;
FResetTimeValue := 5;
(*Ao evento onTimer atribuimos o método que possui
a principal funcionalidade do nosso componente*)
FTimer.OnTimer := UpdateClock;
end;
function TZNDisplayTimerUltimate.GetEnabledTimer: Boolean;
begin
Result := FTimer.Enabled;
end;
function TZNDisplayTimerUltimate.GetInterval: Integer;
begin
Result := FTimer.Interval;
end;
procedure TZNDisplayTimerUltimate.SetEnabledTimer(const Value: Boolean);
begin
FTimer.Enabled := Value;
end;
procedure TZNDisplayTimerUltimate.SetInterval(const Value: Integer);
begin
FTimer.Interval := Value;
end;
procedure TZNDisplayTimerUltimate.SetResetTimeValue(const Value: Integer);
begin
FResetTimeValue := Value;
end;
procedure TZNDisplayTimerUltimate.UpdateClock(Sender: TObject);
begin
UpdateCurrentTime(1);
Self.Caption := IntToStr(FCurrentTimeValue);
if FCurrentTimeValue = 1 then
if Assigned(FOnExecute) then FOnExecute(Self);
end;
procedure TZNDisplayTimerUltimate.UpdateCurrentTime(
const Multiplicador: Integer);
var
AuxSecond: Integer;
begin
AuxSecond := SecondOf(Time);
if ((AuxSecond * Multiplicador) mod FResetTimeValue = 0) then
FCurrentTimeValue := 1
else
FCurrentTimeValue := FCurrentTimeValue + 1;
end;
end.
Downloads nos artigos do Bruno (Cod Gear):Artigo completo (View Full Post)
Mini Book About Components' Creation: http://cc.borland.com/Item/24302
Apostila sobre Criação de Componentes Passo a Passo by Bruno Lichot:
Apostila sobre Criação de Componentes Passo a Passo com exemplos e código fonte inclusos.
Mostra como Criar Compoenentes Simples e Componantes DBWare para Banco de Dados.
e-mail: bruno.lichot@gmail.com
lista: nddv@yahoogrupos.com.br
Atenciosamente,
Bruno Lichot
CodeGear Team Brazil
Amigos, com o tempo curto ... foi o que deu pra fazer dessa vez ...
[]’s ...
















