Você está na página 1de 32

fevereiro

2013
fevereiro
2013
04 05
Curso Delphi
Parte IX

Editorial Autor: Luciano Pimenta

10 20
TMaskeditHV Asp.Net
Um TMaskedit em Potencial para Automação de Relatório Mestre-Detalhe
Cadastros – Parte 1 com ReportViewer

Autor: Hamden Vogel Autor: Marcos César


Silva

26
Delphi XE2
Trabalhando com estilho FireMonkey - Parte II

Índice Autor: Lucas de Oliveira

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


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.
Dica Rápida

03
fevereiro
2013
Editorial
Olá amigos do The Club!
Av. Profº Celso Ferreira da Silva, 190
Jd. Europa - Avaré - SP - CEP 18.707-150
Informações e Suporte: (14) 3732-1529

Internet
http://www.theclub.com.br
Cadastro: cadastro@theclub.com.br
Suporte: suporte@theclub.com.br
Fevereiro, mês do Carnaval, é tempo de brincar e festejar com os Informações: info@theclub.com.br

amigos. Mas como nem tudo na vida é festa, aqui estamos na nossa Skype Cadastro: theclub_cadastro
Skype Suporte: theclub_linha1
segunda edição do ano de 2013! theclub_linha2
theclub_linha3

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. www.twitter.com/theclubbr

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.
Copyright The Club 2013

Luciano Pimenta encerra o curso sobre Delphi, na sua última parte, Diretor Técnico
trabalha com Geradores de Relatórios “Rave Reports”, dando dicas e Marcos César Silva

exemplos práticos para um maior aprendizado. Diagramação


Vitor M. Rodrigues

Neste mês, iremos dar boas vindas para um novo colunista, Hamden Design
Vogel, sendo que neste artigo inicial foi desenvolvido um componente Vitor M. Rodrigues

chamado “TMaskeditHV”, possibilitando inúmeras formatações e con- Revisão


figurações de máscaras para Cadastros em geral. Marcos César Silva, Cintia Amaral

finaliza a revista utilizando o gerador de relatórios “ReportViewer”, Colunistas


desenvolvendo para esta edição um exemplo de relatórios “Mestre/ Hamden Vogel
Lucas de Oliveira
Detalhe” em conjunto com aplicações ASP.Net. Luciano Pimenta
Thiago Cavalheiro Montebugnoli
Marcos César Silva
Fiquem a vontade para nos mandar sugestões para artigos futuros,
para que possamos cada vez os ajudar melhor! Juninho
José Antonio P. M. de Paula

Impressão e acabamento:
GRIL - Gráfica e Editora
Um Forte Abraço! Taquarituba-SP - Tel. (14) 3762-1345

Reprodução
A 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

Thiago Montebugnoli - Editor Chefe


Megazine” são terminantemente proibidos sem autorização
escrita dos titulares dos direitos autorais.

thiago@theclub.com.br Delphi é marca registrada da Borland International,


as demais marcas citadas são registradas
pelos seus respectivos proprietários.

04
fevereiro
2013
Curso Delphi Parte IX

V
imos no artigo anterior, um pequeno exemplo de como criar Na Figura 1 temos o IDE do Rave,
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 Figura 1. IDE do Rave Reports
precisamos modificar algum relatório, será necessário recompilar e distribuir
o nosso executável. 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
Isso não acontece com o Rave Reports (www.nevrona.com/rave), que Inspector) à esquerda.
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: Page
• Pode conectar diretamente ao banco de dados ou usando compo- É no Page, parte central do IDE, onde todos os objetos do relatório são
nentes do Delphi; colocados, ou seja, onde o relatório é desenhado. Um relatório pode possuir
• Exporta nativamente relatórios para os formatos: RTF, HTML, PDF várias Pages, que podem ser acessadas a partir do Project Tree (equivalente
e texto; ao Object TreeView) conforme podemos ver na Figura 2.

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. Figura 2. Pages e como acessa-los através do Project Tree

05
fevereiro
2013
Property Panel Componentes do Rave Reports

No Property Panel é possível acessar e alterar as propriedades de todos O Rave possui vários componentes visuais e não-visuais, para serem usados
os componentes do relatório. Bastante semelhante ao Object Inspector do no relatório. Além disso, podemos adicionar novos componentes na IDE (no
Delphi, o Property Panel mostra a propriedade em negrito quando seu valor site do desenvolvedor, temos outros componentes).
padrão é alterado, como podemos ver na Figura 3.
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 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 Figura 5. Region e bands do primeiro relatório
de uma aplicação Delphi são listadas no Data View Dictionary.
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
Rave Event Editor um novo ClientDataSet no DMPesquisa que retorne todos os dados do cliente,
pois queremos fazer uma listagem, portanto, sem filtro. Adicione um RvDa-
O Rave possui uma linguagem de script própria que permite a implemen- taSetConnection e configure sua propriedade DataSet para o componente
tação de eventos nos relatórios, a Rave Language. O Event Editor é onde toda adicionado anteriormente.
a codificação dos eventos é realizada e onde definimos quais eventos serão
implementados. 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
O Rave possui um compilador próprio para validar os scripts inseridos nos Data View, pois vamos usar a fonte de dados constante no nosso projeto Delphi.
eventos, o RaveCC compiler.

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

Figura 8. Editor do DataText


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 no relatório. Voltando ao DataBand, vamos adicionar alguns DataTexts e usan-
as características dos controles Band e DataBand, se devem ser cabeçalhos, ca- do a propriedade DataView e DataField, configurar os campos do relatório.
beçalho de grupo, grupo, detail etc. Acesse a propriedade BandStyle do Band1.
Podemos testar o relatório, utilizando a tecla F9. Veja que as informações
Altere para Body Header e feche o editor. Na propriedade ControllerBand do relatório estão aparecendo no Rave.
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. Chamando o relatório do Delphi
No Band1 você pode colocar as informações que achar necessário, pois Adicione um novo botão no formulário principal e utilize o seguinte código
o mesmo é o cabeçalho do nosso relatório. Veja na Figura 7, a configuração para chamar o relatório criado anteriormente:
do cabeçalho.
Listagem 01

RvProject1.ExecuteReport(‘rptLi
stagemCliente’);

Não esqueça de indicar o nome do arquivo RAV no RvProject usando a


Figura 7. Cabeçalho do relatório propriedade ProjectFile. Teste e veja o relatório em execução (Figura 9).

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.
Figura 9. Relatório criado no Rave
Na opção Data Text, digite: “Impresso em: ” & Report.DateShort. Assim,
informamos a data da impressão. Adicione outro DataText no relatório e Vimos que é fácil usar o Rave no Delphi. Em um primeiro momento, você
configure com o seguinte valor: “Página ” & Report.CurrentPage & “ de ” & que nunca usou Rave e esta acostumado com o Quick, pode achar meio estra-
Report.TotalPages. nho, mas com o tempo, verá que temos vantagens em usar Rave.

Nossa informação será a página atual e a quantidade de páginas existente Para criar um relatório parametrizado, usando controles do Delphi, é muito

07
fevereiro
2013
simples, basta usar o mesmo DataSource que esta parametrizado como fonte Agora vamos criar uma consulta, baseada nessa conexão com o banco.
de dados do relatório. Faça um exemplo, para o relatório do cliente. 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.
Crie um relatório, usando o cdsCliente que esta parametrizado com o
cliente que esta em tela. Listagem 02 - SQL do Relatório

