32
fevereiro 2013

The Club - megazine · Delphi, o Property Panel mostra a propriedade em negrito quando seu valor ... O Rave possui um compilador próprio para validar os scripts inseridos nos

  • Upload
    lamnhi

  • View
    251

  • Download
    5

Embed Size (px)

Citation preview

Page 1: The Club - megazine · Delphi, o Property Panel mostra a propriedade em negrito quando seu valor ... O Rave possui um compilador próprio para validar os scripts inseridos nos

fevereiro2013

Page 2: The Club - megazine · Delphi, o Property Panel mostra a propriedade em negrito quando seu valor ... O Rave possui um compilador próprio para validar os scripts inseridos nos

fevereiro2013

Page 3: The Club - megazine · Delphi, o Property Panel mostra a propriedade em negrito quando seu valor ... O Rave possui um compilador próprio para validar os scripts inseridos nos

fevereiro2013 03

Autor: Marcos César Silva

20

26

Autor: Lucas de OliveiraÍndice

Editorial

04

10

Autor: Hamden Vogel

05

Autor: Luciano Pimenta

Dica Rápida

Para copiar um componente no formulário de um projeto FireMonkey, não adianta tentar fazer da mesma forma como na plataforma VCL. Porém existe sim uma forma de copiar componentes nos formulários. Através da janela Structure, que é onde exibe todos os compo-nentes do formulário.

Selecione o componente na janela Structure e tecle Ctrl + C, para clonar o componente, selecione no formulário o recipiente no qual será clonado o componente e tecle Ctrl + V.

O recipiente pode ser o próprio formulário ou algum outro componente visual, inclusive uma TLabel. Note que após ser clonado o componente btnOrigem no recipiente onRecipiente (TPannel), o novo componente ficou associado ao nó

do recipiente na estrutura do formulário.

Fire Monkey - Ctrl + C e Ctrl + V em formulários

Curso DelphiParte IX

TMaskeditHVUm TMaskedit em Potencial para Automação de Cadastros – Parte 1

Delphi XE2Trabalhando com estilho FireMonkey - Parte II

Asp.Net Relatório Mestre-Detalhe com ReportViewer

Page 4: The Club - megazine · Delphi, o Property Panel mostra a propriedade em negrito quando seu valor ... O Rave possui um compilador próprio para validar os scripts inseridos nos

fevereiro201304

Delphi é marca registrada da Borland International, as demais marcas citadas são registradas

pelos seus respectivos proprietários.

Thiago Montebugnoli - Editor [email protected]

Olá amigos do The Club!

Fevereiro, mês do Carnaval, é tempo de brincar e festejar com os amigos. Mas como nem tudo na vida é festa, aqui estamos na nossa segunda edição do ano de 2013!

Os dias têm passado muito rápido e atualizar-se é preciso! Nos dias de hoje, a formação e informação continuada é essencial no tão competitivo mercado de trabalho.

Pensando nisso, a revista desse mês está cheia de novidades, co-meçando com nosso consultor técnico Lucas de Oliveira, ele aborda a segunda parte do artigo “Delphi XE2 - trabalhando com estilos no FireMonkey”, uma boa técnica para quem deseja alterar o layout de suas aplicações.

Luciano Pimenta encerra o curso sobre Delphi, na sua última parte, trabalha com Geradores de Relatórios “Rave Reports”, dando dicas e exemplos práticos para um maior aprendizado.

Neste mês, iremos dar boas vindas para um novo colunista, Hamden Vogel, sendo que neste artigo inicial foi desenvolvido um componente chamado “TMaskeditHV”, possibilitando inúmeras formatações e con-figurações de máscaras para Cadastros em geral. Marcos César Silva, finaliza a revista utilizando o gerador de relatórios “ReportViewer”, desenvolvendo para esta edição um exemplo de relatórios “Mestre/Detalhe” em conjunto com aplicações ASP.Net.

Fiquem a vontade para nos mandar sugestões para artigos futuros, para que possamos cada vez os ajudar melhor!

Um Forte Abraço!

Av. Profº Celso Ferreira da Silva, 190 Jd. Europa - Avaré - SP - CEP 18.707-150

Informações e Suporte: (14) 3732-1529

Internethttp://www.theclub.com.br

Cadastro: [email protected]: [email protected] Informações: [email protected] Cadastro: theclub_cadastro

Skype Suporte: theclub_linha1 theclub_linha2 theclub_linha3

www.twitter.com/theclubbr

Copyright The Club 2013

Diretor TécnicoMarcos César Silva

DiagramaçãoVitor M. Rodrigues

DesignVitor M. Rodrigues

RevisãoCintia Amaral

ColunistasHamden Vogel

Lucas de OliveiraLuciano Pimenta

Thiago Cavalheiro MontebugnoliMarcos César Silva

JuninhoJosé Antonio P. M. de Paula

Impressão e acabamento:GRIL - Gráfica e Editora

Taquarituba-SP - Tel. (14) 3762-1345

ReproduçãoA utilização, reprodução, apropriação, armazenamento em banco de dados, sob qualquer forma ou meio, de textos, fotos e outras criações intelectuais em cada publicação da revista “The Club Megazine” são terminantemente proibidos sem autorização escrita dos titulares dos direitos autorais.

Editorial

Page 5: The Club - megazine · Delphi, o Property Panel mostra a propriedade em negrito quando seu valor ... O Rave possui um compilador próprio para validar os scripts inseridos nos

fevereiro2013 05

Vimos no artigo anterior, um pequeno exemplo de como criar um controle VCL no Delphi. Neste artigo, finalizaremos a série mostrando relatórios com Delphi.

Nas primeiras versões do Delphi tivemos uma ferramenta simples de trabalhar e muito poderosa o Quick Report (www.quickreport.co.uk). Algumas caraterísticas da ferramenta:

• Utilizada juntamente com um formulário (o preeview do relatório ficava no formulário);

• Possibilidade de usar herança visual de relatórios (semelhante ao que vimos com os formulários de cadastros);

• Rica biblioteca de componentes para mostrar os dados (com gráficos etc);

Uma desvantagem, no meu ponto de vista, sobre o Quick, é de que se precisamos modificar algum relatório, será necessário recompilar e distribuir o nosso executável.

Isso não acontece com o Rave Reports (www.nevrona.com/rave), que passou a ser a ferramenta padrão do Delphi a partir da versão 7, pois ele trabalha com um IDE separado do Delphi e possui um arquivo com todos os relatórios do projeto. Outras características:

• Pode conectar diretamente ao banco de dados ou usando compo-nentes do Delphi;

• Exporta nativamente relatórios para os formatos: RTF, HTML, PDF e texto;

Vou mostrar nesse artigo o Rave Reports, que assim como o Quick, ainda possui compatibilidade com as ultimas versões do Delphi.

Conhecendo a IDE do Rave Reports

Como comentado, o Rave possui um ambiente visual separado do Delphi. Para acessar, acesse o menu Tools>Rave Reports Designer. Outra maneira, é adicionar um RvProject da aba Rave e dar um duplo clique.

Curso DelphiParte IX

Na Figura 1 temos o IDE do Rave,

Figura 1. IDE do Rave Reports

Ao executar percebemos várias semelhanças com o Delphi, como a paleta de componentes na parte superior e o Property Panel (equivalente ao Object Inspector) à esquerda.

Page

É no Page, parte central do IDE, onde todos os objetos do relatório são colocados, ou seja, onde o relatório é desenhado. Um relatório pode possuir várias Pages, que podem ser acessadas a partir do Project Tree (equivalente ao Object TreeView) conforme podemos ver na Figura 2.

Figura 2. Pages e como acessa-los através do Project Tree

Page 6: The Club - megazine · Delphi, o Property Panel mostra a propriedade em negrito quando seu valor ... O Rave possui um compilador próprio para validar os scripts inseridos nos

fevereiro201306

Property Panel

No Property Panel é possível acessar e alterar as propriedades de todos os componentes do relatório. Bastante semelhante ao Object Inspector do Delphi, o Property Panel mostra a propriedade em negrito quando seu valor padrão é alterado, como podemos ver na Figura 3.

Figura 3. Property Panel do IDE do Rave

Project Tree

A partir do Project Tree é possível acessar todos os relatórios e objetos do projeto. Isso é muito bom, pois podemos ter em alguns casos, controles que podem ser ruins de serem acessados pelo Page e só podem ser acessados pelo Project Tree.

Dentro do painel, ainda podemos visualizar a hierarquia dos componentes colocados no relatório e também, objetos muito importante: os de acesso a dados. Todas as conexões feitas diretamente ao banco de dados ou a partir de uma aplicação Delphi são listadas no Data View Dictionary.

Rave Event Editor

O Rave possui uma linguagem de script própria que permite a implemen-tação de eventos nos relatórios, a Rave Language. O Event Editor é onde toda a codificação dos eventos é realizada e onde definimos quais eventos serão implementados.

O Rave possui um compilador próprio para validar os scripts inseridos nos eventos, o RaveCC compiler.

Componentes do Rave Reports

O Rave possui vários componentes visuais e não-visuais, para serem usados no relatório. Além disso, podemos adicionar novos componentes na IDE (no site do desenvolvedor, temos outros componentes).

Na Figura 4 vemos os principais componentes do Rave.

Figura 4. Componentes do Rave

Criando o primeiro relatório

Vamos fazer um exemplo simples para o relatório. Com o Rave podemos utilizar como fonte de dados do relatório nossos DataSources “normais” da aplicação ou usar o acesso do IDE do Rave Reports.

Nesse primeiro exemplo, vamos fazer uma listagem de clientes. Adicione um RvProject no formulário principal. Dê um duplo clique no componente e acesse o Rave. Salve o arquivo .RAV (salve como “relatório.rav”).

Acesse o relatório e altere o nome para “rptListagemCliente”. Adicione um Region no relatório (aba Report), pois o mesmo determina a área de impressão da página do relatório. Redimensione o mesmo na página. Adicione um Band e um Databand (aba Report). Veja como esta nosso relatório na Figura 5.

Figura 5. Region e bands do primeiro relatório

Feche o Rave e no projeto Delphi, vamos indicar qual a componente que fará a “ponte” entre o projeto Delphi e o relatório do Rave. Primeiro, adicione um novo ClientDataSet no DMPesquisa que retorne todos os dados do cliente, pois queremos fazer uma listagem, portanto, sem filtro. Adicione um RvDa-taSetConnection e configure sua propriedade DataSet para o componente adicionado anteriormente.

Vamos agora, indicar qual a nossa fonte de dados no relatório do Rave. Abre o IDE e acesse File>New Data Object. No editor (Figura 6), escolha Direct Data View, pois vamos usar a fonte de dados constante no nosso projeto Delphi.

Page 7: The Club - megazine · Delphi, o Property Panel mostra a propriedade em negrito quando seu valor ... O Rave possui um compilador próprio para validar os scripts inseridos nos

