Nenhum comentário Download


Delphi – Parte VIII

 

Vimos no artigo anterior, um pequeno exemplo de como a POO pode ajudar a construir aplicações cliente-server no Delphi de forma eficiente e de qualidade. Neste artigo, ainda continuaremos vendo POO, onde mostrarei como criar um controle VCL no Delphi.

 

Nas partes III e IV, vimos alguns componentes da VCL e para nossa alegria, podemos criar nossos próprios componentes. Isso mesmo, podemos criar componentes do “zero” ou estender algum existente. O que vou mostrar neste artigo, é um componente simples, para localizar dados, que pode ser modificado de acordo com a necessidade do desenvolvedor.

 

Para aplicações terem performances, sempre bati na tecla da parametrização de dados, ou seja, os dados que são mostrados em tela devem ser a menor quantidade possível, como já vimos no decorrer da nossa série, usando herança visual de formulários.

 

Em uma aplicação, a quantidade de consultas auxiliares é bastante grande, ou seja, pesquisa de clientes, fornecedores, produtos etc, além de serem utilizadas em vários locais da aplicação.

 

Criar um formulário de consulta para cada uma das pesquisas torna-se trabalhoso, então por que não criar um componente para essas consultas. Nossa aplicação, cada cadastro tem uma aba com pesquisa para o respectivo cadastro.

 

Mas vamos imaginar que uma pesquisa de cliente, pode ser feita em outros locais. Como faríamos? A criação de um componente de pesquisa visa corrigir esse problema, para não criarmos várias telas de pesquisa.

Inciando a construção

Primeiramente, precisamos criar um pacote. Mesmo usando o Delphi XE2, você pode usar qualquer versão do Delphi para criar o componente. Clique em File>New>Other e acesse o item Package (Figura 1).

 

Figura 1. Criando o pacote do componente

 

Salve o pacote com o nome de “pkLocalizar. dproj”.

Criando o formulário

Teremos de criar o formulário de pesquisa, que deve conter os controles de tela para digitar o texto, botão de pesquisa e um grid onde serão mostrados os dados, além de um DataSource que será vinculado a um DataSet. Você pode incluir componentes de sua necessidade.

 

Nossa tela de pesquisa terá dois filtros: um para pesquisa pelo código, outro para pesquisa por uma descrição (nome, titulo etc). Crie um novo formulário no pacote e adicione os controles, veja na Figura 2 como será o formulário de pesquisa.

 

Figura 2. Formulário de pesquisa

 

No código do formulário devemos criar alguns métodos e propriedades, conforme o código a seguir:

 

procedure Buscar;

function GetCodigo: integer;

property DataSet: TClientDataSet;

property SQLCodigo: string;

property SQLDescricao: string;

   

Nota: Caso deseje, você pode criar uma coleção para ter vários tipos de pesquisa, como por exemplo, a propriedade Collumns do DBGrid. De acordo com essa coleção, dinamicamente, você poderia criar os itens do RadioButton da tela anterior. Fica como pesquisa para incrementar o componente.

 

No código anterior, criamos a assinatura de um método que será responsável por realizar a consulta (Buscar) e uma função, para retornar o código retornado pela consulta. Além disso, temos que criar uma propriedade do tipo TClientDataSet, responsável por ter os dados da consulta.

 

Caso deseje, mude para o controle que utilize para trabalhar com dados locais (IBQuery, Query etc).

As outras duas propriedades indicam o código SQL para a consulta por código ou por descrição, assim basta configurar a propriedade do Localizar, para ter a consulta parametrizada no componente.

 

O código do Buscar, simplesmente, repassa pro ClientDataSet o valor do Edit (edtLocalizar), onde o usuário digitará o valor de busca. No caso, temos que verificar qual o tipo de filtro escolhido pelo usuário, para poder configurar no DataSet o respectivo comando SQL da Listagem 1.

 

Listagem 1. Verificando o filtro escolhido

FDataSet.Close;

if (rgLocalizar.ItemIndex = 0) then

begin

  FDataSet.CommandText := FSQLCodigo;

  FDataSet.Params[0].AsInteger := StrToInt(edtLocalizar.Text);

end

else

begin

  FDataSet.CommandText := FSQLDescricao;

  FDataSet.Params[0].AsString := '%' + edtLocalizar.Text + '%';