SELECT e.sNmEmpregado,
Nota: teremos algumas modificações para isso. No formulário e.tDtAdmissao, e.nPcComissao,
base, adicione um botão responsável por chamar o relatório. Crie um s.sNmSetor
método chamado no Click desse botão. Esse método será virtual, pois FROM EMPREGADO e
será subscrito nos formulários herdados. INNER JOIN SETOR s on
s.nCdSetor = e.nCdSetor
WHERE e.nCdEmpregado =
Ao chamar o relatório no cadastro de clientes, sempre será mostrado :EMPREGADO
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. Acesse a propriedade QueryParams do Driver Data View e adicione o
seguinte código:

Repassando parâmetros pela aplicação


EMPREGADO=Param.pEmpregado
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). 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
Figura 8. Conexões do Rave 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
Vamos usar a opção ADO para SQL Server (poderíamos também usar o inicial a unit RvDLADO.
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).
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.
Figura 9. Tabelas listadas no Rave

08
fevereiro
2013
Em GroupDataView indique o DataView do relatório. Por fim, em Grou- relatório, semelhante aos que já criamos até aqui. Adicione dois DataBands
pKey, indique o campo que queremos agrupar, nesse caso sNmSetor. Agora, e configure os mesmos para cada um dos Driver Data Views criados ante-
basta acessar o relatório para ver as modificações e ter a listagem agrupada riormente.
pelo tipo (Figura 10).
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 10. Relatório de listagem agrupado

Relatório master/detail de vendas

Agora, precisamos criar um relatório master/detail, para que possamos Figura 11. Relatório master/detail da OS
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. Conclusão

Crie um novo Driver Data View e use o comando SQL da Listagem 3. 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
Listagem 03 - Pesquisando os dados da OS caminho profissional com Delphi.

SELECT nCdVenda, nVlVenda, Um grande abraço a todos!


tDtVenda
FROM VENDA
WHERE nCdVenda = :VENDA

Sobre o autor
Na propriedade QueryParams do Driver Data View adicione o seguinte
código: Luciano Pimenta
Luciano 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.
VENDA=Param.pVenda É 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.
Para a próxima consulta, use o código da Listagem 4.

Listagem 04 - Retornando os itens da OS www.lucianopimenta.net


SELECT p.sNmProduto, i.nCdVenda,
i.nQtItem, i.nVlItem,
(i.nQtItem * i.nVlItem) as
SubTotal CD de Treinamento em RAD Studio 2007:
FROM ITENS i www.lucianopimenta.com/post.aspx?id=165.
INNER JOIN PRODUTO p ON
i.nCdProduto = p.nCdProduto DVD de treinamento em Visual Web Developer:
WHERE i.nCdVenda = :VENDA www.lucianopimenta.com/post.aspx?id=166.

Curso de Windows Forms:


Configura a propriedade QueryParams conforme a anterior. Crie um novo www.lucianopimenta.com/post.aspx?id=167.

09
fevereiro
2013
TMaskeditHV
Um TMaskedit em Potencial para Automação de Cadastros
Parte 1

H
oje em dia, existe componentes em Delphi para todas ou Combobox (TCheckListComboBox);
senão quase todas as finalidades. O programador tem a 9. Suporte ao componente TAdvSmoothMessageDialog da TMS
ampla escolha de selecionar um componente ou pacote Software (componente pago) para novo design do messagebox utilizado;
de avaliação comercial como alternativas (e muito boas) funcionalidade opcional selecionada na diretiva de compilação Define (padrão
freewares, que muitos deles fazem praticamente a mesma – desabilitado);
coisa dos seus concorrentes pagos. E ainda existem muitos sites abrigando 10. Evento OnExecuteWhenEnterKeyIsPressed para responder junta-
projetos opensource contendo código-fontes, componentes, dll´s e projetos mente à tecla Enter e Eventos OnCustomChanging e OnCustomChangingExit,
disponíveis para sua utilização, a maioria principalmente para uso doméstico, chamados quando na manipulação de combos customizáveis no evento On-
mostrando boas idéias aliado a boas práticas de programação. Change e no evento OnExit, respectivamente;
11. Importação automática de qualquer DataSet (qualquer descendente
O que se mais valoriza, no entanto, é a criatividade para solucionar pro- de TDataSet) – disponível na propriedade CustomDataSet – associando um
blemas a um baixo custo justificável ao processo de desenvolvimento e manu- atributo do mesmo para a propriedade CustomFieldNamePassedAsObjectStrin-
tenção de softwares, particularmente no nosso caso a linguagem ObjectPascal gParameter (campo String) e também para a propriedade CustomFieldName-
da famosa IDE chamada Delphi. PassedAsObjectIntegerParameter (campo Integer) – o propósito é “absorver”
todo o DataSet para dentro do componente, como se tivesse feito um loop
Este componente tem a missão de automatizar muitas coisas elementares automático, poupando este trabalho do programador e ainda disponibilizando
funcionais em um processo de cadastro, como por exemplo: o índice destes dados, conforme item 6 anterior;
12. Propriedade ButtonClickedWhenEnterKeyPressed, para que um
1. Carregamento em uma Combobox de Dados Específicos, como objeto botão (TButton) seja associado com o componente, a fim de ser exe-
Profissão, Escolaridade, Estados, Cidades associadas ao Estado selecionado, cutado o seu evento OnClick quando na saída do formato customizável com
etc; combo no evento OnExit;
2. Cep validado ao Estado selecionado, Validação de Data/Hora, CPF, 13. Suporte a extensão de dados para novos carregamentos em
etc; Comboboxes: Isso já é feito para os formatos Estados, Cidades, Profissões e
3. Formatação e Validação de: Data, Hora, CPF, CNPJ, CEP, Email, Pro- Escolaridade – através da compilação de dentro do componente e extração
fissão, Escolaridade, Moeda, Telefone, Estado, Cidade e Formato Customizável em arquivos de recurso (via XML) para sua leitura no Combobox de dentro do
(custom); componente;
4. Alteração da Cor do Foco em Modo de Edição e Restauração da Cor 14. Suporte a um Calendário embutido;
na saída do Foco; 15. Suporte a um Hint personalizado disponível na propriedade TextHelp
5. Processamento Automático da Tecla Enter, seguindo a Ordem de (do tipo TStrings);
Tabulação dos Controles (mapeados com TabOrder); 16. Validações de entrada MaskInteger e MaskFloat para validação
6. Carregamento de um Combobox opcional para dados importados somente de dados inteiros e numéricos com ponto flutuante, respectivamente;
de um ClientDataSet, podendo obter inclusive seus índices; 17. Método CheckEmptyField para obrigar seu preenchimento;
7. Crítica “silenciosa” ou com mensagens customizadas para validação 18. Suporte Db-Aware através das propriedades DataSource e Data-
de tipos de formatos e fórmulas, incluindo sua entrada de dados incompleta Field;
(campo vazio/inválido) - É possível customizar ainda tipos de mensagens de 19. Repintura do componente (via Canvas) chamadas nos eventos do
erro e o nome do campo; Mouse (Enter/Leave) – desenhada nas mensagens do Windows (TMessage):
8. Suporte a dois tipos de Combobox imbutidos, o padrão (TCombo- CM_MOUSEENTER, CM_MOUSELEAVE, WM_SETFOCUS, WM_KILLFOCUS,
box) e um Combobox com seleção múltipla – semelhante a um Checkbox com WM_NCPAINT;