fevereiro2013 07

Listagem 01

Figura 6. Criando a conexão com a fonte de dados

Clique em Next e escolha o RvDataSetConnection adicionado no projeto Delphi. No Project Tree você pode verificar o DataView criado, altere o seu nome para “dvListagemCliente”. Voltando ao relatório, temos que indicar quais as características dos controles Band e DataBand, se devem ser cabeçalhos, ca-beçalho de grupo, grupo, detail etc. Acesse a propriedade BandStyle do Band1.

Altere para Body Header e feche o editor. Na propriedade ControllerBand altere para DataBand1. No Band2, altere DataView para a conexão de dados criada anteriormente. Essa propriedade é muito importante, pois indica a fonte de dados do relatório.

No Band1 você pode colocar as informações que achar necessário, pois o mesmo é o cabeçalho do nosso relatório. Veja na Figura 7, a configuração do cabeçalho.

Figura 7. Cabeçalho do relatório

Para adicionar a imagem, use um Bitmap (aba Standard) juntamente com a propriedade FileLink e para o rótulo use um Text (aba Standard). Para o rodapé, vamos usar um DataText (aba Report) para podermos pegar variáveis do sistema, como: número de páginas, data de impressão etc.

Adicione, abaixo do Region um DataText, no lado esquerdo e acesse a propriedade DataField. No editor (Figura 8), temos como configurar várias informações para o relatório com variáveis do relatório, parâmetros etc.

Veja a Figura 8.

Na opção Data Text, digite: “Impresso em: ” & Report.DateShort. Assim, informamos a data da impressão. Adicione outro DataText no relatório e configure com o seguinte valor: “Página ” & Report.CurrentPage & “ de ” & Report.TotalPages.

Nossa informação será a página atual e a quantidade de páginas existente

no relatório. Voltando ao DataBand, vamos adicionar alguns DataTexts e usan-do a propriedade DataView e DataField, configurar os campos do relatório.

Podemos testar o relatório, utilizando a tecla F9. Veja que as informações do relatório estão aparecendo no Rave.

Chamando o relatório do Delphi

Adicione um novo botão no formulário principal e utilize o seguinte código para chamar o relatório criado anteriormente:

RvProject1.ExecuteReport(‘rptListagemCliente’);

Não esqueça de indicar o nome do arquivo RAV no RvProject usando a propriedade ProjectFile. Teste e veja o relatório em execução (Figura 9).

Figura 9. Relatório criado no Rave

Vimos que é fácil usar o Rave no Delphi. Em um primeiro momento, você que nunca usou Rave e esta acostumado com o Quick, pode achar meio estra-nho, mas com o tempo, verá que temos vantagens em usar Rave.

Para criar um relatório parametrizado, usando controles do Delphi, é muito

Figura 8. Editor do DataText

Page 8: The Club - megazine · Delphi, o Property Panel mostra a propriedade em negrito quando seu valor ... O Rave possui um compilador próprio para validar os scripts inseridos nos

fevereiro201308

Listagem 02 - SQL do Relatório

simples, basta usar o mesmo DataSource que esta parametrizado como fonte de dados do relatório. Faça um exemplo, para o relatório do cliente.

Crie um relatório, usando o cdsCliente que esta parametrizado com o cliente que esta em tela.

Nota: teremos algumas modificações para isso. No formulário base, adicione um botão responsável por chamar o relatório. Crie um método chamado no Click desse botão. Esse método será virtual, pois será subscrito nos formulários herdados.

Ao chamar o relatório no cadastro de clientes, sempre será mostrado o cliente filtrado no momento. Mas e se eu usar a facilidade da conexão do banco, dentro do IDE? Bom, dai teremos que passar o parâmetro da consulta, pela aplicação Delphi.

Repassando parâmetros pela aplicação

Crie um novo relatório, semelhante ao anterior. Vamos criar uma cone-xão com o banco, usando o menu File>New Data Object. Escolha Database connection. Note as opções de configuração com o banco de dados (Figura 8).

Figura 8. Conexões do Rave

Vamos usar a opção ADO para SQL Server (poderíamos também usar o DBX). Configure para o banco de dados da aplicação. Veja que na aba Test ao clicar no botão, o Rave lista as tabelas do banco de dados (Figura 9).

Figura 9. Tabelas listadas no Rave

Agora vamos criar uma consulta, baseada nessa conexão com o banco. Acesse o botão New Data Object e escolha Driver Data View. Escolha a conexão criada anteriormente e no editor, digite o SQL da Listagem 2.

SELECT e.sNmEmpregado, e.tDtAdmissao, e.nPcComissao, s.sNmSetor FROM EMPREGADO eINNER JOIN SETOR s on s.nCdSetor = e.nCdSetorWHERE e.nCdEmpregado = :EMPREGADO

Acesse a propriedade QueryParams do Driver Data View e adicione o seguinte código:

EMPREGADO=Param.pEmpregado

Estamos indicando para o parâmetro EMPREGADO do SQL, que esse será preenchido com o parâmetro de nome pEmpregado. Agora precisamos passar o valor do parâmetro através da aplicação Delphi. Mas antes, não esqueça de configurar o DataBand relatório de veículos com o Driver Data View criado anteriormente.

Na aplicação Delphi, use o seguinte código:

RvProject1.SetParam(‘pEmpregado’, ‘1’);RvProject1.ExecuteReport(‘rptEmpregado’);

Estamos passando um valor fixo, mas você pode adaptar a aplicação para chamar o relatório no cadastro de empregados (que deve ser criado). Fica como dica de alteração do projeto. Para finalizar, adicione no uses da tela inicial a unit RvDLADO.

Nota: Caso você esteja utilizando as tecnologias BDE, DBX ou IBX no Rave Reports para fazer o acesso direto ao banco de dados, você deverá declarar as units RvDLDBX ou RvDLBDE na cláusula uses do formulário.

Agrupamento

Vamos criar um relatório com a listagem de empregados, agrupado pelo setor. No Rave, crie um novo DataView e use o mesmo SQL da Listagem 2, apenas removendo o WHERE, adicionando o campo nCdSetor e ordenando a consulta pelo mesmo. Vamos adicionar um Band ao relatório e altere BandS-tyle para Group Header. No Band2, no ControllerBand indique o DataBand1.

Page 9: The Club - megazine · Delphi, o Property Panel mostra a propriedade em negrito quando seu valor ... O Rave possui um compilador próprio para validar os scripts inseridos nos

fevereiro2013 09

Listagem 03 - Pesquisando os dados da OS

Listagem 04 - Retornando os itens da OS

Em GroupDataView indique o DataView do relatório. Por fim, em Grou-pKey, indique o campo que queremos agrupar, nesse caso sNmSetor. Agora, basta acessar o relatório para ver as modificações e ter a listagem agrupada pelo tipo (Figura 10).

Figura 10. Relatório de listagem agrupado

Relatório master/detail de vendas

Agora, precisamos criar um relatório master/detail, para que possamos mostrar os dados da venda, juntamente com os produtos que fazem parte da mesma. Precisamos criar as consultas para o relatório, pois teremos que retornar os dados da venda e também dos produtos.

Crie um novo Driver Data View e use o comando SQL da Listagem 3.

SELECT nCdVenda, nVlVenda, tDtVendaFROM VENDAWHERE nCdVenda = :VENDA

Na propriedade QueryParams do Driver Data View adicione o seguinte código:

VENDA=Param.pVenda

Para a próxima consulta, use o código da Listagem 4.

SELECT p.sNmProduto, i.nCdVenda, i.nQtItem, i.nVlItem, (i.nQtItem * i.nVlItem) as SubTotalFROM ITENS iINNER JOIN PRODUTO p ON i.nCdProduto = p.nCdProdutoWHERE i.nCdVenda = :VENDA

Configura a propriedade QueryParams conforme a anterior. Crie um novo

relatório, semelhante aos que já criamos até aqui. Adicione dois DataBands e configure os mesmos para cada um dos Driver Data Views criados ante-riormente.

No DataBand1 configure o ControllerBand para DataBand2. Em BandStyle configure para BodyHeader. No DataBand2 configure o BandStyle para Details. Adicione os campos no DataBand1 para os dados da Venda, e no DataBand2 para os itens.

Veja na Figura 11 como ficou nosso relatório.

Figura 11. Relatório master/detail da OS

Conclusão

Vimos neste artigo a finalização da nossa série sobre Delphi para inician-tes. Espero ter ajudado de alguma maneira a você leitor conseguir iniciar seu caminho profissional com Delphi.

Um grande abraço a todos!

CD de Treinamento em RAD Studio 2007: www.lucianopimenta.com/post.aspx?id=165.

DVD de treinamento em Visual Web Developer: www.lucianopimenta.com/post.aspx?id=166.

Curso de Windows Forms: www.lucianopimenta.com/post.aspx?id=167.

www.lucianopimenta.net

Luciano PimentaLuciano Pimenta (NOVO DOMINIO: www.lucianopimenta.com) é desenvolvedor Delphi/C#

para aplicações Web com ASP.NET, Windows com Win32 e Windows Forms com .NET. Palestrante da 4ª edição da Borland Conference (BorCon) e da 1ª Delphi Conference.

É MVP Embarcadero, grupo de profissionais que ajudam a divulgar o Delphi no mundo. Atualmente é desenvolvedor da SoftDesign fábrica de softwares em Porto Alegre-RS.

Autor de mais de 90 artigos e de mais de 600 vídeos aulas publicadas em revistas e sites especializados, além de treinamentos presenciais e multimídias. É consultor da FP2 Tecnologia (www.fp2.com.br) onde ministra cursos de programação e banco de dados.

Sobre o autor

Page 10: The Club - megazine · Delphi, o Property Panel mostra a propriedade em negrito quando seu valor ... O Rave possui um compilador próprio para validar os scripts inseridos nos

fevereiro201310

Hoje em dia, existe componentes em Delphi para todas ou senão quase todas as finalidades. O programador tem a ampla escolha de selecionar um componente ou pacote de avaliação comercial como alternativas (e muito boas) freewares, que muitos deles fazem praticamente a mesma

coisa dos seus concorrentes pagos. E ainda existem muitos sites abrigando projetos opensource contendo código-fontes, componentes, dll´s e projetos disponíveis para sua utilização, a maioria principalmente para uso doméstico, mostrando boas idéias aliado a boas práticas de programação.

O que se mais valoriza, no entanto, é a criatividade para solucionar pro-blemas a um baixo custo justificável ao processo de desenvolvimento e manu-tenção de softwares, particularmente no nosso caso a linguagem ObjectPascal da famosa IDE chamada Delphi.

Este componente tem a missão de automatizar muitas coisas elementares funcionais em um processo de cadastro, como por exemplo:

1. Carregamento em uma Combobox de Dados Específicos, como Profissão, Escolaridade, Estados, Cidades associadas ao Estado selecionado, etc;