end;

FDataSet.Open;

 

O código do GetCodigo, para retornar o campo chave da tabela, é bastante simples, onde retornamos o primeiro campo (Field) do ClientDataSet.

 

Result := DataSet.Fields[0].AsInteger;

 

Claro, precisamos indicar o campo chave na consulta SQL (propriedades SQLCodigo e SQLDescricao) para que seja o primeiro registro. Ao apertar CRTL+C, o Delphi cria o manipulador para a propriedade DataSet. O código do SetDataSet fica conforme o código a seguir:

 

if FDataSet <> Value then

begin

  FDataSet := Value;

  DataSource1.DataSet := FDataSet;

end;

 

No código, atribuímos para a propriedade DataSet do DataSource, o valor passado. Assim, ao configurarmos a propriedade no Object Inspector, o DataSet é vinculado ao DataSource. Como teremos apenas duas colunas no grid, podemos configurá-las via código para que fiquem com um tamanho especifico, fonte etc.

 

Para isso, criamos um método que realiza essa funcionalidade. Veja na Listagem 2.

 

Listagem 2. Método para configurar as duas colunas do grid.

var

  i : integer;

begin

{ configura as duas colunas do Grid}

if Assigned(FDataSet) then

begin

   grdLocalizar.Columns[0].Title.Caption := 'Código';

   grdLocalizar.Columns[1].Title.Caption := DescrColumn;

   for i := 0 to 1 do

   begin

     grdLocalizar.Columns[i].Title.Font.Name := 'Verdana';

     grdLocalizar.Columns[i].Title.Font.Style := [fsBold];

   end;

   grdLocalizar.Columns[0].Title.Alignment := taCenter;

   grdLocalizar.Columns[0].Alignment := taCenter;

   grdLocalizar.Columns[0].Width := 70;

   grdLocalizar.Columns[1].Width := 520;

end;

end;

 

O valor do Width será de acordo com o tamanho do Grid que você especificar. Entendi que duas colunas são necessárias, pois vamos, em quase sua totalidade, retornar apenas código e uma descrição, mas você pode adaptar para retorna quantas colunas quiser.

 

Apenas lembrando que o formulário é genérico, deve atender a qualquer tipo de consulta, então, devemos pensar sempre no mais comum para o formulário.

Codificando o formulário

Para os botões, que vão indicar se devemos confirmar o item selecionado no DBGrid ou cancelar o formulário (fechando-o), devemos chamar o Close, e a seguir, configurar o ModalResult, que para o botão OK, será mrOK e Cancelar como mrCancel.

 

Veremos adiante, o motivo de usarmos o ModalResult. No evento OnDblClick do DBGrid, devemos usar o mesmo código do botão OK, apenas verificando antes se existem registros na consulta, verificando a propriedade RecordCount do FDataSet. Assim, o usuário pode dar um duplo clique no grid e o mesmo será fechado.

 

No OnDataChange do DataSource, vamos habilitar o OK, apenas se existirem registros no ClientDataSet, ou seja, mais uma validação que fizemos para não termos problemas com o componente:

 

btnOk.Enabled := FDataSet.RecordCount > 0;

 

Para finalizar o formulário, precisamos tratar o evento OnKeyPress do edtLocalizar, usando o código da Listagem 3.

 

Listagem 3. Código do OnKeyPress do Edit

{ se apertou ENTER, realiza a busca }

if key = #13 then

  if edtLocalizar.Text <> '' then

  begin

    if Assigned(FDataSet)then

    begin

      Buscar;

      grdLocalizar.SetFocus;

      StatusBar1.SimpleText := Format(

        '%d registro(s) encontrado(s) com "%s"',

        [FDataSet.RecordCount, edtLocalizar.Text]);

      Key := #0;

    end;

  end;

 

No código anterior, primeiramente, verificamos se foi apertado a tecla ENTER. Após, verificamos se o Edit não esta vazio, isso é bom para que uma pesquisa muito grande não seja executando, trazendo lentidão ao sistema.

 

Poderíamos ainda, colocar uma quantidade mínima de caracteres a serem digitados para que o usuário chegue o mais perto possível do que esta procurando. Após verificarmos se a propriedade DataSet não esta vazia, chamamos o Buscar, configuramos o foco para o DBGrid e adicionamos uma mensagem na StatusBar, com a quantidade de registros encontramos com o valor digitado no Edit.

 