10
fevereiro
2013
20. Texto de aviso ao seu Preenchimento (“Favor Preencher”) – exibido exibir um texto do campo no combobox de seleção, obviamente.
no componente quando informado na propriedade DefaultTextInCustomFor-
mat e na propriedade booleana DefaultTextInCustomFormatShow configurada 3 – Associar na propriedade ButtonClickedWhenEnterKeyPressed um
como True; botão (TButton) que será chamado seu evento OnClick sempre que a tecla
21. Recurso alternativo de carregamento de dados para demais Enter for pressionada no componente. Funciona assim, no evento OnExit do
TMaskedithv’s ou outros componentes, através da propriedade disponível componente o clique do botão será executado, sempre que esta propriedade
CustomDataSet (descendente de TDataSet), extraindo dados assim como de for chamada. O importante é facilitar o desenvolvimento simplificando as
um objeto TDataSet qualquer. chamadas de eventos e teclas chamadoras destes eventos como as teclas
Enter/Tab nos eventos OnKeyPress, por exemplo.
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).

Figura 01: Ilustração do Demo 01 – Carregamento de Dados através do Maske-


Mãos a Obra: Instalando o Componente ditHV. 100% MaskeditHV.

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 Para este primeiro exemplo de demonstração, será utilizado um banco
“Component” -> “Install Component”, procurar ele no “browse” e finalmente de dados Firebird (versão 2.5) chamado “USUARIOS.FDB”; este banco contém
instalá-lo, sem mistério algum. apenas uma tabela, que é a “Usuarios”, relevante para o nosso material de
O componente foi desenvolvido em Delphi 7; acredito que em versões estudo de integração com o mapeamento de datasets para o nosso com-
posteriores não haverá problema algum; não pode-se afirmar que em versões ponente. Também foi utilizado a conexão DBExpress; todos os arquivos de
anteriores o componente funcionará 100% como esperado por falta de testes. configuração (ini’s, dll’s, etc) são distribuídos juntamente com o exemplo, bem
TMaskeditHV é um descendente de TMaskedit da Borland; o “HV” seguinte como os arquivos do banco.
são as iniciais do meu nome (Hamden Vogel).
Primeira coisa a notar é a validação de campo preenchido (método boo-
leano IsEmpty), como por exemplo:
Demo 01: Carregando um registro via ClientDataSet e preen-
chendo pelo componente através de seus formatos customizados
if MaskEditHV1.IsEmpty then Exit;
Existem muitas formas de carregar dados via Combobox, alimentados Como implementei essa validação “sob demanda”, este método boolea-
provenientes de uma fonte de dados. O componente possui uma importante no suportará nesta versão apenas dois formatos (mskftCustom e mskftCpf).
propriedade chamada CustomDataSet em que ela “absorve” qualquer des- Logicamente e propositadamente (para forçar aos demais que implementem
cendente de TDataSet a fim de mostrar no TMaskeditHV como uma seleção o restante) é possível estender aos outros formatos, de acordo com o código-
de dados. Funciona assim: -fonte listado abaixo, que trata deste método. Observe que utiliza outras
validações embutidas internas.
1 – Especifico a fonte de dados e associo na propriedade CustomDataSet;
Código 01
2 – Verifico os campos desta fonte associada às propriedades CustomField-
NamePassedAsObjectStringParameter e CustomFieldNamePassedAsObjectIn- function TMaskEditHV.IsEmpty:
tegerParameter, onde são vinculados atributos desta fonte dados do tipo String Boolean;
e Integer, respectivamente. O propósito disto é uma associação automática begin
destas informações quando na sua exibição (na forma de seleção, quando Result := False;
configurada a propriedade booleana ShowCombo como “True” e o formato
do tipo mskftCustom). O componente tentará obter o primeiro atributo en- case Self.Formate of
contrado nestes tipos de dados, na ordem especificada pelo próprio dataset. mskftCpf: Result :=
Se não for encontrado nenhum, ele retorna vazio. Mas tenha em mente que (EmptyCPF) or (Self.Text =
será necessário, caso contrário não se conseguirá obter seu índice (algo como EmptyStr) or (Self.Text = Self.
integer(fComboCustomText.Objects[customCombo.itemIndex])) nem como DefaultTextInCustomFormat);

11
fevereiro
2013
mskftCustom: Result AsString, True);
:= (Self.Text = EmptyStr) mskCEP.LoadText(Fields[6].
or (Self.Text = Self. AsString);
DefaultTextInCustomFormat); mskSetor.Text := Fields[7].
end; AsString;
end; mskTelefone.
LoadText(Fields[8].AsString);
mskSalario.Text :=
Fields[9].AsString;
Código-fonte do carregamento de dados oriundos de um TDataSet interno mskMatricula.Text :=
do componente: Fields[11].AsString;
end;

Código 02