2. Cep validado ao Estado selecionado, Validação de Data/Hora, CPF, etc;

3. Formatação e Validação de: Data, Hora, CPF, CNPJ, CEP, Email, Pro-fissão, Escolaridade, Moeda, Telefone, Estado, Cidade e Formato Customizável (custom);

4. Alteração da Cor do Foco em Modo de Edição e Restauração da Cor na saída do Foco;

5. Processamento Automático da Tecla Enter, seguindo a Ordem de Tabulação dos Controles (mapeados com TabOrder);

6. Carregamento de um Combobox opcional para dados importados de um ClientDataSet, podendo obter inclusive seus índices;

7. Crítica “silenciosa” ou com mensagens customizadas para validação de tipos de formatos e fórmulas, incluindo sua entrada de dados incompleta (campo vazio/inválido) - É possível customizar ainda tipos de mensagens de erro e o nome do campo;

8. Suporte a dois tipos de Combobox imbutidos, o padrão (TCombo-box) e um Combobox com seleção múltipla – semelhante a um Checkbox com

TMaskeditHVUm TMaskedit em Potencial para Automação de Cadastros

Parte 1

Combobox (TCheckListComboBox);9. Suporte ao componente TAdvSmoothMessageDialog da TMS

Software (componente pago) para novo design do messagebox utilizado; funcionalidade opcional selecionada na diretiva de compilação Define (padrão – desabilitado);

10. Evento OnExecuteWhenEnterKeyIsPressed para responder junta-mente à tecla Enter e Eventos OnCustomChanging e OnCustomChangingExit, chamados quando na manipulação de combos customizáveis no evento On-Change e no evento OnExit, respectivamente;

11. Importação automática de qualquer DataSet (qualquer descendente de TDataSet) – disponível na propriedade CustomDataSet – associando um atributo do mesmo para a propriedade CustomFieldNamePassedAsObjectStrin-gParameter (campo String) e também para a propriedade CustomFieldName-PassedAsObjectIntegerParameter (campo Integer) – o propósito é “absorver” todo o DataSet para dentro do componente, como se tivesse feito um loop automático, poupando este trabalho do programador e ainda disponibilizando o índice destes dados, conforme item 6 anterior;

12. Propriedade ButtonClickedWhenEnterKeyPressed, para que um objeto botão (TButton) seja associado com o componente, a fim de ser exe-cutado o seu evento OnClick quando na saída do formato customizável com combo no evento OnExit;

13. Suporte a extensão de dados para novos carregamentos em Comboboxes: Isso já é feito para os formatos Estados, Cidades, Profissões e Escolaridade – através da compilação de dentro do componente e extração em arquivos de recurso (via XML) para sua leitura no Combobox de dentro do componente;

14. Suporte a um Calendário embutido;15. Suporte a um Hint personalizado disponível na propriedade TextHelp

(do tipo TStrings);16. Validações de entrada MaskInteger e MaskFloat para validação

somente de dados inteiros e numéricos com ponto flutuante, respectivamente;17. Método CheckEmptyField para obrigar seu preenchimento;18. Suporte Db-Aware através das propriedades DataSource e Data-

Field;19. Repintura do componente (via Canvas) chamadas nos eventos do

Mouse (Enter/Leave) – desenhada nas mensagens do Windows (TMessage): CM_MOUSEENTER, CM_MOUSELEAVE, WM_SETFOCUS, WM_KILLFOCUS, WM_NCPAINT;

Page 11: The Club - megazine · Delphi, o Property Panel mostra a propriedade em negrito quando seu valor ... O Rave possui um compilador próprio para validar os scripts inseridos nos

fevereiro2013 11

Código 01

20. Texto de aviso ao seu Preenchimento (“Favor Preencher”) – exibido no componente quando informado na propriedade DefaultTextInCustomFor-mat e na propriedade booleana DefaultTextInCustomFormatShow configurada como True;

21. Recurso alternativo de carregamento de dados para demais TMaskedithv’s ou outros componentes, através da propriedade disponível CustomDataSet (descendente de TDataSet), extraindo dados assim como de um objeto TDataSet qualquer.

Existem mais recursos; foram elencados acima os mais relevantes. A ideia fundamental do componente é de validar diferentes formatos de dados automaticamente, simplificando em muito o trabalho do programador em dispender este tempo adicional de validação em cada evento OnKeyPress/OnExit, funções extra para criticar formatos, funções extra para filtrar estes formatos, funções extra para selecionar dados, e assim por diante. Porque então não encapsular todas estas funcionalidades em um único componente para prover automaticamente para o usuário tais recursos de uma forma mais rápida e produtiva? O intuito do TMaskeditHV era este. Estava nascendo esta idéia, ao meu ver muito produtiva e potencialmente mais produtiva ainda por permitir sua extensão (código-fonte, tipos de formato, arquivos de recurso embutidos, etc).

Mãos a Obra: Instalando o Componente

Instalar o componente não é difícil, basta instalar o arquivo “TMaskedi-tHV.pas” no Delphi, como se instala um componente comum, indo no menu “Component” -> “Install Component”, procurar ele no “browse” e finalmente instalá-lo, sem mistério algum.

O componente foi desenvolvido em Delphi 7; acredito que em versões posteriores não haverá problema algum; não pode-se afirmar que em versões anteriores o componente funcionará 100% como esperado por falta de testes.

TMaskeditHV é um descendente de TMaskedit da Borland; o “HV” seguinte são as iniciais do meu nome (Hamden Vogel).

Demo 01: Carregando um registro via ClientDataSet e preen-chendo pelo componente através de seus formatos customizados

Existem muitas formas de carregar dados via Combobox, alimentados provenientes de uma fonte de dados. O componente possui uma importante propriedade chamada CustomDataSet em que ela “absorve” qualquer des-cendente de TDataSet a fim de mostrar no TMaskeditHV como uma seleção de dados. Funciona assim:

1 – Especifico a fonte de dados e associo na propriedade CustomDataSet;

2 – Verifico os campos desta fonte associada às propriedades CustomField-NamePassedAsObjectStringParameter e CustomFieldNamePassedAsObjectIn-tegerParameter, onde são vinculados atributos desta fonte dados do tipo String e Integer, respectivamente. O propósito disto é uma associação automática destas informações quando na sua exibição (na forma de seleção, quando configurada a propriedade booleana ShowCombo como “True” e o formato do tipo mskftCustom). O componente tentará obter o primeiro atributo en-contrado nestes tipos de dados, na ordem especificada pelo próprio dataset. Se não for encontrado nenhum, ele retorna vazio. Mas tenha em mente que será necessário, caso contrário não se conseguirá obter seu índice (algo como integer(fComboCustomText.Objects[customCombo.itemIndex])) nem como

exibir um texto do campo no combobox de seleção, obviamente.

3 – Associar na propriedade ButtonClickedWhenEnterKeyPressed um botão (TButton) que será chamado seu evento OnClick sempre que a tecla Enter for pressionada no componente. Funciona assim, no evento OnExit do componente o clique do botão será executado, sempre que esta propriedade for chamada. O importante é facilitar o desenvolvimento simplificando as chamadas de eventos e teclas chamadoras destes eventos como as teclas Enter/Tab nos eventos OnKeyPress, por exemplo.

Figura 01: Ilustração do Demo 01 – Carregamento de Dados através do Maske-ditHV. 100% MaskeditHV.

Para este primeiro exemplo de demonstração, será utilizado um banco de dados Firebird (versão 2.5) chamado “USUARIOS.FDB”; este banco contém apenas uma tabela, que é a “Usuarios”, relevante para o nosso material de estudo de integração com o mapeamento de datasets para o nosso com-ponente. Também foi utilizado a conexão DBExpress; todos os arquivos de configuração (ini’s, dll’s, etc) são distribuídos juntamente com o exemplo, bem como os arquivos do banco.

Primeira coisa a notar é a validação de campo preenchido (método boo-leano IsEmpty), como por exemplo:

if MaskEditHV1.IsEmpty then Exit;Como implementei essa validação “sob demanda”, este método boolea-

no suportará nesta versão apenas dois formatos (mskftCustom e mskftCpf). Logicamente e propositadamente (para forçar aos demais que implementem o restante) é possível estender aos outros formatos, de acordo com o código--fonte listado abaixo, que trata deste método. Observe que utiliza outras validações embutidas internas.

function TMaskEditHV.IsEmpty: Boolean;begin Result := False;

case Self.Formate of mskftCpf: Result := (EmptyCPF) or (Self.Text = EmptyStr) or (Self.Text = Self.DefaultTextInCustomFormat);

Page 12: The Club - megazine · Delphi, o Property Panel mostra a propriedade em negrito quando seu valor ... O Rave possui um compilador próprio para validar os scripts inseridos nos

fevereiro201312

Código 02

Código 03

mskftCustom: Result := (Self.Text = EmptyStr) or (Self.Text = Self.DefaultTextInCustomFormat); end;end;

Código-fonte do carregamento de dados oriundos de um TDataSet interno do componente:

with MaskEditHV1.CustomDataSet do begin Filter := ‘NOME = ‘ + QuotedStr(MaskEditHV1.Text); Filtered := True; edtCDUsuario.Text := Fields[0].AsString; edtNome.Text := Fields[1].AsString; edtDtNascimento.Text := Fields[2].AsString; edtEndereco.Text := Fields[3].AsString; edtCidade.Text := Fields[4].AsString; edtEstado.Text := Fields[5].AsString; edtCEP.Text := Fields[6].AsString; edtSetor.Text := Fields[7].AsString; edtTelefone.Text := Fields[8].AsString; edtSalario.Text := Fields[9].AsString; edtMatricula.Text := Fields[11].AsString;

mskCDUsuario.Text := Fields[0].AsString; mskNome.Text := Fields[1].AsString; mskDtNascimento.Text := Fields[2].AsString; mskEndereco.Text := Fields[3].AsString; mskCidade.LoadValueByIndex(Fields[4].AsString, True); mskEstado.LoadValueByIndex(Fields[5].

AsString, True); mskCEP.LoadText(Fields[6].AsString); mskSetor.Text := Fields[7].AsString; mskTelefone.LoadText(Fields[8].AsString); mskSalario.Text := Fields[9].AsString; mskMatricula.Text := Fields[11].AsString; end;

Observação: Para os formatos mskftDateTime e mskftDate, pode-se utilizar a função da Borland FormatDateTime para atribuição ao componente também:

mskDataNascimento.Text := FormatDateTime(‘dd/mm/yyyy’, StrToDateDef(Usuario.DataNascimento, Now));

Existem outros dois métodos importantes para popular os componentes, são os “LoadText” (que basicamente o que ele faz é setar a propriedade do TMaskedit da Borland EditMask para “”, recebe o dado e logo após recupera o valor da EditMask novamente) e o poderoso método LoadValueByIndex, que é o nosso “carregador” de dados através do seu índice. Abaixo segue o código-fonte deste método:

procedure TMaskEditHV.LoadValueByIndex(const Value: string; const bNoVisible: Boolean = True);begin if (Value = EmptyStr) or (StrToIntDef(Value, 0) = -1) then Exit;

if (fCheckState) or (fCheckCity) or (fCheckProfissao) or (fCheckEscolaridade) then begin GetList(bNoFocus, bNoVisible); combo.Visible := False; combo.ItemIndex := combo.Items.IndexOfObject(TObject(StrToIntDef(Value, 0)));

Self.Text := combo.Items.Strings[combo.ItemIndex]; end

Page 13: The Club - megazine · Delphi, o Property Panel mostra a propriedade em negrito quando seu valor ... O Rave possui um compilador próprio para validar os scripts inseridos nos

fevereiro2013 13

Código 04

else begin if (fCheckCustom) then begin if bNoVisible then GetCustomListNoVisible(‘ ‘, bNoFocus) else GetCustomList(‘ ‘, bNoFocus);

customCombo.Visible := False; if (StrToIntDef(Value, 0) > customCombo.Items.Count) then customCombo.ItemIndex := -1 else customCombo.ItemIndex := StrToIntDef(Value, 0);

Self.Text := customCombo.Items.Strings[customCombo.ItemIndex]; end; end; end;

Este método tem a importante missão de recuperar um valor via índice passado como parâmetro do objeto armazenado – no caso ele vai carregar pelo seu combobox interno e se encontrado (índice > -1) ele vai posicionar de acordo com o item correspondente ao índice, da mesma forma como o método do Delphi IndexOfObject já o faz; a diferença é que isso será feito automatica-mente, chamando este método IndexOfObject e carregando o valor desejado, como na linha de código-fonte abaixo da função descrita anteriormente – essa linha é a mais importante:

combo.ItemIndex := combo.Items.IndexOfObject(TObject(StrToIntDef(Value, 0)));

Portanto, o item é carregado e mais um recurso é executado e apresentado, sem necessidade de implementação extra do desenvolvedor.

Tem outros jeitos? Claro que sim. O nosso componente é versátil. Pode-se “alimentar” manualmente via TStrings a combo de seleção, através da proprie-dade TextCustomCombo. Por alguma definição de projeto, melhores práticas, ideias do gestor, etc, poderia por exemplo criar um objeto “usuário” com um método (função com retorno TStringList) para associar à esta propriedade, como descrito abaixo:

function TUsuario. CarregaUsuarios: TStringList;begin if not Assigned(fStringList)

then fStringList := TStringList.Create; Conexao.ObterDataSet(AbreRecordSetSomenteUsuarios, fStringList); Result := fStringList;end;

MaskeditHV1.TextCustomCombo := Usuario.CarregaUsuarios;MaskeditHV1.LoadCustomList;

Este fonte é apenas uma maneira de associar esta propriedade TextCus-tomCombo; por ser ela do tipo TStrings pode-se (conforme exemplo acima) criar suas próprias funções de acesso a dados para associar a esta proprie-dade em seguida; imagine um projeto orientado a objetos em no mínimo 3 camadas cuja camada de apresentação seja direcionada à população dos dados ao nosso componente – utilizando esta propriedade é fácil carregar os dados em abstrato:

1 – associar esta propriedade TextCustomCombo (pode-se inclusive abrir esta propriedade em tempo de design e inserir os dados manualmente nela lá – por ser do tipo TStrings, como um Memo, é possível armazenar como um contêiner);

2 – setar a propriedade formate para mskftCustom;3 – setar a propriedade ShowCombo para True;4 – Via código, chamar a procedure LoadCustomList;

Fazendo isso, os dados serão exibidos em um combobox assim que o componente for clicado (veja próximo Demo abaixo):

Demo 02: Obtendo o valor através do índice como parâmetro

Passando o índice via método LoadValueByIndex pode-se recuperar o valor (combinação pares código-valor) conforme armazenado em um combobox já explicado acima. Isso é mostrado de acordo com a figura do Demo 02 abaixo, do programa de exemplo correspondente:

Figura 02: Ilustração do Demo 02

Page 14: The Club - megazine · Delphi, o Property Panel mostra a propriedade em negrito quando seu valor ... O Rave possui um compilador próprio para validar os scripts inseridos nos

fevereiro201314

Código 05

Para obter o processo inverso (o índice pelo seu valor como parâmetro) – basta chamar a função GetIndex, que retorna um integer – caso nada en-contrado retornará -1.

Segue abaixo o código-fonte da função GetIndex:

function TMaskEditHV.GetIndex: integer;begin Result := -1; // if (fComboCustomText.Count = 0) then Exit;

if Assigned(fcustomchanging) then begin if Assigned(fComboCustomText) then if (customCombo.itemIndex > -1) then if (customCombo.Visible) or not (Self.IsEmpty) then if Assigned(fComboCustomText.Objects[customCombo.itemIndex]) then begin Result := integer(fComboCustomText.Objects[customCombo.itemIndex]); Exit; end; end;

if (Self.Text = EmptyStr) then begin Result := -1; Exit; end;

if (fCheckState) or (fCheckCity) or (fCheckProfissao) or (fCheckEscolaridade) then begin GetList(bNoFocus,True); combo.Visible := False; if combo.Items.IndexOf(Self.Text) > -1 then Result := integer(combo.Items.Objects[combo.Items.IndexOf(Self.Text)]); end else if (fCheckCustom) then begin GetCustomListNoVisible(‘ ‘,

bNoFocus); customCombo.Visible := False;

if customCombo.itemIndex > -1 then if Assigned(fComboCustomText.Objects[customCombo.itemIndex]) then Result := integer(fComboCustomText.Objects[customCombo.itemIndex]) else if Assigned(customComboCheckList) then Result := integer(fComboCustomText.Objects[customComboCheckList.itemIndex]) else Result := customCombo.Items.IndexOf(Self.Text); end;end;

Com isso os índices poderão ser fornecidos como parâmetros ou serem recuperados, através dos métodos LoadValueByIndex (procedure) e GetIndex (function: integer), respectivamente. Semelhantemente à função IndexOfOb-ject (TStrings) para LoadValueByIndex e a propriedade ItemIndex (TComboBox) para GetIndex, em síntese abaixo:

TMaskeditHV DelphiProcedure LoadValueByIndex Function IndexOfObject

Function GetIndex Propriedade ItemIndex

Demo 03: Obtendo o valor através do índice como parâme-tro – parte II

Este próximo Demo é quase que uma revisão do exemplo anterior para o formato mskftCustom. A novidade é o formato mskftState, que é o formato para exibição automática dos Estados Federativos (UF), cujos dados foram compilados de dentro do nosso componente através de arquivos de recurso – sendo inclusos em uma linha logo depois da cláusula implementation, como por exemplo {$R temp.res}.

Funciona assim:

1 – Criar um arquivo com extensão .rc, por exemplo “estados.rc”;2 – Escrever (no nosso exemplo) a seguinte linha neste arquivo “ESTADOS

