Pular para o conteúdo principal

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.

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.

Postagens mais visitadas deste blog

Lazarus - Acessando banco de dados com SQLdb - Parte I

Para fazer nossa primeira aplicação usando banco de dados no Lazarus vamos usar o SQLite e o conjunto de componentes nativo SQLdb. Inicialmente vamos apresentar passo como essa aplicação foi criada. Essa foi a maneira que eu fiz, e eu agradeço sugestões e questionamentos que pessoas que já passaram por essa experiência. Depois irei fazer algumas considerações sobre o uso do SQLdb. SQLite SQLite é uma biblioteca que implementa um motor de banco de dados SQL. É livre para qualquer finalidade, seja uso particular ou comercial. Lê e escreve em um único arquivo que pode ter além de tabelas, índices, gatilhos e visões. Executa em várias plataformas e é indicado para aplicações embarcadas. Maiores detalhes podem ser encontrados no site oficial. Para usá-lo, baixe-o do site e faça a instalação adequada para o seu sistema operacional. No Windows isso é muito simples, apenas copie sqlite3.dll para o system32 da pasta do sistema operacional. Existe uma ferramenta de linha de comando chamada

Tipos de dados no SQLite

Em SQLite, diferente de outros motores de banco de dados, o tipo de dado de um valor está associado com o valor propriamente dito, e não com o seu contêiner. É um sistema de tipo dinâmico. Um campo de uma tabela em SQLite pode receber qualquer tipo de dado. Assim, o SQLite simplesmente ignora o tipo informado no comando CREATE TABLE. Então, dizemos que no SQLite existem classes de armazenamento. E essas classes são: NULL - como em qualquer outro banco de dados. INTEGER - inteiro com sinal, armazenado em 1, 2, 3, 4, 6 ou 8 bytes dependendo da grandeza do valor. REAL - valor de ponto flutuante armazenado em 8 bytes. TEXT - uma string armazenada usando UTF-8, UTF-16BE ou UTF-16LE. BLOB - armazena um blob, como indica o nome. Uma coluna INTEGER PRIMARY é uma exceção. Só aceita números inteiros. Qualquer valor em um comando SQL tem uma classe de armazenamento implícita. Durante a execução do comando SQL, o SQLite pode converter valores entre classes numéricas (INTEGER e REAL)

Lazarus - Criando relatórios com FortesReport (Parte I)

Para a criação de relatórios, o Lazarus já trás o componente LazReport, no entanto ele precisa ser instalado no IDE. Para fazer a instalação do pacote, acesse o menu Package -> Open package file (.lpk) . Localize o diretório de instalação do Lazarus e na pasta components abra lazreport e depois source . Abra o pacote lazreport.lpk , clique em Compile e depois em Install . Como já sabemos isso irá recompilar o IDE. Depois de inicializado novamente estará disponível a aba LazReport . Leia aqui um tutorial básico sobre o LazReport. No entanto queremos mostrar uma alternativa ao LazReport . Por essa razão, vamos apresentar neste artigo o FortesReport . Para quem conhece o QuickReport, que fazia parte do Delphi, não terá dificuldade de desenvolver com esse componente. Baixe o pacote aqui e instale. Os procedimentos são semelhantes aos que mostramos acima. Você terá uma nova aba chamada Fortes Report . Conectando o banco de dados O primeiro passo para criar a aplicação é fazer