O key := #0, não emite o bip característico do sistema operacional. Com essa configuração, o formulário de consulta esta pronto. Alguns retoques a mais poderiam ser feitos, como configurar as colunas do DBGrid para que fiquem centralizadas pelo código e alinhadas a esquerda com o texto.

 

Quando o usuário apertar ESC, podemos fechar o formulário, ou ainda, quando o usuário apertar ENTER no DBGrid o mesmo fechar como se fosse clicado o botão OK. Isso são funcionalidades simples, que deixarei a cargo do leitor, mas a versão para download possui essas funcionalidades.

Criando o componente

Agora, precisamos criar uma unit, que receberá mais propriedades que serão repassadas ao formulário no momento de sua criação em memória, assim como também fazer o registro do componente no Delphi.

Crie uma nova unit e adicione o código da Listagem 4.

 

Listagem 4. Unit do componente

unit uLocalizar;

 

interface

 

uses Classes, Forms, DB, Dialogs, ufrLocalizar, Graphics, Controls,DBClient;

 

  type

   TLocalizar = class(TComponent)

   private

    FDataSet: TClientDataSet;

    FFormLocalizar: TfrLocalizar;

    FSQLDescricao: string;

    FSQLCodigo: string;

    procedure SetDataSet(const Value: TClientDataSet);

    procedure SetSQLCodigo(const Value: string);

    procedure SetSQLDescricao(const Value: string);

   protected

    procedure Notification(AComponent: TComponent;

      Operation: TOperation); override;

   public

     function Execute: Boolean;

     function ReturnValue: integer;

   published

    property DataSet: TClientDataSet read FDataSet write SetDataSet;

   property SQLCodigo: string read FSQLCodigo write SetSQLCodigo;

   property SQLDescricao: string read FSQLDescricao write SetSQLDescricao;

  end;

 

procedure Register;

 

implementation

 

procedure Register;

begin

  RegisterComponents('Luciano Pimenta®', [TLocalizar]);

end;

 

No código, criamos uma classe TLocalizar, descendente de TComponent. Na seção private temos variáveis, uma referente a propriedade publicada DataSet (seção published) que teremos e outra referente ao formulário criado neste artigo, e ainda as variáveis referentes as propriedades SQL.

 

O SetDataSet configura a propriedade e sua implementação é bem simples, onde apenas precisamos configurar a propriedade DataSet e verificar se o formulário esta criado, através do FFormLocalizar. As propriedades dos comandos SQL também são simples, basta configurar as mesmas como a padronização de propriedades.

 

Na seção protected, precisamos implementar o Notification, que é responsável, por atribuir nil a propriedade DataSet, quando por exemplo o ClientDataSet associado a mesma, por excluído.

Isso evita erros no IDE do Delphi. Sua implementação usa o seguinte código:

 

inherited;

if Operation = opRemove then

  if AComponent = FDataSet then

    FDataSet := nil;

 

Continuando o código da Listagem 4, na seção public temos duas funções. A primeira, Execute, indicará se houve algum registro escolhido no formulário, assim podemos repassar para a consulta principal, o valor do registro.

 

A segunda retorna o valor da chave primária do registro escolhido no grid do formulário, onde simplesmente, chama o GetCodigo da variável FFormLocalizar. Por fim, o Register (abaixo de implemetation), registra na Component Pallete do Delphi, onde passamos como parâmetro o nome, a paleta e a classe do componente.

Chamando o formulário de pesquisa

O Execute, é o principal método do nosso componente. Ele se comporta semelhante ao Execute dos controles Dialogs do Delphi, ou seja, é retornado true se foi escolhido um registro na consulta. Na Listagem 5, temos a implementação do método.

 

Listagem 5. Método Execute

if not FDataSet.Active then

  FDataSet.Active := True;

FFormLocalizar := TfrLocalizar.Create(nil);

try

  FFormLocalizar.DataSet := FDataSet;

  FFormLocalizar.SQLCodigo := FSQLCodigo;

  FFormLocalizar.SQLDescricao := FSQLDescricao;

  FFormLocalizar.ShowModal;

  Result := FFormLocalizar.ModalResult = mrOk;