RCDATA DISCARDABLE “estados.xml”;3 – Abrir o utilitário da Borland chamado brcc32.exe (se você executou

Page 15: The Club - megazine · Delphi, o Property Panel mostra a propriedade em negrito quando seu valor ... O Rave possui um compilador próprio para validar os scripts inseridos nos

fevereiro2013 15

a instalação padrão do Delphi, estará localizado em “C:\Arquivos de progra-mas\Borland\Delphi7\Bin”. Só que para executá-lo, não basta apenas dar um duplo-clique porque senão ele não vai fazer nada; deve-se abrir pelo prompt de comando do Windows (sugestão: menu Iniciar => Executar => digite “cmd” (sem aspas);

4 – Compilar pela linha de comando – utilitário brcc32 – “brcc32 -32 estados.rc -foestados.res”

5 – Inserir logo após a cláusula implementation: {$R estados.res}6 – Criar uma função que leia os dados contidos no arquivo de recurso e

os extraia (disponível no fonte).

O nosso componente utiliza uma procedure interna chamada “LoadTem-pXML” que tem a missão de extrair um XML compilado no arquivo de recurso, extraindo para o diretório de onde a aplicação que o chama está rodando; alimentar um ClientDataSet interno com este arquivo XML gerado no arquivo de recurso e carregar seus dados em um combo embutido de dentro do com-ponente. E é assim que a mágica funciona. Tudo imperceptível para o usuário. E tudo imperceptível para o desenvolvedor, em termos de detalhes necessários, relevantes e finalmente encapsulados. É o reaproveitamento de ideias.

Em outras palavras, só falta extrair as informações contidas no arquivo de recurso. Com as funções da API do Windows FindResource e LoadResource podemos procurá-lo e carregá-lo, respectivamente. Mas temos que utilizar uma função completa para realizar todo o trabalho de uma vez, carregando os dados embutidos nele e populando um controle específico. No componen-te segue um caminho para fazer isso, mas esteja certo de que há inúmeras maneiras de se chegar ao mesmo resultado. A função extractResFromPas do componente fará esse trabalho. Sua utilização é simples: por exemplo, para extrair o recurso que criamos nesta demonstração agora, façamos assim: extractResFromPas(‘ESTADOS’, ‘c:\estados.xml’). A partir da chamada desta função o arquivo original que foi compilado como recurso estará disponível no diretório especificado. Com base nele podemos popular uma combo temporária para exibir os dados. E é exatamente isso que é feito no momento.

Então, recapitulando, é necessário seguir esta sequencia de passos definidas anteriormente, com a definição do dataset que será gerado. Nesta implementação do componente, foi escolhido o banco DBISAM da Elevate Sof-tware (shareweare), mas poderia ser qualquer outro, pois isso realmente não importa para este processo, já que todos vão ser transformados em recursos do mesmo jeito: define-se a estrutura, criando a estrutura e populando os dados, e em seguida transformando em arquivos XML através de um ClientDataSet, associando sua propriedade “Assign Local Data” (clicando com o botão direito) com o dataset desejado e depois digitando o método SaveToFile, passando o parâmetro dfXML do tipo TDataPacketFormat. Exemplo:

tempClientDataSet.SaveToFile(ExtractFilePath(Application.ExeName) +’ESTADOS.xml’, dfXML);

Basicamente, é isto. Os dados serão lidos de um XML; cada formato msk-ftCity, mskftState, mskftProfissao e mskftEscolaridade tem um XML próprio já gerado e compilado no componente. A única desvantagem é que para a edição destes XML’s embutidos, deve-se compilar o XML e o componente novamente. O que foi levado em conta aqui é a independência do componente em relação a arquivos secundários; portabilidade e eficiência. Logicamente que novas

idéias podem ser estendidas e aperfeiçoadas; uma DLL ou arquivo serializado ao invés de um XML temporário, etc;

Vamos ilustrar com este demo, o funcionamento do componente para o formato do tipo “estado” (mskftState); a ênfase se dará neste enfoque para explicação passo a passo deste recurso:

Figura 03: Ilustração do Demo 03

Conforme figura acima (Demo 03), são apresentados os formatos mskft-Custom e mskftState, respectivamente.

Sequência de Processamento dos formatos mskftState e mskftCity:

A seguir vamos ilustrar como estes dois formatos se interagem. Não há mistério, a única coisa que deve ser notada aqui é que a propriedade MaskState deve ser preenchida contendo o MaskeditHV do formato mskftState:

O Combobox do formato de Estados (formate = mskftState) carregado com os estados (arquivo XML dos estados), esperando a tecla Enter para desaparecer e setar o campo para o item selecionado;

Após a tecla Enter ser pressionada, o MaskeditHV do formato de cidades recebe o foco, e recebendo a tecla Enter, também carrega um Combobox, mas desta vez com as cidades filtradas pelo estado do campo anterior. Basicamente é assim que o processo em conjunto funciona. É claro que esse filtro de cidades não funcionaria se não tivesse um registro de “estado” selecionado.

Figura 04: Ilustração do Demo 04

Figura contendo o Demo 04, explicando o funcionamento entre estes dois formatos (Estado/Cidade).

Portanto, podemos agora mostrar ao usuário a seleção de estado e cidades desta forma dinâmica e prática. E sem trabalho nenhum para o programador,

Page 16: The Club - megazine · Delphi, o Property Panel mostra a propriedade em negrito quando seu valor ... O Rave possui um compilador próprio para validar os scripts inseridos nos

fevereiro201316

Código 06

Código 07

pois não vai escrever uma linha de código.

Dois formatos de exibição de dados do Combobox embutido: cbftDefault (o padrão) e o cbftCheckList (seleção múltipla – como itens em checkboxes)

Em muitos projetos que desenvolvi, sempre senti falta de um componente ou de uma função mais simples que implementasse algo como um CheckLis-tBox como uma forma de seleção; em outras palavras um CheckListBox com Combobox. Finalmente, resolvi implementar esta alternativa ao formato padrão de itens de um combobox comum, dando a faculdade de escolher entre o formato já conhecido cbftDefault bem como o formato recém-implementado cbftCheckList.

Primeiramente, vamos discorrer sobre o código-fonte desta implemen-tação:

type TComboboxFormate = (cbftDefault, cbftCheckList); ...property ComboboxFormate: TComboboxFormate read FComboboxFormate write SetComboboxFormate default cbftDefault;…procedure TMaskEditHV.GetList(const NoFocus: Boolean = false; const bNoVisible: Boolean = false);begin

case FComboboxFormate of //cbftDefault, cbftCheckList cbftCheckList: combo := TCheckListComboBox.Create(Self); cbftDefault: combo := TComboBox.Create(Self); end;…if FComboboxFormate = cbftCheckList then customComboCheckList := TCheckListComboBox (combo);…type TCheckListComboBox = class(TComboBox) private { Private declarations } FOnSelect : TNotifyEvent; FDDForm : TForm; FListBox : TListBox; FHorzScrollBar : boolean;

FOnDropUp : TNotifyEvent; FUpperCase : boolean; FKey : Word; FReadOnly : Boolean; FColumns : integer;

Portanto, como disse anteriormente, implementei duas formas de seleção, por necessidade em meus projetos – pense o quanto interessante seria as pos-sibilidades de utilização de uma seleção múltipla – carrinho de compras, itens desejados, itens a configurar, usuários selecionados, permissões selecionadas, minhas opções especificadas, selecione as opções desejadas, etc; enfim, as possibilidades são infinitas.

Implementando esta funcionalidade de dentro do componente foi intensa-mente simplificada para o desenvolvedor, pois basta alterar uma propriedade, a ComboboxFormate, setando-a para cbftCheckList ou cbftDefault, para exibir o formato desejado – o componente vai alimentar a propriedade TextCus-tomCombo que você alimentaria manualmente – com seus dados que serão exibidos nele. Segue um trecho abaixo do fonte relacionado à esta propriedade:

property TextCustomCombo: TStrings read fComboCustomText write SetComboCustomText;

A procedure LoadDataSet executa a funcionalidade de carregar interna-mente um DataSet para dentro da propriedade CustomDataSet.

Segue abaixo o código-fonte completo desta procedure LoadDataSet:

procedure TMaskEditHV.LoadDataSet;var indexCustomFieldNameP assedAsObjectStringParameter, indexCustomFieldName PassedAsObjectIntegerParameter: integer; tempClientDataSet: TClientDataSet; Field: TField;begin if Assigned(FCustomDataSet) then begin if not Assigned(tempClientDataSet) then tempClientDataSet := TClientDataSet.Create(Self); try

Page 17: The Club - megazine · Delphi, o Property Panel mostra a propriedade em negrito quando seu valor ... O Rave possui um compilador próprio para validar os scripts inseridos nos

fevereiro2013 17

tempClientDataSet := TClientDataSet(FCustomDataSet); except on E: Exception do begin callMessageBox(Pchar(E.Message), ‘Erro’, MB_OK + MB_ICONERROR); FCustomDataSet := nil; end; end; end else Exit;

if Assigned(tempClientDataSet) then try tempClientDataSet.Open; except on E:Exception do begin callMessageBox(Pchar(‘Erro ao Abrir DataSet:’ +#13#10+E.Message), ‘Erro’, MB_OK + MB_ICONERROR); FCustomDataSet := nil; Abort; end; end;

indexCustomFieldNamePassed AsObjectStringParameter := 0; //default indexCustomFieldNamePassed AsObjectIntegerParameter := 1; //default

if (fCustomFieldNamePassed AsObjectStringParameter <> EmptyStr) then begin Field := tempClientDataSet.FindField(fCustom FieldNamePassed AsObjectStringParameter); if Assigned(Field) then indexCustomFieldName PassedAsObjectStringParameter := Field.DataSet.F ields.IndexOf(Field) else begin fCustomFieldNamePassedAsObject StringParameter := GetNextFieldNameFromSelectedType(StString); Field := tempClientDataSet.FindField

(fCustomFieldNamePassed AsObjectStringParameter); if Assigned(Field) then indexCustomFieldNamePassed AsObjectStringParameter := Field.DataSet.Fields.IndexOf(Field) else indexCustomFieldNamePassed AsObjectStringParameter := -1; end; end;

if (fCustomFieldNamePassed AsObjectIntegerParameter <> EmptyStr) then begin Field := tempClientDataSet.FindField (fCustomFieldName PassedAsObjectIntegerParameter); if Assigned(Field) then indexCustomFieldNamePassed AsObjectIntegerParameter := Field.DataSet. Fields.IndexOf(Field) else begin fCustomFieldNamePassed AsObjectIntegerParameter := GetNextFieldNameFromSelectedType(StInteger); Field := tempClientDataSet.FindField (fCustomFieldNamePassedA sObjectIntegerParameter); if Assigned(Field) then indexCustomFieldNamePassed AsObjectIntegerParameter := Field.DataSet.Fields.IndexOf(Field) else indexCustomFieldNamePassed AsObjectIntegerParameter := -1; end; end;

//ShowMessage(tempClientDataSet. FieldDefs.Items[0].FieldClass.ClassName);

if (indexCustomFieldNamePassed AsObjectStringParameter = -1)

Page 18: The Club - megazine · Delphi, o Property Panel mostra a propriedade em negrito quando seu valor ... O Rave possui um compilador próprio para validar os scripts inseridos nos

fevereiro201318

Código 08

or (indexCustomFieldNamePassed AsObjectIntegerParameter = -1) then begin callMessageBox(‘Erro ao localizar Atributos.’,’Erro’, MB_OK + MB_ICONERROR); Exit; end;

if not (tempClientDataSet. FieldDefs.Items [indexCustomFieldNamePassed AsObjectStringParameter] .DataType in [ftString]) then begin callMessageBox(PChar(‘Erro ao Abrir DataSet.’#13#10 + ‘Atributo ‘ + fCustomFieldNamePassed AsObjectStringParameter + ‘ não é do Tipo String.’), ‘Erro’, MB_OK + MB_ICONERROR); FCustomDataSet := nil; Exit; end;

if not (tempClientDataSet.FieldDefs. Items[indexCustomFieldNamePassed AsObjectIntegerParameter].DataType in [ftInteger]) then begin callMessageBox(PChar(‘Erro ao Abrir DataSet.’#13#10 + ‘Atributo ‘ + fCustomFieldNamePassed AsObjectIntegerParameter + ‘ não é do Tipo Integer.’), ‘Erro’, MB_OK + MB_ICONERROR); FCustomDataSet := nil; Exit; end;

if not Assigned(fComboCustomText) then fComboCustomText := TStringList.Create else fComboCustomText.Clear;

// if (FCustomDataSet.ClassType = TClientDataSet) then FCustomDataSet.First;

tempClientDataSet.First;

try

tempClientDataSet.DisableControls; Application.ProcessMessages; while not tempClientDataSet.Eof do begin if not (tempClientDataSet.Fields [indexCustomFieldNamePassed AsObjectIntegerParameter].IsNull) and (tempClientDataSet.Fields [indexCustomFieldNamePassed AsObjectIntegerParameter].Text <> EmptyStr) and (tempClientDataSet.Fields [indexCustomFieldNamePassed AsObjectStringParameter].Text <> EmptyStr) then fComboCustomText.AddObject (tempClientDataSet.Fields[indexCustomField NamePassed AsObjectStringParameter].AsString, TObject(tempClientDataSet.Fields [indexCustomFieldNamePassed AsObjectIntegerParameter].AsInteger)); tempClientDataSet.Next; end; finally tempClientDataSet.EnableControls; end;end;

Resumindo, a procedure LoadDataSet executa alimenta a propriedade CustomDataSet mencionada anteriormente e também carrega o objeto TStrings fComboCustomText armazenando um campo do tipo string e do tipo integer, ambos informados pelo usuário nas propriedades CustomFieldNamePassedA-sObjectStringParameter e CustomFieldNamePassedAsObjectIntegerParameter, respectivamente. Abaixo é mostrado o momento em que a fComboCustomText recebe os dados do DataSet especificado.

fComboCustomText.AddObject(tempClientDataSet.Fields[indexCustomFieldNamePassed AsObjectStringParameter].AsString,

Page 19: The Club - megazine · Delphi, o Property Panel mostra a propriedade em negrito quando seu valor ... O Rave possui um compilador próprio para validar os scripts inseridos nos

fevereiro2013 19

TObject(tempClientDataSet.Fields[indexCustomFieldNamePassed AsObjectIntegerParameter].AsInteger));

Conclusão

O componente tem grandes premissas; particularmente gosto mais do armazenamento interno de DataSets e geração automática de XML’s a partir de arquivos de recurso; são grandes funcionalidades embutidas em um código para obtenção de dados de forma dinâmica e transparente para o desenvolvedor.

É inegável que tudo isso gera produtividade e eficiência, pois evita o tempo de escrever toda essa “receita de bolo” que já é entregue pronta, bastando apenas chamar os métodos desejados para obter a função desejada – e mui-tos eventos foram sobreescritos justamente para agilizar também o tempo de escrita de códigos úteis, repetitivos e não por último importantes, como filtro de inteiros e floats, através das propriedades MaskInteger e MaskFloat, respectivamente.

Outro ponto que gostei muito em desenvolver nele foi o modo como os formatos foram validados – pelo formato, pelo texto em branco e pelo texto incompleto – pois apesar de ser algo simples de entender não é algo fácil de implementar – cada formato tem suas peculiaridades e o componente nativo do Delphi TMaskedit carece muito destas implementações. Ainda neste sentido cabe as implementações geradas dinamicamente pelos XML’s (conforme dito

[email protected]

Hamden VogelAnalista de Sistemas pós-graduado em Engenharia de Software pela

UPIS e Programador Delphi com larga experiência desde 2000, tem de-senvolvido e vendido softwares em Delphi para a África e Estados Unidos, além do mercado nacional. Colaborou com dicas e componentes para sites especializados em Delphi. Também desenvolve em outras linguagens como C/C++, ASP, PHP e .NET.

Sobre o autor

anteriormente) que são coisas que não são vistas em nenhum outro lugar, encorajando-me a desenvolver por estas mesmas razões – as necessidades de projeto.

Este foi o primeiro artigo. Espero sinceramente que este componente seja útil para o trabalho de vocês leitores, assim como para mim também o foi. Um grande abraço e até próxima. Bons trabalhos com o TMaskeditHV.

Aguardo sugestões, críticas e melhorias, através do código-fonte disponí-vel; fiquem a vontade nesse sentido. Componente freeware.

Page 20: The Club - megazine · Delphi, o Property Panel mostra a propriedade em negrito quando seu valor ... O Rave possui um compilador próprio para validar os scripts inseridos nos

fevereiro201320

Este é um assunto muito requisitado em nosso suporte técnico, não apenas em se tratando de relatórios e sim exemplos envolvendo cadastros e consultas utilizando esta técnica.

Para quem não sabe, Mestre-Detalhe é a Típica Entidade Relacionamento “Um para Muitos”. Mestre é o registro que existe por si só, já o detalhe necessita de um “Mestre” para poder existir.

Podemos realizar esta tarefa sem muitos esforços utilizando o gerador de Relatórios ReportViewer junto com o Microsoft Visual Studio 2010. O intuito deste artigo será de descrever todas as etapas envolvendo um exemplo básico de Pedido de Venda utilizando como Banco de Dados o Microsoft SQL Server 2008 com a base de dados padrão “NorthWind”.

Tabelas Utilizadas

Para exemplificar melhor, nos próximos passos montarei um Modelo (En-tidade Relacinamento –ER) utilizando a ferramenta SQL Server Management Studio do próprio SQL Server 2008.

Usamos como base uma Tabela de Pedido (Orders) se relacionando com outra de itens (OrderDetail), fechando um relacionamento Mestre-Detalhe. Relacionamos também com a tabela de Clientes (Customers) e de Produtos (Products). Iremos recuperar os dados através de uma Instrução SQL envol-vendo todas estas tabelas citadas.

Ver Imagem 01 para maiores detalhes.

Montando um exemplo

Para Isto abra o Microsoft Visual Studio clicando em “File/New/Web Site...” para assim criarmos uma página web do zero.

1-) Definindo a fonte de Dados

A fonte de dados nada mais é do que a origem e a forma como os dados

Asp.NetRelatório Mestre-Detalhe com ReportViewer

serão coletados para podermos trabalhar com eles. Clique com o botão direi-to sobre a solução e escolha “Add New Item...”. Localize o item “Dataset” e defina um nome para este objeto, por exemplo: “dsMestreDetalhe”. Notem que foi criada uma pasta “App_Code” e dentro dela um arquivo do tipo “.xsd”. Geralmente dentro desta pasta é onde armazenamos estes ou outros tipos de arquivos, como por exemplo do tipo classe (.cs). Dê um duplo clique para podermos definir a fonte de dados.

Importante salientar que na região esquerda da tela aparecerá uma ToolBox.

Figura 02: ToolBox.

Figura 01: Modelo Entidade Relacionamento (ER) das tabelas envolvidas.

Page 21: The Club - megazine · Delphi, o Property Panel mostra a propriedade em negrito quando seu valor ... O Rave possui um compilador próprio para validar os scripts inseridos nos

fevereiro2013 21

Código 01Para trabalhar com relatórios existem diversos tipos de componentes, eu recomendo a utilização do “TableAdapter”, nele conseguimos configurar tudo o que precisamos no quesito de instruções SQL. Arraste-o na tela e seguire-mos os passos adiante. Na tela “Choose your data Connection” é o local onde escolhemos a conexão, Ver Imagem 03.

Figura 03: Tipo de Conexão.

Já na próxima etapa denominada “Choose a Command Type”, é o local onde definimos o modo como o tableAdapter irá acessar a Base de Dados. Possuímos três opções, sendo:

- Uso de Instruções SQL;- Criar Stored Procedures;- Usar Stored Procedures.

Escolha a primeira opção e clique em “Next”. Ver Imagem 04.

Figura 04: Acesso a Base de Dados.

Faremos uma Instrução SQL baseada no diagrama disposto logo no início do artigo (Figura 01). Parametrizamos pelo “OrderID” (Número do Pedido) utilizando a cláusula “Between” (intervalo de valores) .

SELECT Customers.CompanyName, Orders.OrderID, Orders.RequiredDate, Products.ProductName, OrderDetails.ProductID, OrderDetails.UnitPrice, OrderDetails.QuantityFROM Orders INNER JOIN Customers ON Customers.CustomerID = Orders.CustomerID INNER JOIN OrderDetails ON OrderDetails.OrderID = Orders.OrderID INNER JOIN Products ON Products.ProductID = OrderDetails.ProductIDWHERE (Orders.OrderID BETWEEN @COD_INI AND @COD_FIM)

A tabela a seguir nos mostra o significado dos campos utilizados.

Campos SignificadoCompanyName Nome do Cliente

OrderID Número do Pedido Venda

RequiredDate Data Requisição

ProductName Nome do Produto

ProductID Código do Produto

UnitPrice Preço Unitário

Quantity Quantidade

Podemos também montar instruções SQL com o auxílio do “Query Builder”, sem nenhuma codificação. Ver Image 05.

Figura 05: Instrução SQL.

Page 22: The Club - megazine · Delphi, o Property Panel mostra a propriedade em negrito quando seu valor ... O Rave possui um compilador próprio para validar os scripts inseridos nos

fevereiro201322

O “TableAdapter” deverá ficar idêntico ao da Figura 06.

Figura 06: TableAdapter “DsMestreDetalhe”.

2-) Definindo o Lay-out do Relatório

- Criando Parâmetros

Crie um arquivo do tipo “.rdlc” e o nomeie como “RptMestreDetalhe”. O passo inicial da nossa tarefa é definir alguns parâmetros como por exemplo: o Título e o SubTítulo, para isto clique no menu “View/Report Data”. Na pasta “Parameters” clique como o botão direito e escolha “Add Parameter...”. Na aba “General” defina em “Name” como “ParamTitulo” e em “Data type” como “text”. O restante deixaremos como padrão, clique em OK para concluir este processo. Os mesmos passos descritos deverão ser realizados para o parâmetro “SubTítulo”. Ver Imagem 07.

Figura 07: Criando Parâmetros.

No Cabeçalho do relatório adicione dois componentes “textBox” contendo os seguintes valores:

Título: =Parameters!ParamTitulo.Value

Subtítulo: =Parameters!ParamSubtitulo.Value

Esta é a referência dos parâmetros que iremos passar mais adiante através do Preview do relatório.

- Criando o agrupamento

No corpo insira um “Table” e na propriedade “DataSetname” deixe como DsMEstreDetalhe. Para adicionar um grupo clique com o botão direito sobre a linha “Data” e escolha “Add Group/Parent Group”. Ver Imagem 08.

Figura 08: Adicionando o Agrupamento.

Na Janela “Tablix group” escolha “Group by: [OrderID]” para agrupar por “orderID” e cheque os valores “Add group header” e “Add group footer”, significando respectivamente para adicionar cabeçalho e rodapé para o grupo. Ver Imagem 09.

Figura 09: Tablix Group.

Um detalhe importante é que a linha que contém os dados a serem agru-pados possui um leve tracejado. (=) . Montaremos um lay-out contendo no cabeçalho os dados: “RequiredDate” e “CompanyName”. No corpo inserimos o “ProductID”, “ProductName”, “UnitPrice”, “Quantity” e um campo calculado com o “Total”, já no rodapé a soma total dos Produtos. Ver Lay-out proposto na Figura 10.

Veja a Figura 10.

- Formatação de Valores

Em se tratando de relatórios, sempre utilizamos a formatação de campos a

Page 23: The Club - megazine · Delphi, o Property Panel mostra a propriedade em negrito quando seu valor ... O Rave possui um compilador próprio para validar os scripts inseridos nos

fevereiro2013 23

fim de dar um visual mais atraente ao usuário, como por exemplo uma máscara numérica ou de Data. Veja exemplos a seguir:

Máscara Numérica: =format(Fields!UnitPrice.Value,”###,###,##0.00”).tostring()

Máscara Data: =format(Fields!RequiredDate.Value,”dd/MM/yyyy”).tostring()

A Função “Format()” tem como primeiro parâmetro o campo e o segundo o tipo de formatação desejada.

Estes valores são inseridos clicando com o botão direito sobre o campo escolhendo a opção “Expression...”. A expressão deverá ser inserida no campo “Set Expression for: Value”. Ver Imagem 11.

Figura 11: Formatando campos.

Faremos este procedimento para os campos “RequiredDate” (Data), “UnitPrice” (Valor Unitário), “Quantity” (Quantidade), “Total” e “Total Geral”.

- Campos Calculados

Os campos calculados são utilizados para obter valores sem a utilização de campos específicos em Banco de Dados. Podemos realizar operações de sub-tração, adição, multiplicação, divisão e funções específicas do relatório. Segue

exemplo a seguir utilizando o campo “Total” e “Total Geral” respectivamente.

Campo “Total”: =format(Fields!UnitPrice.Value*Fields!Quantity.Value, “###,###,##0.00”)

Campo “Total Geral”:=format(SUM(Fields!UnitPrice.Value*Fields!Quantity.Value), “###,###,##0.00”)

3-) Definindo o Preview do relatório

Adicionaremos alguns componentes na tela para efetuar a parametrização e a execução do relatório. Primeiramente adicione uma tabela para organização e em seu interior 2 Labels, 2 TextBoxes e um Button, para podermos filtrar por “OrderID”, que seria o número do pedido de venda. Já o componente Repor-tViewer é responsável pela visualização do relatório, junto com o Scriptmanager (componente necessário para execução de scripts) e o ObjectDataSource para inserção de alguns parâmetros. Ver Imagem 12.

Figura 12: Preview do Relatório.

Clique sobre o componente “ReportViewer” e na região superior direita clique na setinha (>) escolha “Choose Report” para vincular o relatório “Rpt-MestreDetalhe.rdlc” ao preview.

Configurando o ObjectDataSource

O “ObjectDataSource” serve para configurar diretamente com classes de acesso a dados, ou seja, neste caso apontaremos para a fonte de dados citado anteriormente. Para isto clique sobre o mesmo e escolha a opção “Configure Data Source”. A Figura 13 é onde definimos o objeto “DsMestreDetalhe”. Clique em “Next” para configuração da próxima etapa.

Veja a Figura 13.

Esta tela é muito importante, pois é nela onde são situados os métodos de Seleção, Atualização, Inserção e Exclusão. Em se tratando de relatórios

Figura 10: Relatório em desenvolvimento.

Page 24: The Club - megazine · Delphi, o Property Panel mostra a propriedade em negrito quando seu valor ... O Rave possui um compilador próprio para validar os scripts inseridos nos

fevereiro201324

Código 02

apenas utilizaremos o primeiro método, o de Seleção de Dados. Neste caso estamos retornando um objeto do tipo “DsMestreDetalhe” e inserindo dois parâmetros do tipo inteiro, o “COD_INI” e o “COD_FIM”, ambos responsáveis pela parametrização dos dados ali obtidos. Ver Imagem 14.

Figura 14: Definindo o método de Seleção de Dados.

Já na próxima etapa, na região esquerda, estão situados os “Parameters” (parâmetros Nome e Valor) junto com algumas configurações como: “Source” (fonte, a origem do parâmetro), “QueryStringField” (nome do parâmetro) e “DefaultValue” (valor padrão). Clique em “Finish” para terminar este processo. Ver Imagem 15 para maiores detalhes das configurações.

Veja a Figura 15.

Codificando o botão “Imprimir”

Antes de iniciarmos a codificação no evento “Click()” do botão importe a seguinte biblioteca:

using Microsoft.Reporting.

Figura 13: Definindo o objeto.

WebForms;

Esta biblioteca é responsável por alguns métodos utilizados ao decorrer do artigo.

protected void Button1_Click(object sender, EventArgs e){ ObjectDataSource1.SelectParameters[0].DefaultValue = TextBox2.Text; ObjectDataSource1.SelectParameters[1].DefaultValue = TextBox3.Text; ReportParameter[] parametros = {new ReportParameter(“ParamTitulo”,”The Club - O Maior Clube de Programadores do Brasil”),new ReportParameter(“ParamSubtitulo”,”Relatório de Venda - Mestre/Detalhe”) };

ReportViewer1.LocalReport.SetParameters(parametros); ReportViewer1.LocalReport.Refresh();}

Utilizamos o método “SelectedParameters” para filtrar os dados na ins-trução SQL. Faremos referência respectivamente com o primeiro e segundo TextBox. Já com a classe “ReportParameter” inserimos algumas informações

Figura 15: Definindo os Parâmetros.

Page 25: The Club - megazine · Delphi, o Property Panel mostra a propriedade em negrito quando seu valor ... O Rave possui um compilador próprio para validar os scripts inseridos nos

fevereiro2013 25

que mais adiante serão recuperadas dentro do próprio arquivo “.rdlc”. Temos um par chave/valor sendo o nome do Parâmetro seguido do valor. Para finalizar passamos os dados utilizando a função “SetParameters”.

Para atualizar os dados na tela usamos o “refresh()”. Para testar o exemplo pressione o botão F5 e teremos um resultado parecido com a Imagem 16.

Figura 16: Relatório.

Conclusão

O desenvolvimento de relatórios do Tipo “Mestre/Detalhe” é muito habi-tual para nós programadores, usando para diversas tarefas como: Pedidos de

Venda, Orçamentos de Produtos, entre outros tipos. A minha intenção neste ar-tigo foi de proporcionar um exemplo prático para a utilização do ReportViewer em uma página Asp.Net. Podemos também aplicar estes conhecimentos, sem muitos esforços, na plataforma Desktop por exemplo. O ReportViewer, na minha opinião, é uma das melhores ferramentas em se tratando de Desenvol-vimento de relatórios na plataforma “.Net”, tanto pela sua facilidade quanto pela grandiosidade de recursos encontrados. Vou procurar abranger em artigos futuros outros tipos de exemplos envolvendo esta ferramenta.

Um forte abraço e até o mês que vem!

[email protected]

Marcos César SilvaConsultor de Sistemas na consultoria de sistemas DataSmart e

Consultor Técnico do The Club, Bacharel em Ciência da Computação, MBA em Gestão Empresarial, Certificações MCAD (Microsoft Certified Application Developer) e MCSD.NET (Microsoft Certified Solution Developer .NET)

Sobre o autor

Page 26: The Club - megazine · Delphi, o Property Panel mostra a propriedade em negrito quando seu valor ... O Rave possui um compilador próprio para validar os scripts inseridos nos

fevereiro201326

Olá pessoal, no artigo deste mês eu vou continuar abordando o tema de estilos no FireMonkey, porém agora de uma forma que dê para utilizar nos formulários da aplicação inteira. No artigo publicado no mês de janeiro de 2013 na revista The Club, “Delphi XE2 – Trabalhando com estilos no

FireMonkey”, foi passada uma abordagem introdutória da facilidade de utilizar estilos e criar novos estilos ou apenas customizar padrão que acompanha a instalação do Delphi XE2. Porém até este momento vimos como alterar os estilos de apenas um formulário, e numa aplicação pode existir até centenas de formulários.

A pergunta que fica no ar agora é, “Como fazer para aplicar estilos em todos os formulários de uma, suposta, aplicação com 150 formulários?“. Não, não teremos que aplicar os estilos um a um, em cada formulário. A proposta deste artigo é bem simples, vamos criar um formulário base que receberá o estilo definido como padrão, e todos os demais formulários deverão herdar este formulário.

Exemplo prático

Bom, vamos ao que interessa, crie uma pasta onde será salvo o nosso projeto de exemplo, nomeie-a como achar melhor. Abra o Delphi XE2 e crie um novo projeto FireMonkey através do menu (File/ New/ FireMonkey HD Application - Delphi). Pressione as teclas Ctrl + Shift + S para salvar tudo, localize a pasta criada para armazenar o projeto e salve a primeira unit com o nome de unPrincipal, o nome do projeto será EstilosFMX. Altera a propriedade Name do primeiro formulário para frmPrincipal.

Vamos agora adicionar os estilos que disponibilizaremos junto com a nossa aplicação. Adicione ao formulário o componente TStyleBook (Standard). Dê um duplo clique no componente para abrir a tela onde iremos carregar arquivo de estilo (ver figura 1).

Veja a Figura 1. O Delphi por padrão distribui alguns arquivos de estilos prontos, para

localizar estes estilos abra a pasta Meus Documentos, depois acesse a pasta

Delphi XE2Trabalhando com estilos no FireMonkey – Parte II

RAD Studio, dentro desta última tem a pasta referente à versão do Delphi XE2 que é a pasta 9.0, abra esta pasta e por fim acesse a pasta Styles, o caminho completo é (C:\Documents and Settings\Suporte I\Meus documentos\RAD Studio\9.0\Styles).

Após clicar no botão Load, localize a pasta de estilos do Delphi XE2 e adi-cione o primeiro arquivo, Air.style, selecionando-o e clicando em abrir. Depois de carregar as propriedades do estilo clique em Apply and Close. De volta ao formulário vamos nomear o componente StyleBook1 para sbAir.

Desta forma já configuramos um estilo para nossa aplicação, porém, vamos adicionar os demais estilos também para possibilitar o usuário final fazer a escolha do estilo padrão a ser utilizado. Portanto adicione mais 11 componentes TStyleBook ao frmPrincipal, e seguindo as informações da tabela 1 configure os demais componentes TStyleBook repetindo o processo utilizado para configurar o estilo no componente sbAir.

Nota: na tabela 1 tem um estilo chamado MeuEstilo.style, este estilo foi criado num exemplo anterior criado para o artigo “Delphi XE2 – Trabalhando com estilos no FireMonkey” da revista The Club do mês de janeiro de 2013. Não há problemas se não tiver este arquivo, pode adicionar os estilos que achar necessário.

Figura 1 – Carregando o arquivo de estilo para o componente TStyleBook

Page 27: The Club - megazine · Delphi, o Property Panel mostra a propriedade em negrito quando seu valor ... O Rave possui um compilador próprio para validar os scripts inseridos nos

fevereiro2013 27

Ordem Propriedade Name Arquivo de estilo Descrição

0 sbAir Air.style Air

1 sbAmakrits Amakrits.Style Amakrits

2 sbAquaGraphite AquaGraphite.style AquaGraphite

3 sbBlend Blend.Style Blend

4 sbDark Dark.style Dark

5 sbGoldenGraphite GoldenGraphite.Style GoldenGraphite

6 sbiOS iOS.Style iOS

7 sbMacBlue MacBlue.Style MacBlue

8 sbMacGraphite MacGraphite.Style MacGraphite

9 sbMeuEstilo MeuEstilo.style MeuEstilo

10 sbRubyGraphite RubyGraphite.style RubyGraphite

11 sbWindows7 Windows7.Style Windows7Tabela 1 – Nomenclatura de componentes e arquivos de estilos utilizados no

exemplo

A coluna “Ordem” da tabela 1, representará o número pelo qual identi-ficaremos via código estes componentes posteriormente. Depois de adicio-nado todos os estilos, configurarmos a propriedade Name dos componentes TStyleBook teremos um formulário parecido com o que é exibido na figura 2.

Figura 2 – frmPrincipal depois de configurados todos os estilos

Para que a aplicação carregue o estilo, definido pelo usuário em tempo de execução é necessário criarmos um arquivo, onde o executável possa ler a informação que indica qual foi o estilo definido como padrão. Para isso, adicione ao frmPrincipal um TClientDataSet, dê o nome de cdsEstilos. Vamos adicionar um campo virtual para este cdsEstilos. Clique com o botão direito do mouse sobre o cdsEstilos e escolha a opção Fields Editor. No formulário que abrir clique com o botão direito do mouse no centro e escolha a opção New Field. Irá abrir um novo formulário para preencher as especificações do novo campo (ver figura 3). Informe o nome COD_ESTILO, o tipo Integer (inteiro), em Field Type marque a opção Data, pós isso é só confirmar pressionando o botão Ok.

Veja a Figura 3.

Depois de criado o campo COD_ESTILO, iremos configurar o cdsEstilos para acessar / gravar dados em um arquivo XML. Caso ainda não tenha executado o seu projeto, execute-o agora, para que seja criada uma estrutura de pastas dentro da pasta do seu projeto. Como o projeto está gerando executável para Win32, criou a seguinte estrutura dentro da pasta do projeto: (Win32\Debug), dentro da pasta Debug é onde será gerado o executável da nossa aplicação e é também onde teremos que criar o arquivo XML que servirá para armazenar

os dados do cdsEstilos.Voltando ao projeto do Delphi, clique com o botão direito do mouse no

cdsEstilos e escolha a opção Create DataSet. Dê um novo clique com o botão direito do mouse e selecione a opção Save to MyBase XML Table (ver figura 4), neste momento abrirá uma janela pedindo para informar o local onde salvará o arquivo XML, localize a pasta do projeto e vá até a pasta Debug, dê o nome de Estilos.xml.

Figura 4 - Salvando o arquivo XML para o cdsEstilos

Selecione novamente o cdsEstilos e agora vá até a janela Object Inspector e localize a propriedade FileName, insira o nome do arquivo XML, Estilos.xml. Desta forma já configuramos o ClientDataSet para acessar os dados, porém, agora devemos alimentar estes dados, para isso iremos criar um procedimento. Este procedimento será chamado de SetEstilo e fará a função alterar o estilo dos formulários em tempo de execução denominado SetEstilo. Neste procedimento passaremos o parâmetro Estilo do tipo inteiro, é nesta hora que temos que ter em mente o número de cada estilo definido na coluna Ordem descritos na da tabela 1. Outro parâmetro deste procedimento é a constante Create com o valor padrão False, servirá para identificar se o procedimento foi chamado pelo evento OnCreate do frmPrincipal.

Na sessão Public da unit escreva a declaração do procedimento, e depois

Figura 3 – inserindo campo virtual no cdsEstilos

Page 28: The Club - megazine · Delphi, o Property Panel mostra a propriedade em negrito quando seu valor ... O Rave possui um compilador próprio para validar os scripts inseridos nos

fevereiro201328

Listagem 1 - Procedimento SetEstilo

Listagem 2 - Manipulando o cdsEstilos e chamando o procedimento SetEstilo

Listagem 3 - Adicionando o estilo do formulário principal ao formulário base

escreva os códigos no corpo do procedimento como mostra a listagem 1.

//Declaração da procedure public { Public declarations } procedure SetEstilo(Estilo: Integer; const Create: Boolean = False);

//Corpo da procedureprocedure TfrmPrincipal.SetEstilo(Estilo: Integer; const Create: Boolean = False);begin { SE NÃO FOR CHAMADO PELO EVENTO ONCREATE ALTERA O VALOR NO CAMPO NO DATASET E SALVA A ALTERAÇÃO } if not Create then begin cdsEstilos.Edit; cdsEstilosCOD_ESTILO.AsInteger := Estilo; cdsEstilos.Post; end;

case Estilo of 0: StyleBook := sbAir; 1: StyleBook := sbAmakrits; 2: StyleBook := sbAquaGraphite; 3: StyleBook := sbBlend; 4: StyleBook := sbdark; 5: StyleBook := sbGoldenGraphite; 6: StyleBook := sbiOS; 7: StyleBook := sbMacBlue; 8: StyleBook := sbMacGraphite; 9: StyleBook := sbMeuEstilo; 10: StyleBook := sbRubyGraphite; 11: StyleBook := sbWindows7; end;end;

Podemos perceber que através do valor inteiro passado como parâmetro no cabeçalho da procedure verifica qual será o estilo a ser adicionado na propriedade StyleBook do frmPrincipal.

Não se esqueça de abrir o cdsEstilos no evento onCreate do frmPrincipal

e fechá-lo no evento onClose. Após abrir o cdsEstilos pode fazer a chamada do procedimento SetEstilo passando como parâmetro o campo COD_ESTILO e o valor True para o parâmetro Create da procedure. Segue o exemplo destes procedimentos na listagem 2.

// Evento OnCreate do frmPrincipalprocedure TfrmPrincipal.FormCreate(Sender: TObject);begin cdsEstilos.Open; SetEstilo(cdsEstilosCOD_ESTILO.AsInteger);end;

//Evento OnClose do frmPrincipalprocedure TfrmPrincipal.FormClose(Sender: TObject; var Action: TCloseAction);begin cdsEstilos.Close;end;

Adicione dois botões no frmPrincipal, altere suas propriedades Text para “Cadastro” e “Configurar Estilo”. Mais tarde utilizaremos estes botões para chamar as demais telas da nossa aplicação.

Criando o formulário Base de Estilos

Crie um novo formulário através do menu (File/ New/ FireMonkey HD Form - Delphi). Salve a unit com o nome unFrmBaseEstilo e nomeie o formulário com o nome frmBaseEstilo. Pressione as teclas Alt + F11 para chamar a janela Use Unit, e adicione a unPrincipal no formulário clicando em Ok.

No evento onCreate do formulário frmBaseEstilo informe que a proprie-dade StyleBook irá receber o StyleBook presente no frmPrincipal. Veja como isso é implementado na listagem 3.

procedure TfrmBaseEstilo.FormCreate(Sender: TObject);begin StyleBook := frmPrincipal.StyleBook;end;

Em nosso formulário base de estilo precisaremos apenas deste código, pois desta forma já será implementado o estilo em todos os formulários no momento em que forem criados em memória.

Criando o formulário base para cadastro

Page 29: The Club - megazine · Delphi, o Property Panel mostra a propriedade em negrito quando seu valor ... O Rave possui um compilador próprio para validar os scripts inseridos nos

fevereiro2013 29

Listagem 4 - Configurando a exibição da tela de cadastro de clientes

Listagem 5 - Chamando o procedimento SetEstilo para alterar o estilo em tempo de execução

Acesse o menu (File/ New/ Other) na janela que abrir acesse o nó Inheri-table Items e selecione o item com o nome frmBaseEstilo (ver figura 5) e clique em Ok. Salve a unit com o nome unFormBase e dê o nome de frmFormBase. Adicione alguns componentes básicos de cadastro como mostra a figura 6. Lembrando que não é necessário fazer nenhuma programação nestes compo-nentes, servirão apenas para exemplificar a alteração dos estilos.

Figura 5 – Criando uma herança do frmBaseEstilo

Figura 6 – Exemplo do formulário base de cadastro

Este formulário base de cadastro será utilizado herdado para cada novo formulário de cadastro padrão. Caso tenha algum outro formulário que não siga este padrão, terá que fazer a herança apenas do frmBaseEstilo, para que o estilo possa ser aplicado.

Acesse novamente o menu (File/ New/ Other) e agora selecione o frmFormBase e clique em Ok. Iremos criar um formulário para cadastro de clientes. Salve a unit com o nome unClientes e nomeie o formulário com o nome frmClientes. Adicione alguns componentes de edição como Edits e Grid. Não é necessário codificar nenhum componente. Neste momento já podemos acessar o frmPrincipal, adicionarmos a unClientes Através das teclas Alt + F11. Com a unClientes adicionada na Uses da unPrincipal podemos agora configurar o evento onClick do Formulário que irá chamar a tela de cadastro. Veja o exemplo na listagem 4.

procedure TfrmPrincipal.Button1Click(Sender: TObject);

begin frmClientes := TfrmClientes.Create(self); frmClientes.ShowModal; FreeAndNil(frmClientes);end;

Criando a tela de configuração de estilos

Como a tela de configuração de estilo não será no padrão de cadastro da aplicação ela será criada herdando apenas o formulário frmBaseEstilo. Novamente acesse o menu (File/ New/ Other) e escolha agora o frmBaseEstilo. Salve a unit com o nome unConfigEstilo e nomeie o formulário com o nome frmConfigEstilo. Adicione uma TLabel (Standard) um TComboEdit (Additio-nal). Dê o nome de cbeEstilos para o TComboEdit e adicione na propriedade Items os nomes dos estilos na mesma ordem presente na coluna “Descrição” exibido na tabela 1.

Ainda no frmConfigEstilo, faça uso na unit unPrincipal através das teclas Alt + F11. No evento onChange no cbeEstilos iremos chamar novamente o proce-dimento SetEstilo da unPrincipal e passar o valor do índice do item selecionado pelo usuário no cdeEstilos. Veja como implementar esta ação na listagem 5.

procedure TfrmConfigEstilo.cbeEstilosChange(Sender: TObject);begin inherited; frmPrincipal.SetEstilo(cbeEstilos.ItemIndex); StyleBook := frmPrincipal.StyleBook;end;

Após alterar o estilo do formulário principal, atribuímos esta alteração al formulário de configuração para também aderir ao novo estilo.

Para executarmos o teste ainda falta incluir a unit unConfigEstilo na unPrincipal, para podermos fazer a chamada deste formulário em tempo de execução através do botão “Configurar Estilo”. É importante lembrar que apenas o frmPrincipal deve ser criado automaticamente pelo Delphi, os demais serão criados em tempo de execução. Para configurar acesse o menu (Project/ Op-tions) e clique no nó Forms no painel da esquerda, na lista Auto Create Forms deixe apenas o frmPrincipal, os demais mova para a lista da direita, Available Forms. Clique em Ok para confirmar a configuração. Agora é só executar o projeto para ver em tempo de execução a alteração dos estilos configurados no componente TStyleBook. Veja na figura 7 o projeto em execução.

Veja a Figura 7.

Page 30: The Club - megazine · Delphi, o Property Panel mostra a propriedade em negrito quando seu valor ... O Rave possui um compilador próprio para validar os scripts inseridos nos

fevereiro201330

Figura 7 – Projeto de exemplo em tempo de execução

Conclusão

O padrão estilo de uma aplicação é o algo mais do desenvolvedor, todos sabemos que atualmente o usuário final busca mais do que apenas recursos e funcionalidades em um software. O FireMonkey veio com a promessa de deixar esta integração com o usuário mais amigável e atual, com vários recursos de imagens e estilos. Neste artigo vimos como implementar um padrão de estilos de uma só vez em todos os formulários da aplicação, utilizando a herança de formulários. Espero que tenha gostado e que este artigo venha a ser útil a você leitor. Um grande abraço a todos e até a próxima.

[email protected]

Lucas Vieira de OliveiraConsultor Técnico The Club.

Sobre o autor

Page 31: The Club - megazine · Delphi, o Property Panel mostra a propriedade em negrito quando seu valor ... O Rave possui um compilador próprio para validar os scripts inseridos nos

fevereiro2013 05

Page 32: The Club - megazine · Delphi, o Property Panel mostra a propriedade em negrito quando seu valor ... O Rave possui um compilador próprio para validar os scripts inseridos nos

fevereiro2013