Lazarus – Herança de form e criação de componentes em run-time
Autor: Professor Carlos | Publicado: 2010-10-07T16:45:00-04:00

Neste post vamos mostrar como criar um formulário de cadastro, independente da  tabela do banco de dados. A idéia é que, ao abrir o formulário, um trecho de  código identifica cada campo da tabela, instancia um TDBEdit para esse campo e  ainda o identifica com um TLabel. Além do mais esse código será implementado em  um formulário pai. Cada formulário de cadastro será derivado – ou herdado –  deste. Nosso demo terá um form principal que chamará o form de cadastro, o form  pai onde todas as ações irão acontecer, um form de cadastro derivado e um  Data Module.
No Data Module colocamos os componentes de acesso ao banco de  dados. Para isso iremos utilizar ZeosLib com banco de dados  PostgreSQL. Coloque então um TZConnection para conectar ao seu  banco de dados e o configure adequadamente. Coloque também uma  TZQuery e um TZUpdateSQL. Consulte o post Lazarus – Conectando PostgreSQL com ZeosLib se tiver dúvida de  como fazer essas definições. Não coloque TDataSource no  Data Module. Iremos colocá-lo no form pai.

O form Pai

Já existe um form na aplicação. Vamos inserir um novo form e deixar esse primeiro para ser o principal. No novo form vamos criar a propriedade Query conforme mostra o código abaixo. Inclua a unit ZDataset na cláusula uses.

private
    { private declarations }
    fQuery: TZQuery;
    function GetQuery: TZQuery;
    procedure SetQuery(NewQuery: TZQuery);
  public
    { public declarations }
    property Query: TZQuery read GetQuery write SetQuery;
  end;

var
  frmDinamico: TfrmDinamico;
  vetorCampos: array of TDBEdit;
  vetorLabel: array of TLabel;

implementation

{$R *.lfm}

procedure TfrmDinamico.FormShow(Sender: TObject);
var
   Coluna : integer;
   NomeColuna : TDBEdit;
   NomeLabel : TLabel;
   Topo : integer;
begin
   Topo := 65;
   dsTabela.DataSet := fQuery;
   fQuery.Open;
   SetLength(vetorCampos, fQuery.FieldCount);
   SetLength(vetorLabel, fQuery.FieldCount);
   for Coluna := 0 to fQuery.FieldCount - 1 do
   begin
     NomeColuna := TDBEdit.Create(Self);
     NomeColuna.Parent := Self;
     NomeColuna.Left := 105;
     NomeColuna.Top := Topo;
     NomeColuna.Width := 15 + fQuery.Fields[Coluna].DisplayWidth * 7;
     NomeColuna.DataSource := dsTabela;
     NomeColuna.DataField := fQuery.Fields[Coluna].FieldName;
     NomeColuna.Tag := Coluna;
     Topo := Topo + 25;
     NomeColuna.Visible := True;
     NomeColuna.Name := 'DBEdit' + IntToStr(Coluna);
     vetorCampos[Coluna] := NomeColuna;

     NomeLabel := TLabel.Create(Self);
     NomeLabel.Parent := Self;
     NomeLabel.Left := 5;
     NomeLabel.Top := NomeColuna.Top - NomeLabel.Height;
     NomeLabel.Caption := fQuery.Fields[Coluna].DisplayName;
     NomeLabel.Tag := Coluna;
     NomeLabel.Visible := True;
     NomeLabel.Name := 'Label' + IntToStr(Coluna);
     vetorLabel[Coluna] := NomeLabel;
   end;
   if Topo + 25 &gt; 445 then
      Height := 445
   else
      Height := Topo + 25;

end;

procedure TfrmDinamico.FormClose(Sender: TObject; var CloseAction: TCloseAction
  );
begin
  fQuery.Close;
  CloseAction := caFree;
end;

function TfrmDinamico.GetQuery: TZQuery;
begin
  Result := fQuery;
end;

procedure TfrmDinamico.SetQuery(NewQuery: TZQuery);
begin
  fQuery := NewQuery;
end;

Implemente os métodos GetQuery e SetQuery conforme mostrado no código. Na seção var declaramos dois arrays dinâmicos para receber os objetos TDBEdit e TLabel. Agora vamos analisar com mais detalhes o código do evento OnShow. É nesse código que serão criados os objetos em tempo de execução, de acordo com os campos da tabela. No post Lazarus - Criando componentes em run-time nós mostramos como criar componentes em tempo de execução.
Inicialmente no método nós definimos o tamanho dos arrays de acordo com o número de campos da tabela usando o procedure SetLength. Em seguida, dentro de um for que percorre os campos da tabela, é criado um TDBEdit e um TLabel para cada campo. Cada componente é guardado em seu array correspondente. Depois disso é verificada a necessidade de se aumentar a altura do form para que ele possa mostrar todos os componentes criados. Essa verificação é feita através da variável Topo que guarda a posição vertical de cada componente. Como o nosso form é dinâmico, nós liberamos a memória ocupada por ele assim que o mesmo for fechado.

Herdando o form Pai

Depois disso vamos criar o form de cadastro propriamente dito. Para isso selecione o menu File | New e clique em Inherited Component. Na caixa da direita irão aparecer os forms que podem ser herdados. Escolha o form Pai que foi criado anteriormente e pressione OK. Um novo form será criado com todos os membros do form Pai. Agora selecione a opção de menu Project | Project Options. Clique em Forms e transfira o form Filho para a caixa da direita. Isso determina que o form Filho será instanciado em tempo de execução. Para que tudo funcione perfeitamente no form Filho, devemos implementar os métodos herdados e informar a palavra chave inherited. No nosso caso precisamos implementar apenas os método dos eventos OnShow e OnClose, como mostra o código abaixo.

procedure TfrmCidade.FormShow(Sender: TObject);
begin
   inherited
end;

procedure TfrmCidade.FormClose(Sender: TObject; var CloseAction: TCloseAction);
begin
   inherited
end;            

Para testar nosso demo, vamos ao form principal. Coloque um botão e escreva o seguinte código no evento OnClick.

if TfrmCidade(Application.FindComponent('frmCidade')) = nil then
  frmCidade := TfrmCidade.Create(Application);
frmCidade.Query := dmDados.queCidade;
frmCidade.Show;

Estamos supondo que o nome do form Filho é frmCidade, que o nome do Data Module é dmDados e que a TZQuery no Data Module chama-se queCidade. Pronto você pode executar o programa e pressionar o botão. Você terá uma tela assim:



Você pode agora repetir esse procedimento para todas as tabelas que precisem de formulários de cadastro. Bem, certamente não é um form de cadastro perfeito, pois todos os campos são definidos com TDBEdit, mas a partir daqui você pode tentar vôos mais altos.