finally

  FFormLocalizar.Free;

end;

 

O código é bastante simples, primeiro verificamos se o ClientDataSet vinculado ao componente esta ativo, se não “abrimos” o mesmo. A seguir, instanciamos o formulário de pesquisa, repassamos as propriedades e chamamos o ShowModal.

 

O resultado do Execute, será de acordo com o ModalResult do formulário, ou seja, caso seja mrOK foi selecionado algum registro na consulta, senão, nenhum registro foi selecionado. Por isso, configuramos nos botões do formulário o ModalResult.

 

Por fim, no finally, liberamos o formulário da memória. Para finalizar, toda a criação do componente, no pacote, clique em Compile e depois em Install. Caso tudo tenha dado certo, você terá um componente instalado em uma nova paleta no Delphi (Figura 3).

 

 

Figura 3. Componente instalado no Delphi

Colocando um ícone no componente

Para adicionarmos um ícone do componente, precisamos editar um arquivo DCR e adicionar no pacote. Caso esteja usando uma versão mais antiga, como o Delphi 7, podemos usar o Image Editor, acessando o menu Tools>Image Editor. No editor, clique em File>New>Component resource file (dcr).

 

Clique como botão direito em Contents e escolha New>Bitmap. Escolha uma imagem de tamanho 24x24 (use SuperVGA nas opções de cores). Após criar, de um duplo clique Bitmap1 e crie ou cole uma imagem de sua preferência. Após adicionar a imagem, renomeie o arquivo Bitmap1 para “TLOCALIZAR”.

 

Para versões mais novas, o Image Editor não acompanha o Delphi, então vamos usar uma ferramenta externa para resource files. Para esse artigo, usei a ferramenta XN Resource Editor que pode ser baixada em: www.wilsonc.demon.co.uk/d10resourceeditor.htm.

 

Para criar o resource e bem simples, abra a ferramenta e acesse o menu Resource>Add Resource. Escolha a opção Bitmap e clique em OK. Nas propriedades configure “24” para Width e Height. Cole a imagem que deseja ou importe a mesma usando o menu Resource. No painel lateral, renomeie a pasta 1 para “TLOCALIZAR”.

 

Veja na Figura 4 a ferramenta com as configurações.

 

Figura 4. Criando um resource no XN Resource Editor

 

Para os dois casos (Image Editor ou XN Resource Editor), salve o arquivo na mesma pasta do pacote criado no Delphi (com a extensão DCR). Voltando ao Delphi, no source do pacote (DPK), adicione o código que irá adicionar o arquivo:

 

{$R *.dcr}

 

Poderíamos também indicar o nome do arquivo sem a utilização da máscara. Agora, basta recompilar a aplicação e seu componente terá um novo ícone (Figura 5).

 

 

Figura 5. Novo ícone do componente

Testando o componente

Agora, precisamos testar o componente. Vamos criar um projeto de testes e depois usaremos o Localizar no projeto da nossa série. Crie um novo projeto e crie uma conexão com um banco de dados, usando dbExpress ou qualquer outra suíte de componentes para acesso a dados. Nosso principal componente é o ClientDataSet.

 

Crie uma conexão no Data Explorer para o banco Employee.fdb do Firebird. Caso deseje, pode usar qualquer outro banco que possua um driver no Delphi. Arraste a conexão criada para o formulário. Arraste a tabela Customer para o formulário, para ser criado um SQLDataSet.

 

Adicione também um DataSetProvider e modifique para True a propriedade Options>AllowCommandText, assim indicamos que o responsável por receber as consultas (instruções SQL) é o próprio ClientDataSet.

Adicione dois ClientDataSets ao formulário (“cdsEmployee” e “cdsBuscaEmployee”). No cdsEmployee configure o CommandText para: “select * from EMPLOYEE where EMP_NO = :EMP_NO”.

 

Estamos selecionando todos os dados da tabela, mas parametrizando a mesma pelo código, assim nossa consulta retornará todos os registros, apenas quando indicarmos o valor do parâmetro. Você pode fazer sub-consultas, joins nessa consulta, que não será afetada no nosso componente de pesquisa.

 

No cdsBuscaEmployee digite na mesma propriedade: “select EMP_NO, FIRST_NAME from EMPLOYEE where upper(FIRST_NAME) like :FIRST_NAME”. Nesse controle, estamos retornando apenas o código e nome da tabela Employee, parametrizando pelo nome, que será configurado no nosso componente.

 

