Lazarus – Herança de form e criação de componentes em run-time

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 > 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.

2 comentários:

Anônimo disse...

Olá... Parabéns pelo blog!

Programo em Delphi desde 1999, e atualmente surgiu a necessidade de atualizar minha linguagem. Como o valor da licensa é um tanto pesada para mim, iniciei pesquisa em busca de ambientes de programação open source.

Já conhecia o Lazarus, porém na época em que tive o primeiro contato, ainda não me senti seguro o suficiente para adotar esta ferramenta. Me parece que agora o Lazarus está mais maduro. Portanto pergunto:

Hoje já é possível desenvolver aplicativos comerciais (automação comercial) utilizando bancos de dados com total segurança e desempenho?

Abraços.

Luiz Carlos

Professor Carlos disse...

Sim Luiz Carlos
Vou lhe dar um exemplo. Você deve conhecer o ACBr. A empresa que patrocina o ACBr tem um sistema de automação em Lazarus homologado. Sugiro que você entre no grupo lazarus-br no Google. Tem muita gente boa lá que desenvolve em Windows e Linux. Abraço e obrigado pelo comentário.

Django - Composição de queries brutas (raw queries)

Django Rest Framework (DRF) é uma poderosa ferramenta para criar Web APIs. Ele requer o uso de Python e do framework Django. Mesmo o Django...