with MaskEditHV1.CustomDataSet
do Observação: Para os formatos mskftDateTime e mskftDate,
begin pode-se utilizar a função da Borland FormatDateTime para atribuição
Filter := ‘NOME = ‘ + ao componente também:
QuotedStr(MaskEditHV1.Text);
Filtered := True;
edtCDUsuario.Text :=
Fields[0].AsString; mskDataNascimento.Text := FormatDateTime(‘dd/mm/yyyy’,
edtNome.Text := Fields[1]. StrToDateDef(Usuario.DataNascimento, Now));
AsString;
edtDtNascimento.Text := Existem outros dois métodos importantes para popular os componentes,
Fields[2].AsString; são os “LoadText” (que basicamente o que ele faz é setar a propriedade do
edtEndereco.Text := TMaskedit da Borland EditMask para “”, recebe o dado e logo após recupera
Fields[3].AsString; o valor da EditMask novamente) e o poderoso método LoadValueByIndex,
edtCidade.Text := que é o nosso “carregador” de dados através do seu índice. Abaixo segue o
Fields[4].AsString; código-fonte deste método:
edtEstado.Text :=
Fields[5].AsString;
Código 03
edtCEP.Text := Fields[6].
AsString; procedure TMaskEditHV.
edtSetor.Text := Fields[7]. LoadValueByIndex(const Value:
AsString; string; const bNoVisible:
edtTelefone.Text := Boolean = True);
Fields[8].AsString; begin
edtSalario.Text := if (Value = EmptyStr) or
Fields[9].AsString; (StrToIntDef(Value, 0) = -1)
edtMatricula.Text := then Exit;
Fields[11].AsString;
if (fCheckState) or
mskCDUsuario.Text := (fCheckCity) or (fCheckProfissao)
Fields[0].AsString; or (fCheckEscolaridade) then
mskNome.Text := Fields[1]. begin
AsString; GetList(bNoFocus,
mskDtNascimento.Text := bNoVisible);
Fields[2].AsString; combo.Visible := False;
mskEndereco.Text := combo.ItemIndex := combo.
Fields[3].AsString; Items.IndexOfObject(TObject(Str
mskCidade. ToIntDef(Value, 0)));
LoadValueByIndex(Fields[4].
AsString, True); Self.Text := combo.
mskEstado. Items.Strings[combo.ItemIndex];
LoadValueByIndex(Fields[5]. end

12 fevereiro
2013
else then fStringList := TStringList.
begin Create;
if (fCheckCustom) then Conexao.ObterDataSet(Abr
begin eRecordSetSomenteUsuarios,
if bNoVisible then fStringList);
GetCustomListNoVisible(‘ Result := fStringList;
‘, bNoFocus) end;
else
GetCustomList(‘ ‘, MaskeditHV1.TextCustomCombo :=
bNoFocus); Usuario.CarregaUsuarios;
MaskeditHV1.LoadCustomList;
customCombo.Visible :=
False;
if (StrToIntDef(Value, 0) Este fonte é apenas uma maneira de associar esta propriedade TextCus-
> customCombo.Items.Count) then tomCombo; por ser ela do tipo TStrings pode-se (conforme exemplo acima)
customCombo.ItemIndex criar suas próprias funções de acesso a dados para associar a esta proprie-
:= -1 dade em seguida; imagine um projeto orientado a objetos em no mínimo
else 3 camadas cuja camada de apresentação seja direcionada à população dos
customCombo.ItemIndex dados ao nosso componente – utilizando esta propriedade é fácil carregar
:= StrToIntDef(Value, 0); os dados em abstrato:

Self.Text := 1 – associar esta propriedade TextCustomCombo (pode-se inclusive abrir


customCombo.Items. esta propriedade em tempo de design e inserir os dados manualmente nela
Strings[customCombo.ItemIndex]; lá – por ser do tipo TStrings, como um Memo, é possível armazenar como
end; um contêiner);
end; 2 – setar a propriedade formate para mskftCustom;
end; 3 – setar a propriedade ShowCombo para True;
4 – Via código, chamar a procedure LoadCustomList;

Este método tem a importante missão de recuperar um valor via índice Fazendo isso, os dados serão exibidos em um combobox assim que o
passado como parâmetro do objeto armazenado – no caso ele vai carregar componente for clicado (veja próximo Demo abaixo):
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- Demo 02: Obtendo o valor através do índice como parâmetro
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 Passando o índice via método LoadValueByIndex pode-se recuperar o valor
linha é a mais importante: (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,
combo.ItemIndex := combo.Items.IndexOfObject(TObject(StrToIntDef do programa de exemplo correspondente:
(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:

Código 04

function TUsuario.
CarregaUsuarios: TStringList;
begin Figura 02: Ilustração do Demo 02
if not Assigned(fStringList)

13
fevereiro
2013
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- bNoFocus);
contrado retornará -1. customCombo.Visible :=
False;
Segue abaixo o código-fonte da função GetIndex:
if customCombo.itemIndex >
-1 then
Código 05
if
function TMaskEditHV.GetIndex: Assigned(fComboCustomText.
integer; Objects[customCombo.itemIndex])
begin then
Result := -1; Result :=
// if (fComboCustomText.Count integer(fComboCustomText.
= 0) then Exit; Objects[customCombo.itemIndex])
else if
if Assigned(fcustomchanging) Assigned(customComboCheckList)
then then
begin Result :=
if integer(fComboCustomText.
Assigned(fComboCustomText) then Objects[customComboCheckList.
if (customCombo.itemIndex > itemIndex])
-1) then else
if (customCombo.Visible) or Result := customCombo.
not (Self.IsEmpty) then Items.IndexOf(Self.Text);
if end;
Assigned(fComboCustomText. end;
Objects[customCombo.itemIndex])
then
begin
Result := Com isso os índices poderão ser fornecidos como parâmetros ou serem
integer(fComboCustomText. recuperados, através dos métodos LoadValueByIndex (procedure) e GetIndex
Objects[customCombo.itemIndex]); (function: integer), respectivamente. Semelhantemente à função IndexOfOb-
Exit; ject (TStrings) para LoadValueByIndex e a propriedade ItemIndex (TComboBox)
end; para GetIndex, em síntese abaixo:
end;
TMaskeditHV Delphi
if (Self.Text = EmptyStr)
then Procedure LoadValueByIndex Function IndexOfObject
begin Function GetIndex Propriedade ItemIndex
Result := -1;
Exit;
end;
Demo 03: Obtendo o valor através do índice como parâme-
if (fCheckState) or tro – parte II
(fCheckCity) or (fCheckProfissao)
or (fCheckEscolaridade) then Este próximo Demo é quase que uma revisão do exemplo anterior para
begin o formato mskftCustom. A novidade é o formato mskftState, que é o formato
GetList(bNoFocus,True); para exibição automática dos Estados Federativos (UF), cujos dados foram
combo.Visible := False; compilados de dentro do nosso componente através de arquivos de recurso
if combo.Items.IndexOf(Self. – sendo inclusos em uma linha logo depois da cláusula implementation, como
Text) > -1 then por exemplo {$R temp.res}.
Result := integer(combo.
Items.Objects[combo.Items. Funciona assim:
IndexOf(Self.Text)]);
end 1 – Criar um arquivo com extensão .rc, por exemplo “estados.rc”;
else if (fCheckCustom) then 2 – Escrever (no nosso exemplo) a seguinte linha neste arquivo “ESTADOS
begin RCDATA DISCARDABLE “estados.xml”;
GetCustomListNoVisible(‘ ‘, 3 – Abrir o utilitário da Borland chamado brcc32.exe (se você executou

14
fevereiro
2013
a instalação padrão do Delphi, estará localizado em “C:\Arquivos de progra- idéias podem ser estendidas e aperfeiçoadas; uma DLL ou arquivo serializado
mas\Borland\Delphi7\Bin”. Só que para executá-lo, não basta apenas dar um ao invés de um XML temporário, etc;
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” Vamos ilustrar com este demo, o funcionamento do componente para o
(sem aspas); formato do tipo “estado” (mskftState); a ênfase se dará neste enfoque para
4 – Compilar pela linha de comando – utilitário brcc32 – “brcc32 -32 explicação passo a passo deste recurso:
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, Figura 03: Ilustração do Demo 03
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 Conforme figura acima (Demo 03), são apresentados os formatos mskft-
de recurso e carregar seus dados em um combo embutido de dentro do com- Custom e mskftState, respectivamente.
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. Sequência de Processamento dos formatos mskftState e
mskftCity:
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 A seguir vamos ilustrar como estes dois formatos se interagem. Não há
podemos procurá-lo e carregá-lo, respectivamente. Mas temos que utilizar mistério, a única coisa que deve ser notada aqui é que a propriedade MaskState
uma função completa para realizar todo o trabalho de uma vez, carregando deve ser preenchida contendo o MaskeditHV do formato mskftState:
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 O Combobox do formato de Estados (formate = mskftState) carregado com
função o arquivo original que foi compilado como recurso estará disponível no os estados (arquivo XML dos estados), esperando a tecla Enter para desaparecer
diretório especificado. Com base nele podemos popular uma combo temporária e setar o campo para o item selecionado;
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 Após a tecla Enter ser pressionada, o MaskeditHV do formato de cidades
importa para este processo, já que todos vão ser transformados em recursos do recebe o foco, e recebendo a tecla Enter, também carrega um Combobox, mas
mesmo jeito: define-se a estrutura, criando a estrutura e populando os dados, desta vez com as cidades filtradas pelo estado do campo anterior. Basicamente
e em seguida transformando em arquivos XML através de um ClientDataSet, é assim que o processo em conjunto funciona. É claro que esse filtro de cidades
associando sua propriedade “Assign Local Data” (clicando com o botão direito) não funcionaria se não tivesse um registro de “estado” selecionado.
com o dataset desejado e depois digitando o método SaveToFile, passando o
parâmetro dfXML do tipo TDataPacketFormat. Exemplo:

tempClientDataSet.SaveToFil
e(ExtractFilePath(Applicati
on.ExeName) +’ESTADOS.xml’,
dfXML);

Figura 04: Ilustração do Demo 04


Basicamente, é isto. Os dados serão lidos de um XML; cada formato msk-
ftCity, mskftState, mskftProfissao e mskftEscolaridade tem um XML próprio já Figura contendo o Demo 04, explicando o funcionamento entre estes dois
gerado e compilado no componente. A única desvantagem é que para a edição formatos (Estado/Cidade).
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 Portanto, podemos agora mostrar ao usuário a seleção de estado e cidades
a arquivos secundários; portabilidade e eficiência. Logicamente que novas desta forma dinâmica e prática. E sem trabalho nenhum para o programador,

15
fevereiro
2013
pois não vai escrever uma linha de código.
FOnDropUp :
TNotifyEvent;
Dois formatos de exibição de dados do Combobox embutido: FUpperCase : boolean;
cbftDefault (o padrão) e o cbftCheckList (seleção múltipla – como FKey : Word;
itens em checkboxes) FReadOnly : Boolean;
FColumns
Em muitos projetos que desenvolvi, sempre senti falta de um componente : integer;
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 Portanto, como disse anteriormente, implementei duas formas de seleção,
de itens de um combobox comum, dando a faculdade de escolher entre o por necessidade em meus projetos – pense o quanto interessante seria as pos-
formato já conhecido cbftDefault bem como o formato recém-implementado sibilidades de utilização de uma seleção múltipla – carrinho de compras, itens
cbftCheckList. desejados, itens a configurar, usuários selecionados, permissões selecionadas,
minhas opções especificadas, selecione as opções desejadas, etc; enfim, as
Primeiramente, vamos discorrer sobre o código-fonte desta implemen- possibilidades são infinitas.
tação:
Implementando esta funcionalidade de dentro do componente foi intensa-
mente simplificada para o desenvolvedor, pois basta alterar uma propriedade,
Código 06
a ComboboxFormate, setando-a para cbftCheckList ou cbftDefault, para exibir
type TComboboxFormate = o formato desejado – o componente vai alimentar a propriedade TextCus-
(cbftDefault, cbftCheckList); tomCombo que você alimentaria manualmente – com seus dados que serão
... exibidos nele. Segue um trecho abaixo do fonte relacionado à esta propriedade:
property ComboboxFormate:
TComboboxFormate read
FComboboxFormate write property TextCustomCombo:
SetComboboxFormate default TStrings read fComboCustomText
cbftDefault; write SetComboCustomText;

procedure TMaskEditHV.
GetList(const NoFocus: Boolean A procedure LoadDataSet executa a funcionalidade de carregar interna-
= false; const bNoVisible: mente um DataSet para dentro da propriedade CustomDataSet.
Boolean = false);
begin Segue abaixo o código-fonte completo desta procedure LoadDataSet:

case FComboboxFormate of // Código 07


cbftDefault, cbftCheckList
cbftCheckList: combo := procedure TMaskEditHV.
TCheckListComboBox.Create(Self); LoadDataSet;
cbftDefault: combo := var
TComboBox.Create(Self); indexCustomFieldNameP
end; assedAsObjectStringParameter,
… indexCustomFieldName
if FComboboxFormate = PassedAsObjectIntegerParameter:
cbftCheckList then integer;
customComboCheckList tempClientDataSet:
:= TCheckListComboBox (combo); TClientDataSet;
… Field: TField;
type begin
TCheckListComboBox = if Assigned(FCustomDataSet)
class(TComboBox) then
private begin
{ Private declarations } if not
FOnSelect : Assigned(tempClientDataSet)
TNotifyEvent; then
FDDForm : TForm; tempClientDataSet :=
FListBox : TListBox; TClientDataSet.Create(Self);
FHorzScrollBar : boolean; try

16
fevereiro
2013
(fCustomFieldNamePassed
tempClientDataSet :=
AsObjectStringParameter);
TClientDataSet(FCustomDataSet);
if Assigned(Field) then
except on E: Exception do
begin
indexCustomFieldNamePassed
callMessageBox(Pchar(E.
AsObjectStringParameter
Message), ‘Erro’, MB_OK + MB_
:= Field.DataSet.Fields.
ICONERROR);
IndexOf(Field)
FCustomDataSet := nil;
else
end;
end;
indexCustomFieldNamePassed
end
AsObjectStringParameter := -1;
else
end;
Exit;
end;
if Assigned(tempClientDataSet)
if (fCustomFieldNamePassed
then
AsObjectIntegerParameter <>
try
EmptyStr) then
tempClientDataSet.Open;
begin
except on E:Exception do
Field := tempClientDataSet.
begin
FindField (fCustomFieldName
callMessageBox(Pchar(‘Erro
PassedAsObjectIntegerParameter);
ao Abrir DataSet:’ +#13#10+E.
if Assigned(Field) then
Message), ‘Erro’, MB_OK + MB_
indexCustomFieldNamePassed
ICONERROR);
AsObjectIntegerParameter
FCustomDataSet := nil;
:= Field.DataSet. Fields.
Abort;
IndexOf(Field)
end;
else
end;
begin
fCustomFieldNamePassed
indexCustomFieldNamePassed
AsObjectIntegerParameter := Get
AsObjectStringParameter := 0;
NextFieldNameFromSelectedType(S
//default
tInteger);
indexCustomFieldNamePassed
Field :=
AsObjectIntegerParameter := 1;
tempClientDataSet.FindField
//default
(fCustomFieldNamePassedA
sObjectIntegerParameter);
if (fCustomFieldNamePassed
if Assigned(Field) then
AsObjectStringParameter <>
EmptyStr) then
indexCustomFieldNamePassed
begin
AsObjectIntegerParameter
Field := tempClientDataSet.
:= Field.DataSet.Fields.
FindField(fCustom
IndexOf(Field)
FieldNamePassed
else
AsObjectStringParameter);
if Assigned(Field)
indexCustomFieldNamePassed
then indexCustomFieldName
AsObjectIntegerParameter := -1;
PassedAsObjectStringParameter
end;
:= Field.DataSet.F ields.
end;
IndexOf(Field)
else
//
begin
ShowMessage(tempClientDataSet.
FieldDefs.Items[0].FieldClass.
fCustomFieldNamePassedAsObject
ClassName);
StringParameter := GetNextField
NameFromSelectedType(StString);
if (indexCustomFieldNamePassed
Field :=
AsObjectStringParameter = -1)
tempClientDataSet.FindField

17
fevereiro
2013
or (indexCustomFieldNamePassed tempClientDataSet.
AsObjectIntegerParameter = -1) DisableControls;
then Application.ProcessMessages;
begin while not tempClientDataSet.
callMessageBox(‘Erro ao Eof do
localizar Atributos.’,’Erro’, begin
MB_OK + MB_ICONERROR); if not
Exit; (tempClientDataSet.Fields
end; [indexCustomFieldNamePassed
AsObjectIntegerParameter].
if not (tempClientDataSet. IsNull)
FieldDefs.Items and
[indexCustomFieldNamePassed (tempClientDataSet.Fields
AsObjectStringParameter] [indexCustomFieldNamePassed
.DataType in [ftString]) then AsObjectIntegerParameter].Text
begin <> EmptyStr)
callMessageBox(PChar(‘Erro and
ao Abrir DataSet.’#13#10 + (tempClientDataSet.Fields
‘Atributo ‘ + [indexCustomFieldNamePassed
fCustomFieldNamePassed AsObjectStringParameter].Text
AsObjectStringParameter + ‘ não <> EmptyStr)
é do Tipo String.’), ‘Erro’, then
MB_OK + MB_ICONERROR); fComboCustomText.
FCustomDataSet := nil; AddObject (tempClientDataSet.
Exit; Fields[indexCustomField
end; NamePassed
AsObjectStringParameter].
if not (tempClientDataSet. AsString,
FieldDefs. Items[index
CustomFieldNamePassed TObject(tempClientDataSet.Fields
AsObjectIntegerParameter]. [indexCustomFieldNamePassed
DataType in [ftInteger]) then AsObjectIntegerParameter].
begin AsInteger));
callMessageBox(PChar(‘Erro tempClientDataSet.Next;
ao Abrir DataSet.’#13#10 + end;
‘Atributo ‘ + finally
fCustomFieldNamePassed tempClientDataSet.
AsObjectIntegerParameter + EnableControls;
‘ não é do Tipo Integer.’), end;
‘Erro’, MB_OK + MB_ICONERROR); end;
FCustomDataSet := nil;
Exit;
end; Resumindo, a procedure LoadDataSet executa alimenta a propriedade
CustomDataSet mencionada anteriormente e também carrega o objeto TStrings
if not fComboCustomText armazenando um campo do tipo string e do tipo integer,
Assigned(fComboCustomText) ambos informados pelo usuário nas propriedades CustomFieldNamePassedA-
then fComboCustomText := sObjectStringParameter e CustomFieldNamePassedAsObjectIntegerParameter,
TStringList.Create respectivamente. Abaixo é mostrado o momento em que a fComboCustomText
else fComboCustomText.Clear; recebe os dados do DataSet especificado.

// if (FCustomDataSet. Código 08
ClassType = TClientDataSet)
then FCustomDataSet.First; fComboCustomText.
AddObject(tempClientDataSet.Fields[
tempClientDataSet.First; indexCustomFieldNamePassed
AsObjectStringParameter].
try AsString,

18
fevereiro
2013
anteriormente) que são coisas que não são vistas em nenhum outro lugar,
TObject(tempClientDataSet.Fields[ encorajando-me a desenvolver por estas mesmas razões – as necessidades
indexCustomFieldNamePassed de projeto.
AsObjectIntegerParameter].
AsInteger)); 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.

Conclusão Aguardo sugestões, críticas e melhorias, através do código-fonte disponí-


vel; fiquem a vontade nesse sentido. Componente freeware.
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-
Sobre o autor
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 Hamden Vogel
filtro de inteiros e floats, através das propriedades MaskInteger e MaskFloat, Analista de Sistemas pós-graduado em Engenharia de Software pela
respectivamente. 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
Outro ponto que gostei muito em desenvolver nele foi o modo como os especializados em Delphi. Também desenvolve em outras linguagens como
C/C++, ASP, PHP e .NET.
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 thiago@theclub.com.br
cabe as implementações geradas dinamicamente pelos XML’s (conforme dito

19
fevereiro
2013
Asp.Net
Relatório Mestre-Detalhe com ReportViewer
E
ste é 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”.
Figura 01: Modelo Entidade Relacionamento (ER) das tabelas envolvidas.

Tabelas Utilizadas
serão coletados para podermos trabalhar com eles. Clique com o botão direi-
Para exemplificar melhor, nos próximos passos montarei um Modelo (En- to sobre a solução e escolha “Add New Item...”. Localize o item “Dataset” e
tidade Relacinamento –ER) utilizando a ferramenta SQL Server Management defina um nome para este objeto, por exemplo: “dsMestreDetalhe”. Notem
Studio do próprio SQL Server 2008. 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
Usamos como base uma Tabela de Pedido (Orders) se relacionando com de arquivos, como por exemplo do tipo classe (.cs). Dê um duplo clique para
outra de itens (OrderDetail), fechando um relacionamento Mestre-Detalhe. podermos definir a fonte de dados.
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- Importante salientar que na região esquerda da tela aparecerá uma
vendo todas estas tabelas citadas. ToolBox.

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 Figura 02: ToolBox.

20 fevereiro
2013
Para trabalhar com relatórios existem diversos tipos de componentes, eu Código 01
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- SELECT
mos os passos adiante. Na tela “Choose your data Connection” é o local onde Customers.CompanyName, Orders.
escolhemos a conexão, Ver Imagem 03. OrderID, Orders.RequiredDate,
Products.ProductName,
OrderDetails.ProductID,
OrderDetails.UnitPrice,
OrderDetails.Quantity
FROM Orders
INNER JOIN Customers ON
Customers.CustomerID = Orders.
CustomerID
INNER JOIN OrderDetails ON
OrderDetails.OrderID = Orders.
OrderID
INNER JOIN Products ON Products.
ProductID = OrderDetails.
ProductID
WHERE (Orders.OrderID BETWEEN @
COD_INI AND @COD_FIM)

Figura 03: Tipo de Conexão.


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

Já na próxima etapa denominada “Choose a Command Type”, é o local


Campos Significado
onde definimos o modo como o tableAdapter irá acessar a Base de Dados.
Possuímos três opções, sendo: CompanyName Nome do Cliente
OrderID Número do Pedido Venda
- Uso de Instruções SQL; RequiredDate Data Requisição
- Criar Stored Procedures;
- Usar Stored Procedures. ProductName Nome do Produto
ProductID Código do Produto
UnitPrice Preço Unitário
Escolha a primeira opção e clique em “Next”. Ver Imagem 04.
Quantity Quantidade

Podemos também montar instruções SQL com o auxílio do “Query Builder”,


sem nenhuma codificação. Ver Image 05.

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)
Figura 05: Instrução SQL.
utilizando a cláusula “Between” (intervalo de valores) .

21
fevereiro
2013
O “TableAdapter” deverá ficar idêntico ao da Figura 06. 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 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 Figura 08: Adicionando o Agrupamento.
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 Na Janela “Tablix group” escolha “Group by: [OrderID]” para agrupar
“Parameters” clique como o botão direito e escolha “Add Parameter...”. Na por “orderID” e cheque os valores “Add group header” e “Add group footer”,
aba “General” defina em “Name” como “ParamTitulo” e em “Data type” como significando respectivamente para adicionar cabeçalho e rodapé para o grupo.
“text”. O restante deixaremos como padrão, clique em OK para concluir este Ver Imagem 09.
processo. Os mesmos passos descritos deverão ser realizados para o parâmetro
“SubTítulo”. Ver Imagem 07.

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.


Figura 07: Criando Parâmetros.
- Formatação de Valores
No Cabeçalho do relatório adicione dois componentes “textBox” contendo
os seguintes valores: Em se tratando de relatórios, sempre utilizamos a formatação de campos a

22
fevereiro
2013
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”:


Figura 10: Relatório em desenvolvimento. =format(SUM(Fields!UnitPrice.
Value*Fields!Quantity.Value),
“###,###,##0.00”)

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: 3-) Definindo o Preview do relatório

Máscara Numérica: Adicionaremos alguns componentes na tela para efetuar a parametrização


=format(Fields!UnitPrice. e a execução do relatório. Primeiramente adicione uma tabela para organização
Value,”###,###,##0.00”). e em seu interior 2 Labels, 2 TextBoxes e um Button, para podermos filtrar por
tostring() “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
Máscara Data: (componente necessário para execução de scripts) e o ObjectDataSource para
=format(Fields!RequiredDate. inserção de alguns parâmetros. Ver Imagem 12.
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 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


Figura 11: Formatando campos. 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
Faremos este procedimento para os campos “RequiredDate” (Data), Data Source”. A Figura 13 é onde definimos o objeto “DsMestreDetalhe”. Clique
“UnitPrice” (Valor Unitário), “Quantity” (Quantidade), “Total” e “Total Geral”. em “Next” para configuração da próxima etapa.

- Campos Calculados Veja a Figura 13.

Os campos calculados são utilizados para obter valores sem a utilização de Esta tela é muito importante, pois é nela onde são situados os métodos
campos específicos em Banco de Dados. Podemos realizar operações de sub- de Seleção, Atualização, Inserção e Exclusão. Em se tratando de relatórios
tração, adição, multiplicação, divisão e funções específicas do relatório. Segue

23
fevereiro
2013
Figura 13: Definindo o objeto. Figura 15: Definindo os Parâmetros.

apenas utilizaremos o primeiro método, o de Seleção de Dados. Neste caso WebForms;


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 Esta biblioteca é responsável por alguns métodos utilizados ao decorrer
pela parametrização dos dados ali obtidos. Ver Imagem 14. do artigo.

Código 02

protected void Button1_


Click(object sender, EventArgs
e)
{
ObjectDataSource1.
SelectParameters[0].DefaultValue
= TextBox2.Text;
ObjectDataSource1.
SelectParameters[1].DefaultValue
= TextBox3.Text;
ReportParameter[]
parametros =
{
new ReportParameter(“ParamTitul
Figura 14: Definindo o método de Seleção de Dados. o”,”The Club - O Maior Clube de
Programadores do Brasil”),
Já na próxima etapa, na região esquerda, estão situados os “Parameters” new ReportParameter(“ParamSub
(parâmetros Nome e Valor) junto com algumas configurações como: “Source” titulo”,”Relatório de Venda -
(fonte, a origem do parâmetro), “QueryStringField” (nome do parâmetro) e Mestre/Detalhe”)
“DefaultValue” (valor padrão). Clique em “Finish” para terminar este processo. };
Ver Imagem 15 para maiores detalhes das configurações.
ReportViewer1.LocalReport.
Veja a Figura 15. SetParameters(parametros);
ReportViewer1.LocalReport.
Refresh();
Codificando o botão “Imprimir” }

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


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

24
fevereiro
2013
que mais adiante serão recuperadas dentro do próprio arquivo “.rdlc”. Temos Venda, Orçamentos de Produtos, entre outros tipos. A minha intenção neste ar-
um par chave/valor sendo o nome do Parâmetro seguido do valor. Para finalizar tigo foi de proporcionar um exemplo prático para a utilização do ReportViewer
passamos os dados utilizando a função “SetParameters”. em uma página Asp.Net. Podemos também aplicar estes conhecimentos, sem
muitos esforços, na plataforma Desktop por exemplo. O ReportViewer, na
Para atualizar os dados na tela usamos o “refresh()”. Para testar o exemplo minha opinião, é uma das melhores ferramentas em se tratando de Desenvol-
pressione o botão F5 e teremos um resultado parecido com a Imagem 16. 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!

Sobre o autor
Marcos César Silva
Consultor de Sistemas na consultoria de sistemas DataSmart e
Consultor Técnico do The Club, Bacharel em Ciência da Computação,
Figura 16: Relatório. MBA em Gestão Empresarial, Certificações MCAD (Microsoft Certified
Application Developer) e MCSD.NET (Microsoft Certified Solution
Developer .NET)
Conclusão

O desenvolvimento de relatórios do Tipo “Mestre/Detalhe” é muito habi- marcos@theclub.com.br


tual para nós programadores, usando para diversas tarefas como: Pedidos de

25
fevereiro
2013
Delphi XE2
Trabalhando com estilos no FireMonkey – Parte II

O
lá 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 Figura 1 – Carregando o arquivo de estilo para o componente TStyleBook
estilo definido como padrão, e todos os demais formulários deverão herdar
este formulário. 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
Exemplo prático Studio\9.0\Styles).

Bom, vamos ao que interessa, crie uma pasta onde será salvo o nosso Após clicar no botão Load, localize a pasta de estilos do Delphi XE2 e adi-
projeto de exemplo, nomeie-a como achar melhor. Abra o Delphi XE2 e crie cione o primeiro arquivo, Air.style, selecionando-o e clicando em abrir. Depois
um novo projeto FireMonkey através do menu (File/ New/ FireMonkey HD de carregar as propriedades do estilo clique em Apply and Close. De volta ao
Application - Delphi). Pressione as teclas Ctrl + Shift + S para salvar tudo, localize formulário vamos nomear o componente StyleBook1 para sbAir.
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 Desta forma já configuramos um estilo para nossa aplicação, porém,
do primeiro formulário para frmPrincipal. 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
Vamos agora adicionar os estilos que disponibilizaremos junto com a nossa componentes TStyleBook ao frmPrincipal, e seguindo as informações da tabela
aplicação. Adicione ao formulário o componente TStyleBook (Standard). Dê um 1 configure os demais componentes TStyleBook repetindo o processo utilizado
duplo clique no componente para abrir a tela onde iremos carregar arquivo para configurar o estilo no componente sbAir.
de estilo (ver figura 1).
Nota: na tabela 1 tem um estilo chamado MeuEstilo.style, este
Veja a Figura 1. estilo foi criado num exemplo anterior criado para o artigo “Delphi XE2
– Trabalhando com estilos no FireMonkey” da revista The Club do mês
O Delphi por padrão distribui alguns arquivos de estilos prontos, para de janeiro de 2013. Não há problemas se não tiver este arquivo, pode
localizar estes estilos abra a pasta Meus Documentos, depois acesse a pasta adicionar os estilos que achar necessário.

26 fevereiro
2013
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 Figura 3 – inserindo campo virtual no cdsEstilos
9 sbMeuEstilo MeuEstilo.style MeuEstilo
10 sbRubyGraphite RubyGraphite.style RubyGraphite
11 sbWindows7 Windows7.Style Windows7 os dados do cdsEstilos.
Tabela 1 – Nomenclatura de componentes e arquivos de estilos utilizados no Voltando ao projeto do Delphi, clique com o botão direito do mouse no
exemplo 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
A coluna “Ordem” da tabela 1, representará o número pelo qual identi- 4), neste momento abrirá uma janela pedindo para informar o local onde
ficaremos via código estes componentes posteriormente. Depois de adicio- salvará o arquivo XML, localize a pasta do projeto e vá até a pasta Debug, dê
nado todos os estilos, configurarmos a propriedade Name dos componentes o nome de Estilos.xml.
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 Figura 4 - Salvando o arquivo XML para o cdsEstilos
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 Selecione novamente o cdsEstilos e agora vá até a janela Object Inspector
(ver figura 3). Informe o nome COD_ESTILO, o tipo Integer (inteiro), em Field e localize a propriedade FileName, insira o nome do arquivo XML, Estilos.xml.
Type marque a opção Data, pós isso é só confirmar pressionando o botão Ok. Desta forma já configuramos o ClientDataSet para acessar os dados, porém,
agora devemos alimentar estes dados, para isso iremos criar um procedimento.
Veja a Figura 3. 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
Depois de criado o campo COD_ESTILO, iremos configurar o cdsEstilos para passaremos o parâmetro Estilo do tipo inteiro, é nesta hora que temos que
acessar / gravar dados em um arquivo XML. Caso ainda não tenha executado ter em mente o número de cada estilo definido na coluna Ordem descritos na
o seu projeto, execute-o agora, para que seja criada uma estrutura de pastas da tabela 1. Outro parâmetro deste procedimento é a constante Create com
dentro da pasta do seu projeto. Como o projeto está gerando executável para o valor padrão False, servirá para identificar se o procedimento foi chamado
Win32, criou a seguinte estrutura dentro da pasta do projeto: (Win32\Debug), pelo evento OnCreate do frmPrincipal.
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 Na sessão Public da unit escreva a declaração do procedimento, e depois

27
fevereiro
2013
escreva os códigos no corpo do procedimento como mostra a listagem 1. 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
Listagem 1 - Procedimento SetEstilo e o valor True para o parâmetro Create da procedure. Segue o exemplo destes
procedimentos na listagem 2.
//Declaração da procedure
public Listagem 2 - Manipulando o cdsEstilos e chamando o procedimento SetEstilo
{ Public declarations }
procedure SetEstilo(Estilo: // Evento OnCreate do
Integer; const Create: Boolean frmPrincipal
= False); procedure TfrmPrincipal.
FormCreate(Sender: TObject);
//Corpo da procedure begin
procedure TfrmPrincipal. cdsEstilos.Open;
SetEstilo(Estilo: Integer; SetEstilo(cdsEstilosCOD_
const Create: Boolean = False); ESTILO.AsInteger);
begin end;
{
SE NÃO FOR CHAMADO PELO //Evento OnClose do frmPrincipal
EVENTO ONCREATE ALTERA O VALOR procedure TfrmPrincipal.
NO CAMPO NO DATASET E SALVA A FormClose(Sender: TObject; var
ALTERAÇÃO Action: TCloseAction);
} begin
if not Create then cdsEstilos.Close;
begin end;
cdsEstilos.Edit;
cdsEstilosCOD_ESTILO.
AsInteger := Estilo; Adicione dois botões no frmPrincipal, altere suas propriedades Text para
cdsEstilos.Post; “Cadastro” e “Configurar Estilo”. Mais tarde utilizaremos estes botões para
end; chamar as demais telas da nossa aplicação.

case Estilo of
0: StyleBook := sbAir; Criando o formulário Base de Estilos
1: StyleBook := sbAmakrits;
2: StyleBook := Crie um novo formulário através do menu (File/ New/ FireMonkey HD
sbAquaGraphite; Form - Delphi). Salve a unit com o nome unFrmBaseEstilo e nomeie o formulário
3: StyleBook := sbBlend; com o nome frmBaseEstilo. Pressione as teclas Alt + F11 para chamar a janela
4: StyleBook := sbdark; Use Unit, e adicione a unPrincipal no formulário clicando em Ok.
5: StyleBook := No evento onCreate do formulário frmBaseEstilo informe que a proprie-
sbGoldenGraphite; dade StyleBook irá receber o StyleBook presente no frmPrincipal. Veja como
6: StyleBook := sbiOS; isso é implementado na listagem 3.
7: StyleBook := sbMacBlue;
8: StyleBook := Listagem 3 - Adicionando o estilo do formulário principal ao formulário base
sbMacGraphite;
9: StyleBook := procedure TfrmBaseEstilo.
sbMeuEstilo; FormCreate(Sender: TObject);
10: StyleBook := begin
sbRubyGraphite; StyleBook := frmPrincipal.
11: StyleBook := StyleBook;
sbWindows7; end;
end;
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
Podemos perceber que através do valor inteiro passado como parâmetro momento em que forem criados em memória.
no cabeçalho da procedure verifica qual será o estilo a ser adicionado na
propriedade StyleBook do frmPrincipal.
Criando o formulário base para cadastro
Não se esqueça de abrir o cdsEstilos no evento onCreate do frmPrincipal

28 fevereiro
2013
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 begin
em Ok. Salve a unit com o nome unFormBase e dê o nome de frmFormBase. frmClientes := TfrmClientes.
Adicione alguns componentes básicos de cadastro como mostra a figura 6. Create(self);
Lembrando que não é necessário fazer nenhuma programação nestes compo- frmClientes.ShowModal;
nentes, servirão apenas para exemplificar a alteração dos estilos. 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
Figura 5 – Criando uma herança do frmBaseEstilo + 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.

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

procedure TfrmConfigEstilo.
cbeEstilosChange(Sender:
TObject);
begin
inherited;
frmPrincipal.
SetEstilo(cbeEstilos.
ItemIndex);
StyleBook := frmPrincipal.
Figura 6 – Exemplo do formulário base de cadastro StyleBook;
end;
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 Após alterar o estilo do formulário principal, atribuímos esta alteração al
o estilo possa ser aplicado. formulário de configuração para também aderir ao novo estilo.

Acesse novamente o menu (File/ New/ Other) e agora selecione o Para executarmos o teste ainda falta incluir a unit unConfigEstilo na
frmFormBase e clique em Ok. Iremos criar um formulário para cadastro de unPrincipal, para podermos fazer a chamada deste formulário em tempo de
clientes. Salve a unit com o nome unClientes e nomeie o formulário com execução através do botão “Configurar Estilo”. É importante lembrar que apenas
o nome frmClientes. Adicione alguns componentes de edição como Edits e o frmPrincipal deve ser criado automaticamente pelo Delphi, os demais serão
Grid. Não é necessário codificar nenhum componente. Neste momento já criados em tempo de execução. Para configurar acesse o menu (Project/ Op-
podemos acessar o frmPrincipal, adicionarmos a unClientes Através das teclas tions) e clique no nó Forms no painel da esquerda, na lista Auto Create Forms
Alt + F11. Com a unClientes adicionada na Uses da unPrincipal podemos agora deixe apenas o frmPrincipal, os demais mova para a lista da direita, Available
configurar o evento onClick do Formulário que irá chamar a tela de cadastro. Forms. Clique em Ok para confirmar a configuração. Agora é só executar o
Veja o exemplo na listagem 4. 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.
Listagem 4 - Configurando a exibição da tela de cadastro de clientes
Veja a Figura 7.
procedure TfrmPrincipal.
Button1Click(Sender: TObject);

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

Conclusão Sobre o autor


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 Lucas Vieira de Oliveira
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
Consultor Técnico The Club.
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.

suporte@theclub.com.br

30
fevereiro
2013
05
fevereiro
2013
fevereiro
2013

Você também pode gostar