Não esqueça de configurar os parâmetros nos dois ClientDataSets. Adicione um Localizar no formulário e configure DataSet para cdsBuscaEmployee. Digite nas propriedades SQL do Localizar, os mesmos comandos mostrados anteriormente para cada propriedade SQL.

 

Adicione os campos do cdsEmployee no formulário para que o mesmo fique semelhante a Figura 6. Adicione também um botão para chamar o Localizar.

 

Figura 6. Formulário de cadastro para teste

 

Agora, simplesmente, precisamos usar o seguinte código no botão:

 

if Localizar1.Execute then

begin

  cdsEmployee.Close;

  cdsEmployee.Params[0].AsInteger := Localizar1.ReturnValue;

  cdsEmployee.Open;

end;

 

Veja que o código é bem simples, onde verificamos se foi escolhido algum registro no formulário de consulta, usando o Execute. Após, simplesmente, configuramos o parâmetro da consulta com o valor retornado pelo componente (ReturnValue).

 

Com isso, temos um componente de pesquisa que pode ser reaproveitado em vários formulários usando o mesmo componente de dados. Veja o mesmo em execução na Figura 7, onde temos uma pesquisa pelo nome e outra pelo código.

 

Figura 7. Testando o componente

Usando na aplicação

Onde vamos usar nosso componente de pesquisa? No cadastro de vendas, temos algumas pesquisas auxiliares (cliente, empresa e produto), onde usamos a tela de pesquisa genérica. Vamos modificar para usar o nosso componente de pesquisa.

 

Adicionar três Localizar no formulário e faça a ligação da propriedade DataSet com os respectivos ClientDataSets de pesquisa. Veja na Listagem 6 o valor das propriedades dos componentes e o código dos botões para chamar o componente de pesquisa e fazer as configurações necessárias.

 

Listagem 6. Alteração do código dos botões

Localizar Cliente

SQLCodigo = select nCdCliente, sNmCliente from CLIENTE where nCdCliente =:nCdCliente

SQLDescricao = select nCdCliente, sNmCliente from CLIENTE where UPPER(sNmCliente) like :sNmCliente

 

Botão Cliente

if (locCliente.Execute) then

   nCdCliente := locCliente.ReturnValue

else

   DMPesquisa.cdsPesquisaCliente.Close;

 

Localizar Empregado

SQLCodigo = select nCdEmpregado, sNmEmpregado from EMPREGADO where nCdEmpregado =:nCdEmpregado

SQLDescricao = select nCdEmpregado, sNmEmpregado from EMPREGADO where UPPER(sNmEmpregado) like :sNmEmpregado

 

Botão Empregado

if (locEmpregado.Execute) then

   nCdEmpregado := locEmpregado.ReturnValue

else

   DMPesquisa.cdsPesquisaEmpregado.Close;

 

Localizar Produto

SQLCodigo = select nCdProduto, sNmProduto from PRODUTO where nCdProduto = :nCdProduto

SQLDescricao = select nCdProduto, sNmProduto from PRODUTO where UPPER(sNmProduto) like :sNmProduto

 

Botão Produto

if (locProduto.Execute) then

   nCdProduto := locProduto.ReturnValue

else

   DMPesquisa.cdsPesquisaProduto.Close;

 

Veja a redução drástica de linhas de código que tivemos. Podemos ainda alterar nosso código existente para substituir as variáveis pelos métodos do Localizar. Por exemplo: não precisamos mais usar o nCdCliente e sim apenas o locCliente.ReturnValue. Só vantagens usando um controle de tela criado.

Veja na Figura 8 a aplicação usando o Localizar.

 

Figura 8. Usando o Localizar no formulário de vendas

Conclusão

Vimos neste artigo como podemos criar componentes que nos auxilie em aplicações client/server para ser utilizado em consultas auxiliares, poupando tempo de programação e diminuição de código. Poderíamos usar formulários com a mesma finalidade, mas o componente tem facilidade de ser instalado e estar disponível em qualquer projeto Win32.

 

O que mostrei aqui foi apenas o pontapé inicial, use sua criatividade para agilizar seu dia a dia de programador. Um grande abraço a todos!