Clube Delphi 164
Clube Delphi 164
Clube Delphi 164
Paulo Quicoli (pauloquicoli@gmail.com) A DevMedia conta com um departamento exclusivo para o atendimento Paulo Quicoli - Editor da Revista
ao leitor. Se você tiver algum problema no recebimento do seu exemplar pauloquicoli@gmail.com
Equipe Editorial ou precisar de algum esclarecimento sobre assinaturas, exemplares ante-
Guinther Pauli (guintherpauli@gmail.com) riores, endereço de bancas de jornal, entre outros, entre em contato com:
Sumário
Conteúdo sobre Novidade
ta
edição
Novidades na VCL
Como não poderia deixar de ser, a VCL (Visual Compo-
nent Library), tida como a parte clássica do Delphi, conta
com algumas novidades exclusivas nesta nova versão
do IDE. Tendo em vista sua estabilidade, aliada a enor-
me expansão do FireMonkey no cenário mais recente,
poucas são suas novas pontualidades nesta versão, mas
podemos destacar a facilidade com conexões de Internet
ou Bluetooth com componentes de AppTethering.
Figura 1. IDE do Delphi XE8
Novos VCL Styles
Surgido como uma das grandes novidades do Delphi O ganho aqui fica por conta da notória evolução de um recurso
XE2, o VCL Styles pode ser brevemente definido como interno que, anteriormente ao XE2, só era conseguido por meio
sendo um recurso para incrementar a aparência de uma do uso de componentes de terceiros, o que acabava por gerar
aplicação VCL. Para isso, são utilizados estilos, cada qual uma dependência natural para com estes, além de eventualmente
definindo um conjunto elaborado de detalhes gráficos dispender custos para aquisição e uso.
personalizados que incidirão sobre o look and feel da
aplicação. Logo, sua atuação ocorre de forma semelhante AppAnalytics
ao tradicional recurso de temas do Windows. O AppAnalytics pode ser considerado um novo recurso disponí-
Visando manter o valor das aplicações construídas e vel para projetos VCL e FireMonkey, que prevê a coleta de dados
constante melhora para novos desenvolvimentos VCL, (de forma anônima) resultantes da interação do usuário com a
o Delphi XE8 apresenta agora mais três novos estilos aplicação, possibilitando então uma auditoria de processos por
para VCL Styles, intitulados Glow, Sky e Tablet Light. meio da interpretação e análise dessas informações.
4 ClubeDelphi • Edição 164 Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia
4
No Delphi, o AppAnalytics se reflete em um componente homô-
nimo (Figura 2), oriundo da classe TAppAnalytics.
Na prática, os dados capturados pelo componente são então
enviados a um aplicativo Web AppAnalytics para imediato
processamento e eventual análise. Por indiretamente lidar com
informações de uso e envolvimento do usuário, a própria Embar-
cadero faz questão de ressaltar em caixa alta a forma anônima que
o componente atua, se restringindo a transmitir as informações
somente ao contexto da aplicação em questão.
TMapView
Agora no XE8 os aplicativos para Android e iOS com FireMonkey
oferecem suporte ao componente TMapView, cuja função es-
sencial é prover interatividade com os mapas dessas aplicações
por meio de ações como: controle por gestos (gestures), adição
de marcadores (markers), controle de coordenadas e orientação.
Figura 2. Componente TAppAnalytics Adicionalmente, ainda por meio do uso de um objeto MapView
é possível determinar o estereótipo do mapa por entre quatro
Novidades para FireMonkey segmentações: normal, satélite, híbrido e terreno.
O Delphi XE8 traz consigo também uma série de melhorias Aqui um ponto de observação: em aplicativos para Android, o
para o FireMonkey. Grande parte delas trata de pequenos uso do TMapView requer a obtenção de um certificado adicional
aspectos sutis, mas que não deixam de impactar de forma da própria Google, denominado Google Maps API Key para a
direta em diversos pontos relacionados a um projeto Mobile efetiva utilização das funcionalidades envolvidas. Caso isso não
Application. seja feito, tal pontualidade acaba por se tornar um fator determi-
nante, uma vez que em tempo de execução uma exceção é gerada,
TWebBrowser inviabilizando o uso ideal da aplicação. Esse certificado é gerado
O TWebBrower é um componente visual do FireMonkey que, usando o login no Google.
uma vez adicionada à aplicação, toma a forma de um Web Browser
interno permitindo que este carregue e mostre conteúdos Web Photo Library
em sua área. Inicialmente introduzido na ferramenta como sendo De olho na melhor usabilidade e experiência do usuário em
de uso exclusivo às construções Mobile (Android e iOS), agora suas aplicações móveis, o FireMonkey conta agora com um
no XE8 seu uso é expandido às construções Desktop (Windows recurso que possibilita salvar fotos capturadas pela câmera do
e Mac OS X). dispositivo diretamente na biblioteca de fotos do sistema do
próprio aparelho.
Apresentação nativa de diversos controles no iOS Na prática, a utilização desta nova opção se dá pelo envolvimento
Nesta nova versão do IDE, especificamente para projetos de e manuseio de alguns elementos distintos. O primeiro a ser citado
Target Platform iOS, os tradicionais controles TCalendar, TEdit, é o IFMXPhotoLibrary, que caracteriza uma interface para salvar
TListView, TMemo, TMultiView e TSwitch ganham agora uma as imagens no álbum de fotos do sistema. Para isso, sua estrutura
forma de apresentação nativa (native), em complemento a sua já conta com um método exclusivo denominado intuitivamente de
existente apresentação estilizada (styled). O impacto direto pela AddImageToSavedPhotosAlbums, que acaba por dispensar co-
escolha de uma ou outra forma de apresentação se dá então sobre mentários. Relacionado a isso surge então outros dois elementos,
o aspecto visual do controle, fazendo com que o mesmo assuma TParamsPhotoQuery e IFMXCameraService, ambos relacionados
uma representação nativa da própria plataforma de destino, à obtenção de fotos a partir da câmera. Por fim, a propriedade
quando marcado como native, ou siga uma estilização do próprio NeedSaveToAlbum é que determina se a aplicação poderá ou não
FireMonkey, no caso de styled. Na prática, essa escolha se dá pela salvar as fotos obtidas pela câmera na biblioteca de fotos.
configuração da propriedade ControlType dos controles, conforme
mostrado na Figura 3. Standard Actions para TMediaPlayer
Sem deixar de citar, apesar da novidade, styled ainda se mantém Na FMX, TMediaPlayer é o componente responsável pela mani-
como o valor padrão para a propriedade. pulação de arquivos multimídia, garantindo a devida interação
Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia Edição 164 • ClubeDelphi5
5
Conheça as novidades no Delphi XE 8
6 ClubeDelphi • Edição 164 Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia
6
Novidades na RTL TBluetooth
Como não poderia deixar de ser, o core do Delphi também ganha Ainda relacionado a Bluetooth, o XE8 conta com a adição de
melhorias neste novo release da ferramenta. Sendo assim, agora no um novo componente em sua Tool Palette (Figura 6) denominado
XE8 a RTL (Run-Time Library) ganha o acréscimo de novas units, TBluetooth. Sua função principal é agir como um wrapper das
o que resulta em novos recursos e componentes abrangentes aos classes do Bluetooth “clássico” do framework do Delphi, tais como
dois contextos de desenvolvimento do IDE: VCL e FMX. TBluetoothManager e TBluetoothDevice, encapsulando assim os
principais recursos das mesmas. Com essa simples mudança a
TNetHTTPClient e TNetHTTPRequest Embarcadero novamente foca no aspecto RAD que consagrou a
Conforme mostra a Figura 5, TNetHTTPClient e TNetHTTPRe- ferramenta, tornando possível uma abordagem totalmente base-
quest são dois novos componentes que surgem na RunTime Library ada em componentes por parte do desenvolvedor.
do Delphi. De forma básica a usabilidade de ambos está totalmente
inter-relacionada, uma vez que lidam diretamente com questões
cliente-servidor do contexto HTTP. Mais especificamente, por meio
de seus recursos (métodos, propriedades e eventos) torna-se facili-
tada as eventuais tratativas de pedidos de request cliente, tal qual
a ação de resposta (response) oriunda do servidor HTTP.
Hash API
A RTL traz ainda uma nova API Hash, centrada na também
nova unit System.Hash, que inclui toda uma estrutura de clas-
Figura 5. Componentes TNetHTTPClient e TNetHTTPRequest ses e métodos que habilitam o uso direto de três funções Hash
na construção de aplicações Delphi: MD5 (THashMD5), SHA-1
Suporte a Beacon (THashSHA1) e Bob Jenkins (THashBobJenkins). A fim de facilitar
No cenário tecnológico mais recente, a palavra beacon é um sua utilização, todas acabam por apresentar diversos métodos
termo empregado para designar um dispositivo Bluetooth Low em comum, tal qual GetHashString, que retorna o valor Hash da
Energy. Esta tecnologia nada mais é do que um tipo de Bluetooth string fornecida, e Update, que atualiza o valor Hash.
específico, que pode incluir informações em seus dados de pu-
blicidade (advertising) as quais possibilitarão sua identificação e FixedInt e FixedUInt
cálculo de sua distância por qualquer outro dispositivo, dentro de A partir desta versão o Delphi passa a contar com dois novos
uma margem de área, sem que ele esteja efetivamente conectado tipos Integer, denominados FixedInt e FixedUInt. Por definição
ou emparelhado ao dispositivo Beacon (Beacon device). ambos são do tipo inteiro com um tamanho fixado em 32 bits
Visando atender a mais este cenário de possibilidades, o Delphi invariável, o que garante uma melhor interoperabilidade no seu
XE8 inclui na RTL uma API Beacon exclusiva, restrita a projetos uso tanto em projetos para 32-bit quanto 64-bit.
para Mac OS X, iOS e Android. Por questões de acessibilidade, a Como diferencial, FixedInt se apresenta como um tipo assinado
plataforma Windows não é suportada. Além disso, até o presente (signed), abrangendo valores que vão de -2147483648 a 2147483647.
momento, dois são os tipos Beacon suportados: Em seu uso prático, para projetos de Target Platform Windows
• iBeacon: formato proprietário definido pela Apple, tido como (32 e 64-bit), FixedInt é equivalente ao tipo LongInt, enquanto que
o modo “padrão”; para as outras plataformas de destino (que inclui Mac OS X, iOS
• AltBeacon: formato aberto. Também conhecido como Alter- e Android) sua equivalência se faz ao tipo Integer tradicional. Já
native Beacon e, portanto, tido como modo “alternativo” pela FixedUInt é considerado um tipo não-assinado (unsigned), cujo
ferramenta. intervalo compreende os valores de 0 a 4294967295. Em comple-
O ponto de vista prático deste suporte a Beacon no Delphi acaba mento, o tipo LongWord é seu equivalente para projetos para a
por envolver diversas nuances, o que dá margens à elaboração de plataforma Windows, na mesma medida que o tipo Cardinal é
um artigo exclusivo deste contexto. Todavia, apenas para citar, o iní- seu equivalente para as plataformas Mac OS X, iOS e Android.
cio da efetiva utilização do recurso ocorre por meio da obtenção de
uma instância de TBeaconManager, tal como mostrado a seguir: LongInt e LongWord
Ainda com relação a tipos inteiro, o XE8 apresenta um dife-
BeaconManager := TBeaconManager.GetBeaconManager(TBeaconScanMode.Standard); rencial para os tipos LongInt e LongWord, especificamente ao
desenvolvimento para a plataforma iOS 64-bit. Tal mudança se DCCIOSARM64 é o nome do compilador nativo que, a partir
refere à duplicação do tamanho de bytes suportado pelo tipo, de deste release, fica responsável pela produção de aplicações iOS
4 para 8 bytes, exclusivo em sua atuação nesta plataforma. Para tidas como universais – Universal iOS Apps – que refletem então
todas as outras, incluindo iOS 32-bit e Windows 64-bit, o tamanho aplicativos Delphi para a plataforma, prontas a rodar tanto em
de 4 bytes é mantido. dispositivos 32 quanto 64-bit. No aspecto prático, este recurso é
habilitado por meio da opção “Generate iOS universal binary file
IDE e Compilador (armv7 + arm64)”, disponível nas opções de projeto cuja Target Pla-
As novidades nessa versão do Delphi incluem também novos as- tform esteja marcada exclusivamente como iOS Device 64-bit.
pectos singulares que abrangem o próprio IDE, que é o ponto base
de interação com o usuário desenvolvedor. Isso inclui também Multi-Device Preview
toda sua parte de compilador que desde as versões mais recentes Multi-Device Preview é o nome de um novo recurso que dá ao de-
do produto, apresenta uma gama de elemento que transcende a senvolvedor uma prévia do visual de uma mesma aplicação, ainda
engine tradicional para Win32. em tempo de Design, em diversos aparelhos distintos, facilitando
eventuais ajustes necessários. Em termos de localização, o recurso
Nova Target Platform: iOS Device – 64-bit fica disponível no menu View (View > Multi-Device Preview) da
Em seu cenário recente o Delphi acabou por incorporar a seu con- ferramenta e é habilitado para projetos do tipo Multi-Device. Para
texto uma gama de plataformas distintas, em complemento ao seu exemplificar melhor o que foi dito, a Figura 8 mostra então o Multi-
tradicional suporte ao Win32. Assim, a partir de uma mesma base Device Preview em ação, a partir de um projeto FireMonkey.
de código, o desenvolvedor consegue agora produzir aplicativos
tanto para Desktop quanto Mobile, em quatro opções diferentes
de sistemas operacionais: Windows, Mac OS X, iOS e Android.
Como já é sabido, na prática, o direcionamento de um projeto a
uma plataforma alvo se dá pela configuração de sua Target Plat-
form, disponível no Project Manager do IDE. Como novidade, a
versão XE8 conta agora com o acréscimo de mais uma nova opção
de plataforma de destino, denominada iOS Device – 64-bit, tal
como mostra a (Figura 7). Sua nomeação autoexplicativa acaba
por determinar seu fundamento, que é a produção de aplicativos
para dispositivos iOS 64-bit.
8 ClubeDelphi • Edição 164 Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia
8
Listagem 1. Predefinições para iPhone 4
...
<MobileDevice>
<Displayname>iPhone 4”</Displayname>
<Name>iPhone4in</Name>
<DevicePlatform Default = “True”>2</DevicePlatform>
<FormFactor>2</FormFactor>
<Portrait Enabled=”True” Width=”320” Height=”568” Top=”127” Left=”39”
StatusbarHeight=”20” Artwork=”$(BDS)\ObjRepos\en\Devices\images\
iphone5.cpng” StatusBarPos=”0”/>
<UpsideDown Enabled=”True” Width=”320” Height=”568” Top=”124” Left=”34”
StatusbarHeight=”20” Artwork=”$(BDS)\ObjRepos\en\Devices\images\
iphone5180.cpng” StatusBarPos=”0”/>
<LandscapeLeft Enabled=”True” Width=”568” Height=”320” Top=”39” Left=”124”
StatusbarHeight=”20” Artwork=”$(BDS)\ObjRepos\en\Devices\images\
iphone590.cpng” StatusBarPos=”0”/>
<LandscapeRight Enabled=”True” Width=”568” Height=”320” Top=”34”
Left=”127” StatusbarHeight=”20” Artwork=”$(BDS)\ObjRepos\en\Devices\
images\iphone5270.cpng” StatusBarPos=”0”/>
</MobileDevice>
...
Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia Edição 164 • ClubeDelphi
99
Conheça as novidades no Delphi XE 8
arquivo .gdb foi adicionado ao projeto, fazendo com que a janela Add • Suporte a envios de atualizações a repositórios remotos – Push;
Featured Files fosse acionada, já contemplando os arquivos comple- Além disso, agora temos a atualização da cópia de trabalho
mentares necessários para o ideal deploy futuro da aplicação. (working copy) com as últimas alterações provindas do repositório
remoto – Pull.
BOX 1. Git
Suporte a Mercurial
Ainda relacionado a gerenciamento de código-fonte, o IDE do
Delphi XE8 traz ainda como novidade o suporte a mais um sistema
de controle de versão, agora o Mercurial, que se junta aos coirmãos
anteriores, Subversion e Git. Em uma breve definição, salvo suas pecu-
Figura 10. Janela Add Featured Files liaridades, o Mercurial tem sua essência aproximada ao Git, uma vez
que se caracteriza como sendo um distributed version control system
Desabilitando Built-in RAD Studio Java Libraries ou, em português, sistema de controle de versão distribuído. Neste
Outra novidade trazida especificamente para projetos Android, início de parceria (Delphi-Mercurial), três são as ações principais
diz respeito ao descarte de bibliotecas Java built-in em um projeto. quanto ao uso da ferramenta sob o contexto do XE8: Clone, Commit
Neste contexto, o termo built-in é empregado para designar as e Show Log. A primeira efetua uma cópia íntegra de um repositório
Java Libraries “embutidas” nativamente pelo Delphi para seus remoto para o início dos trabalhos locais. Já a segunda, Commit, envia
projetos deste segmento. Por questões óbvias, de acordo com a as alterações para o repositório local, enquanto a última simplesmente
essência do projeto, uma ou outra biblioteca acaba por se tornar mostra as informações de log do projeto.
desnecessária e sua remoção é quase que obrigatória para tor- A partir disso, sem sombra de dúvidas, este recurso é digno de
nar a aplicação mais enxuta e otimizada. Todavia, até o XE7 o um artigo completo sobre o mesmo, uma vez que envolve nuances
processo de remoção de uma Built-in Java Library de um projeto que o fomentam como um produto bastante singular.
era algo não tão trivial, e fundamentalmente envolvia a mani-
pulação de um arquivo complementar denominado classes.dex. Settings Migration Tool
A fim de simplificar esta abordagem, o Delphi XE8 possibilita Settings Migration Tool é o nome de uma ferramenta externa que
que qualquer built-in Java library seja desabilitada por meio do acompanha a instalação do Delphi XE8, cuja função é realizar a im-
próprio Project Manager, tal como mostra a Figura 11. portação e exportação de definições de configuração (configuration
settings) entre versões distintas do IDE. De forma fundamental, seu
principal uso se dá pela eventual migração/transporte de definições
de configuração do IDE a diferentes instâncias instaladas em am-
bientes distintos. Ainda dada sua utilização, dois aspectos restritivos
são impostos pela ferramenta. A primeira se reflete em seu suporte,
previsto a todas as versões do produto a partir do Delphi 7, logo,
outros releases clássicos, tal como o Delphi 5, estão fora do contexto
proposto. Outra restrição existente se dá pelo próprio processo de
migração, que ocorre sempre em linha linear crescente, o que esta-
Figura 11. Opção Disable para Built-in Java Libraries belece uma atividade entre versões de mesma numeração ou maior
(Ex: de XE2 para XE8, nunca de XE2 para XE8).
Melhorias para Git Por padrão, o Settings Migration Tool é disponibilizado em:
A integração do IDE com o sistema de gerenciamento de código-
fonte Git (BOX 1) é melhorado nesta versão por meio do acréscimo C:\Program Files (x86)\Embarcadero\Studio\16.0\bin
de duas novas funcionalidades:
• Suporte a autenticação para conexões a repositórios privados A seguir, a Figura 12 mostra sua janela de execução, a qual
remotos – Authenticate; apresenta suas principais operações:
10 ClubeDelphi • Edição 164 Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia
10
• Export settings to a migration file: exporta as definições de Customer Experience Program
configuração para um arquivo XML tradicional, porém com uma Customer Experience Program é a denominação dada a um
extensão própria (.idesettings); programa colaborativo proposto pela Embarcadero, cujo intuito
• Migrate settings to a newer product version: migra as definições é coletar dados anônimos dos usuários da ferramenta durante
de configuração para uma nova instância do IDE instalada na sua interação com o IDE. Já existente há algum tempo em grande
máquina corrente; parte de seus concorrentes, esse tipo de programa visa a tomada
• Restore settings from backup: restaura as configurações a partir de decisões mais assertivas, por parte da empresa fabricante, no
de uma cópia de segurança provida; que diz respeito a eventuais melhorias ou mudanças na experi-
• Import settings from a migration file: importa um arquivo ência do usuário com o ambiente de desenvolvimento. Sob a ótica
.idesettings. da empresa, tal usuário representa desde o profissional da área,
passando por programadores casuais, até simples aventureiros.
Por envolver a transmissão de dados pessoais, a Embarcadero
apenas disponibiliza o acesso ao programa no próprio IDE do
Delphi XE8, cabendo ao usuário seu ingresso voluntário ao Cus-
tomer Experience Program. Logo, tal opção é acessível por meio
menu Tools > Options > Environment Options, conforme mostra
a Figura 13.
Castalia
O IDE do XE8 conta agora com a integração nativa a uma ferra-
menta já bastante conhecida em meio à comunidade de desenvol-
vedores Delphi, o Castalia. De forma breve, este plug-in pode ser
Figura 13. Opção Customer Experience Program
definido como um facilitador de tarefas ao desenvolvedor, rela-
cionadas ao desenvolvimento de software dentro do IDE. A partir
de um ponto de vista mais conceitual, todas essas tarefas podem Novo sistema de Help off-line
então ser subdivididas em quatro segmentos independentes: O arquivo de Ajuda (Help) off-line que acompanha a instalação
• Recursos Visuais: compreende o conjunto de elementos visuais do Delphi XE8 apresenta um novo formato, se comparado com o
de apoio que se incorporam ao editor de código (Code Editor) do formato apresentado até o XE7. Anteriormente o formato utilizado
Delphi; era denominado H2, e sua visualização ficava atrelada a um de-
• Recursos Não-Visuais: compreende o conjunto de atalhos, os quais terminado visualizador. Agora o Microsoft Compiled HTML Help
irão prover facilidades e funcionalidades durante a codificação; (.chm) é o novo formato adotado, cuja popularidade e simplicidade
• Controles de edição de código: faz referência aos controles in- se mostram superiores à opção anteriormente existente.
corporados ao IDE que colaboram na navegação de código;
• Menu: envolve as funcionalidades e estatísticas de código. Novidades para FireDAC
No cenário Delphi recente, o FireDAC se consolidou defini-
Tais quais outros recursos apresentados nesta versão do IDE, tivamente como a principal opção nativa para acesso a dados,
o Castalia mostra-se complexo o suficiente para o natural sur- numa substituição quase que completa do dbExpress. Em vista
gimento de artigos exclusivos sobre o tema, fundamentalmente disso, a cada nova versão da ferramenta a expectativa (e neces-
direcionados ao seu uso prático. sidade) por novidades e melhorias torna-se bastante grande.
Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia Edição 164 • ClubeDelphi 11
11
Conheça as novidades no Delphi XE 8
12 ClubeDelphi • Edição 164 Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia
12
Desenvolvendo um
Sistema Financeiro em
Delphi– Parte 3
Implementação da camada de acesso a dados
O
Delphi, no que diz respeito a linguagem, imple- mento das classes de negócio para tabelas no banco de dados e o
menta todos os requisitos de uma linguagem mapeamento das propriedades para as colunas das tabelas criadas, e
orientada a objetos, pois nos permite fazer vice-versa. Veremos objetos sendo transformados em linhas na tabela
abstração, encapsulamento, herança e polimorfismo. e propriedades transformadas em colunas.
Porém, o que mais vemos nos desenvolvedores Delphi é
o desenvolvimento de aplicações de forma estruturada,
onde são explorados apenas os recursos RAD que o IDE ra bastante semelhante ao mundo real, o que torna-se um desafio
proporciona. a mais quando precisamos fazer a persistência destes nos bancos
Um dos grandes pontos negativos de trabalhar com de dados relacionais.
Delphi é a falta de um framework de mapeamento
BOX 1. Inversão de Controle e Injeção de Dependência
objeto-relacional robusto e que seja apoiado pela Em-
barcadero. Existem iniciativas como o DORM (open- Este princípio é a base para qualquer bom design de software orientado a objetos. O princípio da
source) e o Aurelius (comercial), mas são soluções que Inversão de Dependência nos diz que módulos de alto nível não devem ser dependentes de módulos
ainda não se tornaram populares. Neste artigo veremos de baixo nível, ambos devem depender de abstrações, que por sua vez, não devem depender de
uma solução manual para resolver esse problema de detalhes.
mapeamento objeto-relacional. Existem boas práticas, Inverter a dependência faz com que o cliente não fique frágil a mudanças relacionadas a detalhes
como a Inversão de Controle (BOX 1), que no Delphi de implementação, isto é, mudar um detalhe da implementação não faz com que sejam necessárias
não possui um framework apoiado e incentivado pela alterações no cliente. Este princípio é bastante presente em muitos padrões de projeto, pois a
Embarcadero. maioria deles definem uma interface para que não haja dependências de implementações.
Apesar da ferramenta não oferecer um framework Outro padrão que geralmente anda junto com o princípio de inversão de dependência é a
nativo para trabalharmos com orientado a objetos, a Injeção de Dependências, que é uma forma de conseguir a inversão de controle. Nesta solução, as
Delphi Language possui todos os recursos necessários dependências entre os módulos não são definidas programaticamente, mas sim pela configuração
para desenvolvermos orientado a objetos, como Generics de uma infraestrutura de software (container) que é responsável por injetar em cada classe suas
e Métodos Anônimos. dependências declaradas.
O padrão de Injeção de dependências sugere que uma conexão com banco de dados seja injetada
Impedância Objeto Relacional na classe. Com isso, além de inverter a dependência, a classe não precisa se preocupar com o ciclo de
A Orientação a Objetos traz como principal vantagem vida das suas dependências (no exemplo da conexão, a classe não precisa abrir ou fechar conexão,
sobre a estruturada a representação de objetos de manei- apenas receber uma referência desta conexão e a utiliza).
Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia Edição 164 • ClubeDelphi 13
13
Desenvolvendo um Sistema Financeiro em Delphi– Parte 3
Existem soluções de bancos de dados orientados a objetos, mas evitando erros em tempo de execução do tipo Invalid Type Cas-
estes ainda não se tornaram populares, sendo os bancos de dados ting, muito comum para quem trabalhava com listas de objetos em
relacionais ainda muito mais utilizado que estes. versões anteriores a 2009 em Delphi. Para fazer uso de Generics
Não existe uma conversão direta entre os objetos que cons- temos que declarar o namespace System.Generics.Collections na
truímos nas linguagens de programação e o banco de dados seção uses do código.
relacionais, por isso é necessário criarmos o que é chamado de As duas principais classes deste namespace são a TList<T> e
Mapeamento Objeto-Relacional, onde objetos são transformados TobjectList<T>. A Classe TList<T> possibilita guardar coleções
geralmente em registros em uma tabela e vice-versa. de qualquer tipo de dados, tanto primitivos quanto objetos. Já
a classe TObjectList<T>, que é uma especialização de TList,
Mapeamento Objeto Relacional pode armazenar somente objetos. Ela tem a vantagem de que
Isso surgiu porque precisamos salvar o estado de um objeto (atri- no momento que liberarmos sua instância da memória, todos
butos, herança, polimorfismo) em tabelas do banco de dados. O os objetos contidos nela também são liberados, diferentemente
mapeamento básico pode ser feito através de conversões manuais das instâncias de TList, onde no caso de as usarmos para arma-
ou através de frameworks de persistência, onde ambas possuem zenar objetos, precisamos liberar cada objeto individualmente
vantagens e desvantagens, mas a conversão manual nos dá uma da memória.
melhor performance, enquanto que com frameworks de persis-
tência não precisamos nos preocupar com escrita de SQL. FireDAC
Em Delphi os frameworks de mapeamento objeto relacional FireDAC é a versão da Embarcadero para o AnyDAC, que foi
geralmente nos possibilitam a configuração de persistência adquirida e integrada ao Delphi e o C++ Builder. No Delphi o
através de arquivos XML ou o recurso de annotations ou custom FireDAC está disponível na versão XE3 e XE4 com um conjunto
atributes. de componentes extras, e a partir da versão XE5 vem na instala-
No mapeamento objeto relacional básico o que temos é o ma- ção padrão.
peamento de classes para uma tabela do banco de dados, temos É um conjunto de componentes de alta performance, de fácil
também o mapeamento de objetos da classe para registros da utilização e provê conexão com vários bancos de dados, tanto
tabela e as propriedades da classe para colunas da tabela. locais quanto coorporativos. Cada banco de dados possui um
driver e trabalhado de forma diferente. Os drivers do FireDAC
Padrão DAO (Data Access Object) são nativos para cada banco de dados e ainda possui pontes para
O padrão de projeto DAO surgiu com a necessidade de separar- ODBC e dbExpress. Vários bancos de dados são suportados, entre
mos a lógica de negócios da lógica de persistência de dados. eles temos: MySQL, SQL Server, Oracle e SQLite.
Este padrão permite que possamos mudar a forma de persistên- Para quem tem projetos multicamadas com Delphi e DataSnap
cia sem que isso influencie em nada na lógica de negócio, além (BOX 2), ou também servidores REST, basta migrar a parte Server
de tornar nossas classes mais legíveis. de DBExpress para FireDAC, pois o TFDQuery é um TDataSet,
Classes DAO são responsáveis por trocar informações com o portanto compatível com o TDataSetProvider e TClientDataSet.
SGBD e fornecer operações CRUD e de pesquisas, além de ser Na parte Client, continua o TClientDataSet sem necessidade de
capaz de buscar dados no banco e transformar em objetos ou qualquer alteração. Tem suporte a Firemonkey e VCL e possui
lista desses através de listas genéricas. Também deverá receber os um conjunto de componentes visíveis e não-visíveis, DataSets,
objetos, converter em instruções SQL e mandar para o bando de Adapters.
dados. Toda interação com a base se dará através destas classes,
nunca das classes de negócio, muito menos de formulários. BOX 2. DataSnap
Se aplicarmos este padrão corretamente será abstraído comple-
O Datasnap é o mecanismo desenvolvimento multicamadas do Delphi. Servidores de aplicação
tamente o modo de busca e gravação dos dados, tornando isso
Datasnap podem ser desenvolvidos tanto em Delphi quanto C++ Builder, porém a grande
transparente para aplicação, facilitando muito na hora de fazermos
vantagem está no cliente, que pode ser desenvolvido em qualquer linguagem de programação que
manutenção na aplicação ou migração de banco de dados.
tenha suporte a JSON. Em 2009 foi criado um novo driver para o DBExpress, mas ao invés deste driver
Também conseguimos centralizar a troca de dados com o SGBD,
conectar-se a um banco de dados qualquer, ele se conectaria a um servidor de aplicação. Assim, a
assim teremos um ponto único de acesso a dados e nossa aplicação
partir do Delphi 2009 a conexão do cliente com o servidor de aplicação passou a ser feita utilizando
um ótimo design orientado a objeto.
DBExpress trafegando os dados utilizando o protocolo TCP/IP.
Generics
Generics é um recurso que foi incorporado na versão 2009 do Classe Base DAO
Delphi, que permite criar estruturas genéricas. Na orientação a Abra o projeto no Delphi para continuarmos. Para facilitar o
objetos podemos definir o tipo de retorno de uma lista de objetos trabalho com as classes DAO, criamos uma classe base a qual será
e somente serão permitidas inserções de objetos daquele tipo, do herdada por todas as classes DAO que irão interagir com o banco
contrário ocorrerá um erro ainda em tempo de compilação, assim, de dados, conforme pode ser visto na Listagem 1.
14 ClubeDelphi • Edição 164 Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia
14
Listagem 1. Classe base TDAO iremos recorrer a ela, que nos dará uma conexão configurada e
pronta para executarmos comandos de consulta e atualização no
01 unit DAO.Base;
02 interface
banco de dados Firebird.
03 uses Neste exemplo, fixamos os parâmetros de conexão no atributo
04 DAO.ConnectionFactory, FireDAC.Comp.Client; ConnectionString, mas o ideal é que estes venham num arquivo
05 type
06 TDAO = class
separado como, por exemplo, um arquivo INI ou XML, para que
07 protected possamos mudar as informações do banco de dados sem que seja
08 Connection: TFDConnection; necessário compilar o aplicativo.
09 function GetKeyValue(ATable: string): Integer;
10 public
11 constructor Create; Listagem 2. Relembrando a classe TConnectionFactory
12 destructor Destroy; override;
13 end; 01 unit DAO.ConnectionFactory;
14 implementation 02 interface
15 { TDAO } 03 uses
16 constructor TDAO.Create; 04 FireDAC.Comp.Client, FireDAC.Stan.Def, FireDAC.Stan.Error,
17 begin FireDAC.Stan.Async,
18 Connection := TConnectionFactory.GetConnection; 05 FireDAC.VCLUI.Wait, FireDAC.DApt, FireDAC.Phys.FB;
19 end; 06 type
20 destructor TDAO.Destroy; 07 TConnectionFactory = class
21 begin 08 private
22 Connection.Free; 09 class var FDConnection: TFDConnection;
23 inherited; 10 public
24 end; 11 class function GetConnection: TFDConnection;
25 function TDAO.GetKeyValue(ATable, AColumn: string): Integer; 12 end;
26 var 13 implementation
27 SQL: string; 14 { TConnectionFactory }
28 Id: Integer; 15 class function TConnectionFactory.GetConnection: TFDConnection;
29 begin 16 begin
30 SQL := ‘select coalesce(max(‘ + AColumn + ‘),0) + 1 from ‘ + ATable; 17 FDConnection := TFDConnection.Create(nil);
31 Id := Integer(Connection.ExecSQLScalar(SQL)); 18 FDConnection.ConnectionString := ‘DriverID=FB;Server=127.0.0.1;
32 result := Id; Database=D:Database\DBFINANCEIRO.FDB;User_name=SYSDBA;
33 end; Password=masterkey’;
34 end. 19 FDConnection.Connected := True;
20 Result := FDConnection;
21 end;
22 end.
Nesta classe temos a declaração de uma referência para TFDCon-
nection com escopo protegido, de maneira que possamos acessar
este atributo em todas as classes derivadas desta. Usaremos este Classe TOrigemDAO
objeto em todas as classes descendentes, tanto para executar co- Para a classe de modelo TOrigem criaremos sua correspondente
mandos de consulta quanto comandos de atualização no banco DAO, chamada TOrigemDAO, apresentada na Listagem 3. Ela
de dados. deve ser capaz de receber um objeto do tipo TOrigem e persisti-
Temos também o método GetKeyValue, que deverá ser chamado lo na tabela ORIGENS do banco de dados. Da mesma forma, a
toda vez que precisarmos gerar uma nova chave para alguma classe TOrigemDAO deve ser capaz de buscar na base de dados
tabela, bastando passar por parâmetro o nome e a coluna que um registro da tabela ORIGENS e convertê-lo em um objeto da
desejamos receber a chave. classe TOrigem, como mostra a Listagem 4.
Utilizamos o método ExecSQLScalar do FireDAC para bus- Para facilitar a criação de objetos da classe TOrigem, criamos
carmos o valor desejado. Este deve ser usado sempre que nossa um construtor que recebe todos atributos da classe, de maneira
consulta retornar apenas uma informação. O seu retorno é do que, ao criarmos uma instância de TOrigem, já populamos todas
tipo variant, por isso fizemos um typecast antes de fazermos as suas propriedades, nos poupando diversas linhas de código,
atribuição à variável Id. como podemos ver nas linhas 25 a 30 da Listagem 4.
O mecanismo de geração da chave que escolhemos foi o de Na Listagem 5 temos todos os métodos para fazer as operações
pegar o máximo valor existente na coluna da tabela mais um, básicas da classe TOrigemDAO. No método insert não recebemos
porém poderíamos usar qualquer outro mecanismo, como o uso todos os campos da tabela por parâmetro e sim um objeto da classe
de Generators do Firebird, por exemplo. TOrigem, que virá com todas as propriedades populadas, ou de
Toda comunicação com o banco de dados será feita através de uma aplicação de teste ou da interface com o usuário.
um objeto da classe TFDConnection que será disponibilizado pela Nos métodos Insert, Update e Delete temos a declaração de uma
classe TConnectionFactory, como mostra a Listagem 2. variável SQL do tipo String, onde atribuiremos o comando SQL.
A classe TConnectionFactory é uma fábrica de conexões, ou Especificamente nos métodos Insert e Update faz-se necessária a
seja, cada vez que precisarmos de uma conexão com o FireDAC criação de uma variável que representa o TipoOrigem, do tipo Char.
Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia Edição 164 • ClubeDelphi 15
15
Desenvolvendo um Sistema Financeiro em Delphi– Parte 3
Listagem 3. Interface da Classe TOrigemDAO Connection. Este método possui três parâmetros: o primeiro é o
01 TOrigemDAO = class(TDAO) SQL que desejamos que executar, o segundo é um vetor com os
02 public parâmetros do SQL, e por último um vetor com os tipos de dados
03 function Insert(AOrigem: TOrigem): Integer; dos parâmetros passados. Este último parâmetro é opcional, e caso
04 procedure Update(AOrigem: TOrigem);
05 procedure Delete(AId: Integer); não seja informado, o próprio FireDAC se encarrega de identificar
06 function FindById(AId: Integer): TOrigem; de qual tipo é. Nesse caso, achamos interessante já passar esta in-
07 function FindAll: TObjectList<TOrigem>;
formação para que seja executado o mais performático possível.
08 end;
16 ClubeDelphi • Edição 164 Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia
16
Para isso, usaremos outro objeto da arquitetura do FireDAC que TFDQuery dentro de um bloco try/except, para garantirmos
é o FDQuery. Com ele podemos executar consultas na base de que o objeto será liberado da memória no final de seu uso.
dados que retornam um DataSet que pode ser percorrido, e assim O objeto FDQuery precisa de uma conexão para executar o
criarmos nossos objetos da classe TOrigem. comando, por esse motivo atribuímos a conexão na propriedade
Connection o SQL e damos um Open na query.
Listagem 6. Métodos de Consulta Na sequência verificamos se a query retornou algum registro:
caso não tenha retornado, lançamos uma exceção informando
01 function TOrigemDAO.FindAll: TObjectList<TOrigem>;
02 var
que nenhum registro foi encontrado na base de dados. Caso seja
03 FDQuery: TFDQuery; encontrado o registro, criamos um objeto da classe TOrigem e
04 TipoOrigem: TTipoOrigem; populamos suas propriedades com as colunas retornadas no
05 Origens: TObjectList<TOrigem>;
06 begin
DataSet do FDQuery.
07 FDQuery := TFDQuery.Create(nil); No método FindAll é onde teremos a recuperação de todas
08 try as linhas da tabela ORIGENS em uma lista genérica de TOri-
09 FDQuery.Connection := Connection;
gem. De início, dá para notar que o retorno deste método é um
10 FDQuery.SQL.Text := ‘select * from ORIGENS’;
11 FDQuery.Open(); TObjectList do tipo TOrigem.
12 if FDQuery.FieldByName(‘TIPO_ORIGEM’).AsString = ‘P’ then Para construirmos nossa lista criamos uma lista genérica de
13 TipoOrigem := toPagamento
TOrigem e executamos uma query que retorna todas as linhas
14 else if FDQuery.FieldByName(‘TIPO_ORIGEM’).AsString = ‘R’ then
15 TipoOrigem := toRecebimento; da tabela. Em seguida, percorremos todas as linhas do DataSet
16 Origens := TObjectList<TOrigem>.Create(); gerado e, para cada linha, criamos um objeto de TOrigem e
17 while not FDQuery.Eof do adicionamos na lista. No final, apenas atribuímos a variável
18 begin
19 Origens.Add(TOrigem.Create(FDQuery.FieldByName(‘ID_ORIGEM’).AsInteger, local Origens no retorno do método.
20 FDQuery.FieldByName(‘DESCRICAO’).AsString, TipoOrigem));
21 end; Classes TClienteDAO e TFornecedorDAO
22 finally
23 FDQuery.Free;
Para facilitar o trabalho com os objetos TCliente e TFornecedor
24 end; da Listagem 7, criamos construtores que recebem as proprie-
25 result := Origens; dades por parâmetro e já as populam, facilitando bastante a
26 end;
criação dos objetos e adição nas listas.
27
28 function TOrigemDAO.FindById(AId: Integer): TOrigem; A interface da classe TClienteDAO é bastante semelhante
29 var à classe TOrigemDAO, conforme podemos visualizar na
30 Origem: TOrigem;
Listagem 8. A diferença se dá pela adição de um novo método
31 FDQuery: TFDQuery;
32 TipoOrigem: string; de busca chamado FindByName (linha 06), que servirá para
33 begin realizarmos consultas no cadastro de clientes pelo nome.
34 FDQuery := TFDQuery.Create(nil); Nesta classe não temos o método FindAll, utilizado para retor-
35 try
36 FDQuery.Connection := Connection; nar todos os registros da tabela. Nesse caso, não devemos fazer
37 FDQuery.SQL.Text := ‘select * from ORIGENS este tipo de busca no banco de dados, pois com o aumento do
where ID_ORIGEM = ‘ + IntToStr(AId); tamanho da base de dados, pode-se retornar muitos registros
38 FDQuery.Open();
39 if FDQuery.RecordCount = 0 then
e deixar lento o aplicativo.
40 raise Exception.Create(‘Objeto não encontrado na base de dados!’); Os métodos Insert, Update e Delete da classe TClienteDAO
41 Origem := TOrigem.Create(); seguem a mesma linha do que já havíamos codificado na
42 Origem.Id := FDQuery.FieldByName(‘ID_ORIGEM’).AsInteger;
43 Origem.Descricao := FDQuery.FieldByName(‘DESCRICAO’).AsString;
classe TOrigemDAO, conforme a Listagem 9. Veja que não
44 TipoOrigem := FDQuery.FieldByName(‘TIPO_ORIGEM’).AsString; temos nenhum atributo do tipo enumeração e não precisamos
45 if TipoOrigem = ‘P’ then fazer nenhuma conversão, tornando ainda mais simples a
46 Origem.TipoOrigem := toPagamento
codificação.
47 else if TipoOrigem = ‘R’ then
48 Origem.TipoOrigem := toRecebimento; O método FindByName irá receber uma String com um nome
49 finally do cliente ou parte do nome, e que será usada em conjunto com
50 FDQuery.Free;
o operador Like, que permite fazermos pesquisa na base de da-
51 end;
52 Result := Origem; dos. Utilizamos também a função QuotedStr, que irá adicionar
53 end; aspas antes e depois da string afim de não recebermos erros do
banco de dados, conforme mostra a Listagem 10.
O restante do método se comporta de maneira bastante seme-
No método FindById buscamos um registro na base de dados lhante ao FindAll, criando uma lista genérica de objetos que é
a partir de um Id passado por parâmetro. Para executarmos populada a cada registro do DataSet que foi preenchido com
um comando SQL no FDQuery criamos um objeto da classe os dados vindos da tabela CLIENTES.
Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia Edição 164 • ClubeDelphi 17
17
Desenvolvendo um Sistema Financeiro em Delphi– Parte 3
01 type 26 end;
02 TCliente = class 27
03 private 28 implementation
04 FId: Integer; 29
05 FNome: string; 30 constructor TCliente.Create;
06 FCPF: string; 31 begin
07 public 32 end;
08 property Id: Integer read FId write FId; 33
09 property Nome: string read FNome write FNome; 34 constructor TCliente.Create(AId: integer; ANome, ACPF: string);
10 property CPF: string read FCPF write FCPF; 35 begin
11 constructor Create; overload; 36 FId := AId;
12 constructor Create(AId: integer; ANome, ACPF: string); overload; 37 FNome := ANome;
13 end; 38 FCPF := ACPF;
14 39 end;
15 TFornecedor = class 40
16 private 41 constructor TFornecedor.Create(AId: integer; ANome, ACNPJ: string);
17 FId: Integer; 42 begin
18 FNome: string; 43 FId := AId;
19 FCNPJ: string; 44 FNome := ANome;
20 public 45 FCNPJ := ACNPJ;
21 property Id: Integer read FId write FId; 46 end;
22 property Nome: string read FNome write FNome; 47
23 property CNPJ: string read FCNPJ write FCNPJ; 48 constructor TFornecedor.Create;
24 constructor Create(AId: integer; ANome: string; ACNPJ: string); overload; 49 begin
25 constructor Create; overload; 50 end;
Listagem 8. Interface da Classe TClienteDAO A interface da classe TFornecedorDAO não será exibida no cor-
po deste artigo, mas pode ser consultada no arquivo fonte que
01 TClienteDAO = class(TDAO)
02 function Insert(ACliente: TCliente): Integer; acompanha a revista no site. Sua definição é muito semelhante a
03 procedure Update(ACliente: TCliente); TClienteDAO, mudando-se apenas o tipo de objeto, de TCliente
04 procedure Delete(AId: Integer);
para TFornecedor.
05 function FindById(AId: Integer): TCliente;
06 function FindByName(AName: string): TObjectList<TCliente>; Na Listagem 11 temos a implementação de toda a classe TForne-
07 end; cedorDAO, que ficou com o código mais enxuto que o da TClien-
Listagem 9. Métodos Insert, Update e Delete da classe TClienteDAO
teDAO. Na linha 3, por exemplo, temos a execução do comando
Delete de forma direta, sem uma variável auxiliar para armazenar
01 function TClienteDAO.Insert(ACliente: TCliente): Integer; o SQL. Já no método Insert não temos a criação da variável auxiliar
02 var
03 SQL: string;
para armazenar o identificador da tabela, pois estamos atribuindo
04 IdCliente: Integer; diretamente na propriedade Id do objeto AFornecedor o retorno
05 begin do método herdado GetKeyValue.
06 IdCliente := GetKeyValue(‘CLIENTES’, ‘ID_CLIENTE’);
07 SQL := ‘insert into CLIENTES values (:PAR1, :PAR2, :PAR3)’; Na Listagem 12 temos a classe TItemConta com sua interface
08 Connection.ExecSQL(SQL, [ACliente.Id, ACliente.Nome, ACliente.CPF], e implementação. No SetNumParcela (linha 39) temos uma vali-
09 [ftInteger, ftString, ftString]); dação da informação que está sendo passada ao atributo Fnum-
10 end;
11 Parcela, que somente aceita valores positivos.
12 procedure TClienteDAO.Update(ACliente: TCliente);
13 var
14 SQL: string;
Classe TContaDAO
15 begin A Listagem 13 mostra as operações da classe DAO principal
16 SQL := ‘update CLIENTES set NOME_CLIENTE = :PAR1, CPF = :PAR2 where do sistema, pois engloba as duas principais classes de negócio, a
ID_CLIENTE = :PAR3’;
TContaPagar e TContaReceber.
17 Connection.ExecSQL(SQL, [ACliente.Nome, ACliente.CPF, ACliente.Id],
18 [ftString, ftString, ftInteger]); O método Delete é relativamente simples, pois somente chama
19 end; uma exclusão da linha da tabela CONTAS, isso porque confi-
20
21 procedure TClienteDAO.Delete(AId: Integer);
guramos o banco de dados para fazer delete cascade entre as
22 var tabelas CONTAS e ITENS_CONTA, ou seja, quando excluirmos
23 SQL: string; uma conta, automaticamente o banco de dados irá excluir todos
24 begin
25 SQL := ‘delete from CLIENTES where ID_CLIENTE = ‘ + IntToStr(AId);
os itens de conta vinculados a ela. Desta forma, reduzimos a
26 Connection.ExecSQL(SQL); codificação do nosso método, além de deixar a aplicação mais
27 end; performática, pois o próprio banco se encarrega de fazer a ex-
clusão em cascata.
18 ClubeDelphi • Edição 164 Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia
18
Listagem 10. Métodos FindById e FindByName da classe TClienteDAO
Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia Edição 164 • ClubeDelphi 19
19
Desenvolvendo um Sistema Financeiro em Delphi– Parte 3
20 ClubeDelphi • Edição 164 Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia
20
No método Insert temos a utilização de um importante recurso Desta vez o método Insert fará operações de inserção em duas
chamado transação (BOX 3), isso porque precisamos garantir que tabelas, por isso temos duas vezes a chamada do GetKeyValue:
nossos dados fiquem de forma íntegra na base de dados. Abrimos um para a tabela CONTAS e vários quantos forem os itens para
uma transação chamando o método StartTransaction da classe a tabela ITENS_CONTA, dentro de um foreach que irá percorrer
TFDConnection e, em seguida contornamos o restante dos pro- todos os itens de conta.
cedimentos de atualização com um bloco try/except, de maneira O método Update funciona de maneira semelhante ao método
que, caso ocorra algum erro durante este processo é chamado o Insert e também precisamos executá-lo dentro de uma transação,
método Rollback, caso contrário, é chamado o método Commit, afim de garantir a total integridade dos dados.
que confirma todas as alterações feitas no banco. Neste artigo pudemos observar o quanto é árduo o trabalho de
Pelo fato de termos uma única tabela para contas a pagar e a rece- codificarmos as classes DAO, de maneira a trabalhar totalmente
ber, se faz necessário o uso de uma variável chamada IdReferencia, orientado a objetos. Lembre-se que ainda temos as classes de
isto porque a tabela CONTAS deverá armazenar uma referência Modelo na interface com o usuário que correspondem a camada
para CLIENTES quando for uma conta a receber e uma referência de visão ou View.
para FORNECEDORES quando for uma conta a receber. Até a próxima.
BOX 3. Transações
Autor
Transações são um conjunto de operações atômicas que devem ser executadas pelo banco de dados FILIPE DALEPIANE
e existe para garantirmos a integridade dos dados após uma série atualizações, garantindo que
filipe.dalepiane@gmail.com
Bacharel em Ciência da Computação, certificado Delphi De-
todas ocorreram com sucesso. No caso de uma falha, todo o processo é cancelado, trazendo grande
veloper, colunista da revista Clube Delphi e .NET Magazine.
segurança ao sistema. Controle de transações é algo obrigatório em sistemas de informações.
Desenvolve em Delphi para Desktop e Mobile e C# para Web.
-ReadCommited: nível de isolamento padrão, onde as transações têm acesso a apenas dados Trabalha atualmente como Analista de Sistemas na AVMB Consultoria e
efetivados, que foram feitos commit, nunca acessa dados que ainda não foram confirmados em Assessoria em Informática em Santa Maria-RS (www.avmb.com.br).
outras transações.
-RepeatableRead: este nível enxerga os dados apenas no início da transação e não tem acesso a
dados não confirmados por outras transações. Você gostou deste artigo?
-DirtyRead: nível de isolamento mais baixo, permitindo que a transação corrente leia dados ainda
não confirmados por outras transações, mesmo sem o commit. Dê seu voto em www.devmedia.com.br/clubedelphi/feedback
-Serializable: maior nível de isolamento, onde todas as operações devem ser feitas de forma Ajude-nos a manter a qualidade da revista!
totalmente isolada, uma após a outra.
Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia Edição 164 • ClubeDelphi 21
21
Explorando a API do
Windows no Delphi –
Parte 1
Conheça e utilize recursos do Windows
A
s APIs do Windows são expostas através de DLLs API do Windows, que é um conjunto de DLLs que fazem parte do
que podem ser utilizadas no Delphi e quando as sistema, expondo as funções do mesmo. Nesse artigo vamos explorar
utilizamos estamos lidamos diretamente com o as APIs da categoria Arquivos, Cursores, Registros e Informações sobre
sistema operacional. Dentre as categorias existentes nas o sistema e Windows.
APIs, pode-se dizer que as principais são:
• Windows;
• Arquivos; Uma DLL (Dynamic-link library ou biblioteca de vínculo di-
• Informações sobre o sistema; nâmico), é um arquivo com extensão que consiste numa coleção
• Cursores; de funções e procedures que podem ser chamadas por outras
• Mensagens; aplicações e outras DLLs, que por sua vez, é ligada em tempo de
• Mouse; execução ao programa que as usa.
• Teclado;
• Impressoras; Informações sobre o Sistema
• Ícones; É possível obter informações sobre o Sistema através de algumas
• Arquivos INI; funções expostas:
• Registro; • GetComputerName: está declarada em kernel32.dll e irá ler o
• Dispositivos; nome do computador, que será devolvido em uma variável do
• Acessibilidade. tipo string. Esta deve ser passada como parâmetro na função. Sua
declaração é feita da seguinte forma:
A Embarcadero disponibiliza no Delphi o acesso a
essas APIs através da unit Windows, que realiza uma GetComputerNameA (ByVal lpBuffer As String, nSize As Long) As Long
ponte entre o código Delphi e as várias DLLs disponibi-
lizadas pelo sistema. As principais DLLs são: O parâmetro lpBuffer é uma sequência de caracteres que deve
• User32.dll; ser grande o suficiente para manter o nome do computador. Já
• kernel32.dll; nSize é o comprimento em caracteres de lpBuffer, geralmente
• Comdlg32.dll; usado com o valor 255.
• gdi32.dll; • GetUserName: está declarada em advapi32.dll e recupera o
• shell32.dll; nome do usuário que está logado no Windows. Este também é
• Advapi32.dll; retornado em uma string que devemos passar como parâmetro.
• winmm.dll. Sua declaração é a seguinte:
22 ClubeDelphi • Edição 164 Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia
22
GetUserNameA (ByVal lpBuffer As String, nSize As Long) As Long InfoSys. Já a propriedade Caption dos Tlabels deve ficar como
visto na Figura 1.
O lpBuffer é uma sequência de caracteres que deve ser grande
o suficiente para manter o nome do usuário. O nSize é o compri-
mento em caracteres de lpBuffer, geralmente com o valor 144.
Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia Edição 164 • ClubeDelphi 23
23
Explorando a API do Windows no Delphi – Parte 1
• CopyFile: está declarada em kernel32.dll e copia um arquivo - LpNewFileName - O arquivo de destino, ou seja, o novo
de um local para outro, assim como a cópia de um arquivo no arquivo para criar;
Windows Explorer. Em sua declaração temos três parâmetros: - BFailIfExists - Se 0, a função irá substituir LpNewFileName
- LpExistingFileName - O arquivo de origem, ou seja, o arquivo caso ele já existe, caso contrário, a função irá falhar.
a ser copiado;
Listagem 2. Implementação
24 ClubeDelphi • Edição 164 Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia
24
• MoveFile: move ou renomeia um arquivo ou pasta. Se - FILE_ATTRIBUTE_HIDDEN - Um arquivo oculto, que
um diretório é movido/renomeado, todos os subdiretórios e normalmente não é visível para o usuário, dependendo da
arquivos contidos nele serão afetados. A função retorna 1 se configuração;
for bem-sucedida ou zero se ocorrer um erro. Espera-se dois - FILE_ATTRIBUTE_NORMAL - Um arquivo sem atributos
parâmetros: (esse não pode ser usado com atributos);
- LpExistingFileName - O arquivo de origem ou diretório, ou - FILE_ATTRIBUTE_READONLY - Um arquivo de somente
seja, o arquivo ou diretório para renomear (mover); leitura;
- LpNewFileName - O arquivo de destino ou diretório, ou seja, - FILE_ATTRIBUTE_SYSTEM - Um arquivo de sistema,
o novo nome do arquivo ou diretório, que se dá ao arquivo de utilizado exclusivamente pelo sistema operacional;
origem para que seja movido. - FILE_FLAG_DELETE_ON_CLOSE - Exclui o arquivo, uma
• CreateFile: cria ou abre um arquivo em disco para acesso vez que o mesmo está fechado;
posterior. É necessário ter os direitos de acesso permitidos para - FILE_FLAG_OVERLAPPED - Permiti que o arquivo seja
o arquivo a ser utilizado. Essa função tem inúmeros parâmetros lido e gravado ao mesmo tempo. Se for utilizado, as fun-
para especificar os níveis e tipos de acesso, e irá retornar o iden- ções que leem e escrevem no arquivo devem especificar
tificador para o arquivo criado/aberto se for bem-sucedido, ou -1 essa estrutura (OVERLAPPED) para identificar o ponteiro
se um algum erro ocorreu. Seus parâmetros são: do arquivo;
- LpFileName - O nome do arquivo a ser criado ou aberto; - FILE_FLAG_POSIX_SEMANTICS - Permiti que o nome
- DwDesiredAccess - Zero ou um dos seguintes parâmetros, do arquivo seja maiúsculo ou minúsculo;
especificando as quantidades de acesso, de leitura e gravação - FILE_FLAG_RANDOM_ACCESS - Otimiza o cache de
para o arquivo: arquivos para acesso aleatório (poder pular por várias
- GENERIC_READ - Permitir que o programa leia os dados partes do arquivo);
do arquivo; - FILE_FLAG_SEQUENTIAL_SCAN - Otimiza o cache de
- GENERIC_WRITE - Permitir que o programa grave dados arquivos para acesso sequencial crescente);
no arquivo. - FILE_FLAG_WRITE_THROUGH – Lê e escreve diretamen-
- DwShareMode - Zero ou um dos seguintes parâmetros, te no arquivo, ignorando qualquer cache de disco;
especificando as quantidades de acesso, de leitura e gravação - HTemplateFile – Identifica 1 arquivo aberto e copia os atri-
concedidas a outros programas enquanto o programa ainda butos, ou zero para não copiar os mesmos.
está com ele aberto: • DeleteFile: exclui um arquivo completamente, sem enviá-lo
- FILE_SHARE_READ - Permitir que outros programas para a lixeira. Ele também não solicita a confirmação da exclusão,
possam ler os dados do arquivo; então deve ser utilizado com toda atenção. A função retorna 1 se
- FILE_SHARE_WRITE - Permitir que outros programas for bem-sucedida, ou zero caso tenha ocorrido algum erro como,
possam gravar dados no arquivo. por exemplo, quando o arquivo a ser excluído não existe. A função
- LpSecurityAppributes - Os atributos de segurança dados ao espera por um único parâmetro LpFileName, que representa o
arquivo criado ou aberto; nome do arquivo a ser excluído;
- DwCreationDisposition - Exatamente um dos seguintes parâ- • FindClose: termina a pesquisa de um arquivo iniciado por
metros, especificando como e quando criar ou abrir o arquivo, FindFirstFile. Esta função fecha o identificador da pesquisa de
dependendo se ele já existe ou não: arquivos;
- CREATE_ALWAYS - Cria um novo arquivo, substituindo • FindFirstFile: começa uma pesquisa de arquivo e fornece infor-
o mesmo caso esse já exista; mações sobre o primeiro arquivo correspondente. As pesquisas
- CREATE_NEW - Cria um novo arquivo, mas falha se ele de arquivos têm base em apenas um nome de arquivo com sua
já existe; extensão ou não. A pesquisa só olha em um único diretório, mas
- OPEN_ALWAYS - Abre um arquivo existente e, se o arquivo identifica quaisquer nomes no mesmo que corresponde à sequência
não existir, ele será criado; da pesquisa A função retorna um identificador de pesquisa que
- OPEN_EXISTING - Abre um arquivo existente, mas falha pode ser usado para procurar por arquivos correspondentes adicio-
se o arquivo não existe. nais, usando FindNextFile. Pode ser retornado -1 também caso não
- TRUNCATE_EXISTING - Abre um arquivo existente e haja arquivos coincidentes com a pesquisa. Seus parâmetros são:
apaga o seu conteúdo. A função falhará se o arquivo não - LpFileName – É a sequência de pesquisa de arquivos para
existe. procurar, incluindo o caminho completo. Pode conter os
- DwFlagsAndAttributes - Uma combinação dos seguintes curingas como * ou ?;
parâmetros, especificando os atributos do arquivo para um - LpFindFileData - Recebe informações de identificação sobre
recém-criado, para cria-lo ou abri-lo. Deve ser incluso um o primeiro arquivo encontrado.
handle para o arquivo, para especificar os seus atributos: • FindNextFile: continua uma pesquisa de arquivo que começou
- FILE_ATTRIBUTE_ARCHIVE - Um arquivo normal; com FindFirstFile. Encontra e fornece informações de identificação
Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia Edição 164 • ClubeDelphi 25
25
Explorando a API do Windows no Delphi – Parte 1
sobre o próximo arquivo que corresponde à sequência da pesquisa. A propriedade Name do Form1 é modificada para Frm_Prin-
A função retorna 1 se foi encontrado um outro arquivo, ou zero cipal e os seis TButons para Btn_Copiar, Btn_Mover, Btn_Criar,
se não existem mais arquivos correspondentes (ou se ocorreu um Btn_Apagar, Btn_CriaDir e Btn_BuscArqui. O TListBox é chamado
erro). Seus parâmetros são: de Lst_BuscArqui, e os seis TLabel devem ter a propriedade Name
- HFindFile - identificador do arquivo para a pesquisa come- modificada para Lbl_Copiar, Lbl_Mover, Lbl_Apagar, Lbl_Criar,
çado com FindFirstFile; Lbl_CriaDir e Lst_BuscArqui. Salvamos a unit com o nome de
- LpFindFileData - recebe informações de identificação sobre o Unt_Principal e o projeto como Arquivos. Mude também a pro-
próximo arquivo correspondente que foi encontrado. priedade Caption dos TLabels e TButtons conforme a Figura 2.
• CreateDirectory: cria um novo diretório em disco e define os Incluímos na seção uses a unit ShellApi, pois precisaremos dela
atributos de segurança do mesmo. A função retorna 1 se for bem- para criar, copiar, mover, apagar os arquivos. Usaremos arquivos
sucedida, ou zero se ocorrer algum erro. Seus parâmetros são: de textos normais (*.txt).
- LpPathName - nome do novo diretório a ser criado; A Listagem 3 mostra a implementação do botão Buscar Arquivos.
- LpSecurityAttributes - atributos de segurança para dar ao TSearchRec é um record que está declarado em SysUtils e define
novo diretório. uma estrutura de dados, que veremos na Listagem 4. Ela será
• FileExists: retorna um valor boolean se o arquivo especificado utilizada para armazenar informações de pesquisa de arquivos
como parâmetro existe ou não. pelas rotinas FindFirst e FindNext.
• DirectoryExists: retorna um valor boolean se o diretório espe-
cificado existir ou não. Seu único parâmetro é o Directory, que Listagem 3. Usando as funções e implementando o código dos botões
representa o nome do diretório para verificar sua existência. A
procedure TFrm_Principal.Btn_BuscArquiClick(Sender: TObject);
função poderá falhar se o usuário não tiver permissão para o
var
caminho informado do diretório. SR: TSearchRec;
• CloseHandle: fecha um identificador e o objeto associado a ele. Pasta: String;
begin
Depois de ter sido fechado, o identificador não será mais válido. Pasta := ‘C:\Apagar Arquivos Txt’;
A função retorna 1 se for bem-sucedida, ou zero se ocorreu al-
if not(DirectoryExists(Pasta)) then
gum erro. Seu parâmetro HObject representa o identificador do begin
objeto a fechar. Application.MessageBox(‘Não Existe a Pasta “C:\Apagar Arquivos Txt” ‘,
‘ Atenção’,MB_ICONINFORMATION + MB_OK);
Exit;
Aplicação sobre Arquivos end;
Ao criar um novo aplicativo do tipo Win32, adicionamos ao
If FindFirst(Pasta +’\*.txt’, faAnyFile, SR) =0 then
formulário principal seis componentes TLabel, seis TButton e um begin
TListBox, como mostra a Figura 2. Repeat
if (SR.Attr and faDirectory) <> faDirectory then
Lst_BuscArqui.Items.Add(Sr.Name);
FindClose(SR);
end;
end;
TSearchRec = record
Time: Integer;
Size: Integer;
Attr: Integer;
Name: TFileName;
ExcludeAttr: Integer;
FindHandle: THandle;
FindData: TWin32FindData;
end;
26 ClubeDelphi • Edição 164 Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia
26
- faHidden: Arquivos ocultos; GetForegroundWindow
- faSysFile: Os arquivos de sistema; Esta função acha a janela que está atualmente em primeiro pla-
- faVolumeID: Volume: arquivos ID; no. A janela em primeiro plano é a janela geralmente na qual o
- faDirectory: Arquivos Diretório; usuário está atualmente trabalhando, ou seja, a janela com o foco.
- faArchive: Arquivos; A função retorna zero se um erro ocorrer, ou o identificador da
• Name: Nome do arquivo. janela se bem-sucedido.
Windows GetWindowText
A categoria Windows permite interação com suas janelas, habi- Retorna o texto da barra de título de uma janela. Esta função
litar e desabilitar diversas configurações e muito mais. Essa API funciona com qualquer janela, não apenas aquelas em sua aplica-
conta com 31 funções, mas para esse artigo apresentaremos apenas ção. O texto é devolvido em uma variável do tipo String, passada
as principais que serão utilizadas na aplicação de exemplo para como parâmetro. A função também retorna o comprimento do
entendermos mais sobre. texto, se bem-sucedida, ou zero se ocorreu algum erro. Seus
parâmetros são:
ShowWindow • Hwnd - A janela para ler o título;
A função pode minimizar, maximizar ou restaurar uma deter- • LpString - Variável que recebe o texto da barra de título da
minada janela. Retorna zero se a janela estiver invisível antes janela;
da chamada, ou um valor diferente se estiver visível. Recebe os • CCH - O comprimento em caracteres de LpString, ou seja, a
seguintes parâmetros de entrada: quantidade de caracteres do título da janela.
• Hwnd - O identificador da janela, para alterar o status de como
é mostrado; GetWindowTextLength
• NcmdShow - Exatamente um dos seguintes parâmetros, espe- Retorna o comprimento em caracteres do texto da barra de
cificando como mostrar a janela: título de uma janela, ou retorna zero se ocorrer erro. Seu único
- SW_HIDE - Esconde a janela; parâmetro é Hwnd, que deve receber o identificador da janela a
- SW_MAXIMIZE - Maximiza a janela; ser lida.
- SW_MINIMIZE - Minimiza a janela;
- SW_RESTORE - Restaura a janela (não maximizada e nem EnableWindow
minimizada); Essa função ativa ou desativa uma janela. Se estiver desativada,
- SW_SHOW - Mostra a janela; ela não pode receber o foco e irá ignorar qualquer tentativa de
- SW_SHOWMAXIMIZED - Mostra a janela maximizada; entrada. Alguns tipos de controles, como botões, aparecerão desa-
- SW_SHOWMINIMIZED - Mostra a janela minimizada; tivados, embora qualquer janela possa ser ativada ou desativada.
- SW_SHOWMINNOACTIVE - Mostra a janela minimizada, A função retorna zero se a janela está ativada, ou um valor diferen-
mas não a ativa; te de zero se a janela está desativada. Recebe dois parâmetros:
- SW_SHOWNA - Mostra a janela em seu estado atual, mas • Hwnd -Um identificador para a janela a ser ativada ou desa-
não a ativa; tivada;
- SW_SHOWNOACTIVATE -Mostra a janela em seu tamanho • FEnable - Se zero, a janela será desativada, caso contrário, a
e a posição mais recente, mas não ativa; janela será ativada.
- SW_SHOWNORMAL - Mostra a janela e a ativa (geralmente
o normal). SetWindowPos
A função move uma janela para um novo local na tela. Suas
FindWindow coordenadas físicas, dimensões e posição, bem como o Z-order,
Esta função procura por todas as janelas abertas que corres- que determina se a janela está em cima das outras, podem ser
pondam ao nome da classe da janela informado e/ou nome da definidos. A função retorna zero caso ocorra um erro. A relação
janela. Essa busca não é sensível a maiúsculas e seus parâmetros de seus parâmetros pode ser vista a seguir.
são relacionados a seguir: • Hwnd – Move a janela;
• LpClassName - O nome da classe da janela para se encontrar. • HwndInsertAfter – É o identificador da janela para posicionar
Passe zero para permitir que a janela seja de qualquer classe; esta janela para trás. Um dos seguintes parâmetros pode ser pas-
• LpWindowName - O texto da barra de título da janela para se encon- sado, indicando onde Z-ordem deve colocar a janela:
trar. Passe zero para permitir que a janela tenha qualquer nome. - HWND_BOTTOM - Coloca a janela na parte inferior;
- HWND_NOTOPMOST - Coloca a janela abaixo de todas
Se ocorrer algum erro, ou uma janela correspondente não puder as janelas de nível superior, e acima de todas as janelas não-
ser encontrada, a função retorna zero. Caso contrário, a função superiores;
retornará um identificador para a janela encontrada. - HWND_TOP - Coloca a janela na parte superior;
Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia Edição 164 • ClubeDelphi 27
27
Explorando a API do Windows no Delphi – Parte 1
28 ClubeDelphi • Edição 164 Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia
28
Listagem 5. Implementando os Códigos dos Botões
chamar sua atenção é possível minimizar essas outras e deixar pelo seu número de ID. Se tudo estiver certo, a função retorna um
apenas seu sistema em foco. identificador para o cursor carregado, caso contrário, a função
retorna zero. Seus parâmetros são:
Cursores - HInstance – Carrega o cursor a partir de um arquivo de re-
Os Cursores fazem parte de uma categoria que pode facilmente curso do programa. Pode definir zero caso queira carregar de
ser confundida com Mouse, que é outra categoria. Por exemplo, um arquivo de recurso do Windows.
quando o cursor exibir uma ampulheta, não é o mouse que a de- - LpCursorName - Informe o nome ou o número do cursor,
tém, e sim o cursor. Nessa categoria temos as seguintes funções para que seja carregado através, de um arquivo de recurso do
relacionadas a seguir: Windows. Para cursores do Windows, use uma das opções
• ShowCursor: mostra ou esconde o cursor do mouse. Na verdade, para carregar o cursor desejado:
é um contador e, se for 1 então o cursor é visível, se esse contador - IDC_APPSTARTING - O cursor inicial (seta e ampu-
for negativo, então o cursor não será visível. A função retorna o lheta).
valor deste contador e possui o parâmetro Bshow, que se for zero, - IDC_ARROW - O cursor ponteiro de seta regular.
diminui o contador em 1, caso contrário, incrementa em 1. - IDC_CROSS - O cursor transversal.
• GetCursor: encontra o identificador para o cursor do mouse - IDC_IBEAM - O cursor em forma de I (cursor de edição
em uso atualmente. Esse é o cursor que está sendo usado para de texto).
representar o ponteiro do mouse na tela. A função retorna um - IDC_NO - O cursor com círculo com uma barra.
identificador para a imagem se bem-sucedido, ou retornará zero - IDC_SIZEALL - O cursor de quatro pontas.
se algum erro ocorrer. - IDC_SIZENESW - O cursor de duas pontas, apontando
• GetCursorPos: lê a posição atual do cursor do mouse, ou seja, as para o canto superior direito, e inferior esquerdo.
coordenadas X e Y do cursor em relação à tela. Essas informações - IDC_SIZENS - O cursor de duas pontas, apontando para
são transferidas para o parâmetro LpPoint. A função retorna zero cima e para baixo.
se ocorreu um erro ou 1 se for bem-sucedida. - IDC_SIZENWSE - O cursor de duas pontas, apontando
• LoadCursor: Carrega um cursor a partir de um arquivo de para o canto inferior direito, e superior esquerdo.
recurso do programa ou de um arquivo de recurso de cursor do - IDC_SIZEWE - O cursor de duas pontas, apontando para
próprio Windows, que pode ser referenciado pelo seu nome ou a esquerda e para a direita.
Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia Edição 164 • ClubeDelphi 29
29
Explorando a API do Windows no Delphi – Parte 1
- IDC_UPARROW - O cursor de seta para cima. como cursor em uso, depois esperamos cinco segundos e desfa-
- IDC_WAIT - O cursor de espera (ampulheta). zemos a operação. Por fim, em Btn_PosCursorClick modificamos
• SetCursor: define a imagem usada para representar o cursor do a posição do cursor para 30, 30 pixels.
mouse. O novo cursor pode ser qualquer um válido que tiver sido
criado ou carregado. Se for bem-sucedida, a função retorna um Listagem 6. Implementando os Códigos dos Botões
identificador para a imagem do cursor, caso contrário, a função
procedure TFrm_Cursor.Btn_ShowCursorClick(Sender: TObject);
retorna zero. Seu único parâmetro é o HCursor, que representa begin
um identificador válido de cursor. ShowCursor(False);
Sleep(7000);
• SetCursorPos: define a posição do cursor do mouse. Se você ShowCursor(True);
tentar definir as coordenadas fora da área da tela, por exemplo, end;
se você definir a posição para 700,40 em uma tela de 640x480, o
procedure TFrm_Cursor.Btn_TrocaCurClick(Sender: TObject);
cursor irá até a borda da tela ou retângulo. Os parâmetros dessa var
função são justamente essas coordenadas X e Y. CursorAnt: Integer;
CursorNov: Integer;
begin
CursorAnt := GetCursor();
Aplicação de Cursores CursorNov := LoadCursor(0, IDC_SIZEALL);
SetCursor(CursorNov);
Em uma nova aplicação do tipo Win32 adicionamos cinco con- Sleep (5000);
troles TLabel e cinco TButton, como mostra a Figura 4. Com a tela SetCursor(CursorAnt);
end;
já montada, modificamos as propriedades Name do Form1 para
Frm_Cursor e os cinco TButon para Btn_ShowCursor, Btn_Troca- procedure TFrm_Cursor.Btn_PosMouseClick(Sender: TObject);
Cur, Btn_PosMouse, Btn_BuscCursor, e Btn_PosCursor. Ao salvar var
Cord: TPoint;
a aplicação definimos a unit com o nome de Unt_Principal e o pro- begin
jeto como Cursores. A Listagem 6 mostra a utilização da API. GetCursorPos(Cord);
Showmessage(‘O Mouse esta na Posição X ‘ + IntTostr(Cord.X) +’ Posição Y ‘ +
IntTostr(Cord.Y));
end;
Registro
Podemos dizer que várias configurações do Sistema Operacional
Windows se encontram gravadas no “Registro do Windows”.
Figura 4. Tela do Aplicativo O registro do Windows mantém essas configurações em uma
espécie de dicionário, onde temo o par chave e valor. A confi-
]O procedimento Btn_ShowCursorClick simplesmente esconde e guração em si é a chave, e seu conteúdo é o valor. Qualquer mu-
exibe o cursor do mouse. Entre uma operação e outra foi utilizado dança realizada em registros existentes deve ser feita com todo o
o procedimento sleep, que pausa o processamento do aplicativo cuidado, porque chave com valor incorreto pode desestabilizar
em execução por uma quantidade de segundos, expressos em o sistema operacional.
milissegundos. Já o procedimento Btn_TrocaCurClick realiza a Um exemplo de configuração do Windows armazenada no
troca do cursor. Observe que o cursor em uso é armazenado em registro são os dados retornados pela função GetVersionEx, nos
uma variável para que possa posteriormente ser recuperado. traz a versão do Windows instalado no computador. O mesmo
Com o procedimento Btn_PosMouseClick recuperamos a posição poderia ser feito lendo a seguinte chave do registro:
atual do cursor e a exibimos em tela. No procedimento Btn_Busc-
CursorClick recuperamos um cursor específico e o definimos HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WindowsNT\CurrentVersion\ProductName
30 ClubeDelphi • Edição 164 Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia
30
Então podemos imaginar que, caso modifiquemos essa chave um código de erro diferente de zero. Seus parâmetros são:
por código, a função GetVersionEx poderia retornar um valor • HKey - Um identificador para uma chave do registro, que é a
incorreto. A seguir temos funções da categoria Registro. chave a ser excluída. Ou um dos seguintes valores, especificando
a chave do registro:
RegOpenKeyEx • HKEY_CLASSES_ROOT - A chave base de HKEY_CLAS-
Abre uma chave no registro do Windows. Esta função não irá criar SES_ROOT.
a chave, se ela não existir. A função retorna zero se consegui abrir a • HKEY_CURRENT_CONFIG - A chave base de HKEY_CUR-
chave, ou um código de erro diferente de zero caso não consiga abrir, RENT_CONFIG.
seus parâmetros são: • HKEY_CURRENT_USER - A chave base de HKEY_CUR-
• HKey - A chave do registro aberto, ou um dos seguintes valores, RENT_USER.
sobre o qual a chave está sob: • HKEY_DYN_DATA - A chave base de HKEY_DYN_DATA.
• HKEY_CURRENT_USER - Armazena informações sobre os • HKEY_LOCAL_MACHINE - A chave base de HKEY_LO-
programas do usuário atual. CAL_MACHINE
• HKEY_LOCAL_MACHINE - Armazena informações sobre • HKEY_PERFORMANCE_DATA - A chave base de HKEY_
os programas para todos os usuários. PERFORMANCE_DATA.
• HKEY_USERS - Contém informações de qualquer usuário, e • HKEY_USERS - A chave base de HKEY_USERS.
não apenas o fornecido pela HKEY_CURRENT_USER. • LpSubKey - O nome da subchave dentro da chave HKey a
• HKEY_CURRENT_CONFIG - Armazena informações de excluir.
configuração do computador.
• HKEY_DYN_DATA - Armazena dados dinâmicos. RegDeleteValue
• LpSubKey - O nome da chave para abrir. Exclui um valor armazenado em uma chave especificada no
• UlOptions - Reservado. Defina como zero. registro. Esta função só funciona com valores armazenados, não
• SamDesired - Um ou mais dos seguintes valores, especificando podendo excluir subchaves. O valor pode ser de qualquer tipo de
o acesso de leitura / gravação desejado: dado do registo. A função retorna zero se for bem-sucedida, ou
• KEY_ALL_ACCESS - Permissão para todos os tipos de acesso. um código de erro diferente de zero. Parâmetros:
• KEY_CREATE_LINK - A permissão para criar links sim- • HKey - Um identificador para a chave do registro aberto, que
bólicos. contém o valor a ser excluído.
• KEY_CREATE_SUB_KEY - Permissão para criar subchaves. • LpValueName - O nome do valor a ser excluído.
• KEY_ENUMERATE_SUB_KEYS - Permissão para enumerar
subchaves. WriteString
• KEY_EXECUTE - O mesmo que KEY_READ. WriteString é uma procedure do Delphi e está declarada na unit
• KEY_NOTIFY - Autorização para prestar notificação de Registry. O procedimento irá escrever um valor, de qualquer tipo
alteração. de dado, na chave especificada.
• KEY_QUERY_VALUE - Permissão para consultar dados
subchave. ReadString
• KEY_READ - Permissão para o acesso de leitura em geral. Já ReadString é uma função, que também está declarada na unit
• KEY_SET_VALUE - Permissão para definir dados subchave. Registry. Ela vai ler o valor de uma chave, passada como parâme-
• KEY_WRITE - Permissão para o acesso geral de gravação. tro, e irá devolver uma string com o valor da chave.
• PhkResult - Recebe a informação da chave do registro.
GetValueNames
RegCloseKey GetValueNames é uma procedure do Delphi e também está
RegCloseKey fecha uma chave de registro. Isto deve ser feito declarada na unit Registry. O procedimento retorna uma lista
depois que terminar de ler ou escrever no registro. Fechando a de strings (Tstrings) de uma chave específica passada como
chave de registro são liberados alguns recursos. Obviamente, parâmetro.
você não pode mais usar a chave depois de fechá-la, será preciso
abri-la novamente. A função retorna zero se for bem-sucedida, Aplicação de Registro
ou um código de erro diferente de zero. Seu único parâmetro é Criamos uma nova aplicação Win32 e seu formulário principal
HKey, a chave do registro para fechar. adicionamos seis controles TLabel, quatro TButton, dois TMemo
e um TImage, como vemos na Figura 5. Nosso aplicativa vai confi-
RegDeleteKey gurar a calcular para ser iniciada junto ou não com a inicialização
RegDeleteKey apaga uma chave do registro. A chave a ser apagada do Windows e irá mostrar os papéis de parede registrados.
não pode ter quaisquer subchaves dentro dela ou então a operação Com a tela já montada modificarmos algumas propriedades.
de exclusão falhará. A função retorna zero se for bem-sucedida, ou A propriedade Name do Form1 para Frm_Registro e os seis
Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia Edição 164 • ClubeDelphi 31
31
Explorando a API do Windows no Delphi – Parte 1
32 ClubeDelphi • Edição 164 Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia
32
Continuação: Listagem 8. Implementando os Códigos do Aplicativo
if Reg.OpenKey(‘\Control Panel\Desktop’, True) then Reg.RootKey := HKEY_LOCAL_MACHINE;
begin
Img_PapParede.Picture.LoadFromFile(Reg.ReadString(‘Wallpaper’)); if Reg.OpenKey(‘\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\
Reg.CloseKey; Shell Folders’, True) then
end; begin
Lista := TStringList.Create;
Finally Reg.GetValueNames(Lista);
Reg.Free;
Inherited; Mmo_NomeChaves.Lines.Add(Lista.Text);
end;
end; For I := 0 to Lista.Count -1 do
Mmo_ValorChaves.Lines.Add(Reg.ReadString(Lista.Strings[I]));
procedure TFrm_Registro.Btn_ItensClick(Sender: TObject);
var Lista.Free;
Reg: TRegistry; end;
Lista: TStrings;
I: integer; Finally
begin Reg.CloseKey;
Reg := TRegistry.Create; Reg.Free;
end;
Try end;
Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia Edição 164 • ClubeDelphi 33
33
Cadastros e relatórios
dinâmicos em Delphi
Permita ao usuário criar cadastros e relatórios no
sistema
P
artindo da ideia que um sistema inteligente é Fique por Dentro
aquele que pode ser customizado de acordo com
a necessidade do cliente, hoje em dia não temos Empresas que estão ligadas ao desenvolvimento de software
muitos sistemas inteligentes. Há diversos sistemas preocupam-se cada vez mais em construir softwares que sejam ro-
grandes, que são construídos com base em um ramo bustos e atendam a todas as necessidades de seus clientes. Sistemas
de negócio. Porém, quando é necessário fazer uma de gestão empresarial, ERP (Enterprise Resource Planning), sempre
simples mudança em um relatório ou em um cadastro, têm a necessidade de novos módulos, ou adequações para atender
os clientes recebem a reposta que o sistema está estável um maior público. Módulos como financeiro, administrativo e tantos
e que diversas outras empresas usam o mesmo sem outros, geralmente possuem seus cadastros e movimentos, e normal-
problemas. Mas a resposta ideal à sua solicitação seria: mente possuem uma grande quantidade de campos. Por esses e tantos
vamos adequar, vamos fazer tais mudanças e alterações. outros motivos, há vários relatórios, com campos diversificados, afim
Sendo assim é hora de pensar, será que realmente meu de tornar esses cadastros e movimentos mais eficientes. Este artigo
negócio está competitivo? Como deve ser a estrutura, apresenta uma forma de permitir aos próprios usuários a criação de
da modelagem de dados? Meus cadastros, relatórios e cadastros e relatórios, tornando o sistema muito dinâmico e eficiente
gráficos necessitam constantemente de mudanças? em diversos aspectos.
Então por que não fazer um sistema mais inteligente,
ou adicionarmos um módulo ao nosso sistema que per-
mita ao próprio cliente, customizar e criar seus próprios ferramenta de geração de relatórios. Com ele podemos criar desde
relatórios e ainda indo um pouco mais longe, por que relatórios simples até os mais complexos. A suíte disponibiliza
não dar a possibilidade de ele mesmo criar cadastros. também o FastScript que permite a criação de scripts em várias
Veremos uma forma de como um cliente pode criar linguagens de programação, o FastReport Exports que permite
cadastros e relatórios personalizados. a exportação de relatórios do FastReport para diversos formatos
Para isso usaremos como banco de dados o Firebird, a como XLS, HTML, CSV entre outros. Dentre seus vários recursos,
ferramenta de relatório será o FastReport, e a parte de da sua versão comercial, usaremos o cross-tab, para criarmos esses
conexão de dados usaremos unidac. Lembrando que relatórios personalizados.
qualquer que seja o banco de dados, ou qualquer que
seja a empresa, seu negócio tem que ser inteligente para Unidac
que tenha uma maior competitividade. O UniDAC provê suporte e acesso a diversos servidores de banco
de dados como Oracle, Firebird, InterBase, Microsoft SQL Server,
FastReport PostgreSQL, MySQL, entre outros. Atende a diversas ferramentas
O FastReport era uma suíte unicamente externa para (Delphi, C++ Builder, Lazarus e Free Pascal) em diferentes plata-
geração de relatórios em Delphi. Essa suíte passou a formas (Windows, Mac OS, iOS, Linux e FreeBSD).
ser incorporada como ferramenta oficial de desenvolvi- Pode se dizer que a estrutura do Unidac é composta por dois
mento de relatórios a partir do Delphi XE2, possuindo elementos. O primeiro deles seria uma engine, ou seja, seu mo-
uma versão própria para essa finalidade. O FastReport tor que provê ao desenvolvedor uma interface de programação
possui algo muito interessante que é a conversões de comum e unificada, receptível aos diversos bancos suportados.
relatórios Quick Report, Rave Reports e Report Builder Já o segundo elemento é a sua parte fundamental, que é a sua
por meio de units. É considerado por muitos uma ótima camada de acesso a dados. Esse acesso a dados é composto pelos
verificações de erros e tudo mais, para que possa ter uma boa ALTER TABLE TAB_CAMPOS ADD CONSTRAINT FK_CAMCODIGO
usabilidade. FOREIGN KEY (TABELA) REFERENCES TABELA_USUPER (TABELA)
ON DELETE CASCADE ON UPDATE CASCADE;
Criando a aplicação
No Delphi criamos um novo projeto Win32 e salvamos a unit
principal com o nome de Unt_Principal.pas e o formulário como
Frm_Principal. O projeto salvamos como CriarCadastro ou
conforme o gosto. Adicionamos a seguir um novo formulário,
salvando-o como Unt_CriaCadastro e o nomeando-o como
Frm_CriaCadastro. Agora repetindo o processo, adicionamos mais
um formulário e salvamos como Unt_ExcCadastro e o nomeamos
como Frm_ExcCadastro. Finalizando a criação dos formulários,
adicionamos o último formulário e salvamos sua unit como
Unt_Cadastro, e Frm_Cadastro. Na Tabela 2 identificamos qual
será a funcionalidade dos formulários.
Figura 2. Formulário principal
Formulário Funcionalidade
Formulário principal da aplicação, onde ficam os menus
Frm_Principal Listagem 2. Private, Public e Uses do formulário Principal
que chamam todos os outros formulários (Telas).
Pode ser considerado o formulário mais importante, é private
Frm_CriaCadastro
onde será criado os cadastros e relatórios. MenuCad,
Será o formulário que apresentará todos os cadastrados MenuTabPer: TMenuItem;
Frm_ExcCadastro
criados, para que possam ser excluídos. ImgItMenu: Integer;
E por fim o nosso formulário do cadastro, onde o usuário
Frm_Cadastro irá cadastrar, manipular os seus dados e chamar o seu procedure MenuCadPerClick(Sender: TObject);
{ private declarations }
relatório.
public
Tabela 2. Funcionalidades dos formulários procedure AdicionaMenu(Menu: String);
procedure RemoveMenu(Menu: String);
Programando o formulário principal { public declarations }
end;
Adicionamos então aos formulários os seguintes componentes
para conexão: TuniConnection, Provider: TinterBaseUniProvider, var
Transação: TuniTransaction, Script: TuniScript, Qry_Tabelas: Tu- Frm_Principal: TFrm_Principal;
niQuery, Qry_Codigo: TuniQuery. Adicionamos também outros
implementation
três componentes: Mnu_Principal: TmainMenu, Imgl_Menu:
TimageList, ApeErro: TapplicationEvents. Com isso o nosso Uses Unt_CriaCadastro, Unt_ExcCadastro, Unt_Cadastro;
formulário principal fica pronto para ser programado. Após a
adição de todos os componentes, ele deverá ficar com a aparência
da Figura 2. Programaremos então os itens do menu principal, presente na
Figura 3. O primeiro item do menu, Criar Cadastro, é onde iremos
Nota
criar o formulário de criação de cadastros. Vamos chamarmos o
cadastro e após isso o liberamos, como mostra a Listagem 3.
Mais adiante vamos utilizar um TClientDataSet, então é importante adicionarmos MidasLib à seção
uses, após a interface. Com isso não é necessário distribuir o arquivo Midas.dll.
Na Listagem 7 quando criamos e adicionamos um menu, con- O tratamento de erros foi todo centralizado através compo-
figuramos que seu evento Click é implementado pela procedure nente TApplicationEvents. Nele colocamos mensagens perso-
MenuCadPerClick. Essa procedure cria o formulário do cadastro nalizadas para erros, como “is not a valid date”, “Input value”,
e passa para ele qual será o cadastro a ser criado, passando para “Insufficient memory for this operation”, etc. O componente
ele qual tabela a ser carregada. Toda manipulação do cadastro é TApplication-Events pode capturar os eventos da aplicação e
apresentada na Listagem 9. um desses eventos é o evento de exceção, ou seja, sempre que
uma exceção for levantada ela passará por esse evento, que
Listagem 8. Procedure RemoveMenu é onde realizamos toda a tratativa. Fazemos isso conforme a
procedure TFrm_Principal.RemoveMenu(Menu: String);
Listagem 10.
var A partir desse momento vamos tratar da criação das tabelas no
I: Integer; banco de dados. Os procedimentos a seguir são essenciais para
begin
for I := 0 to Mnu_Principal.Items.Count - 1 do
o projeto e embora possam parecer complexos, com a devida
begin atenção, não o são.
if AnsiSameCaption(Mnu_Principal.items[I].Caption, ‘C&adastros’) then Na seção private do formulário Frm_CriaCadatro temos oito
begin
MenuCad := Mnu_Principal.items[I]; procedures e quatro functions, como na Tabela 3, onde é possível
Break; identificar o nome e a finalidade de cada uma delas.
end;
end;
Montando o formuláro de criação de cadastros
for I := 0 to MenuCad.Count - 1 do Agora que já temos conhecimento das funções e procedi-
begin
mentos do formulário de criação de cadastro, iremos montar
if AnsiSameCaption(MenuCad.Items[I].Caption, Menu) then
begin o formulário, onde temos um TpageControl com duas abas. A
MenuCad.Remove(MenuCad.items[I]); primeira aba é utilizada para a criação do cadastro em si. Já a
Break;
segunda, para ensinar o usuário a fazer os seus cadastros.
end;
end; Nesta segunda aba temos três imagens. A primeira é um
end; exemplo de criação de um cadastro, a segunda, é a imagem
Listagem 9. Click dos Menus dos Cadastrados Criados desse cadastro em execução, ou seja, o resultado como ficaria
o cadastro criado a partir da primeira imagem, já a terceira
procedure TFrm_Principal.MenuCadPerClick(Sender: TObject); imagem seria o relatório desse cadastro. Veremos então esse
begin
try formulário com foco na primeira aba, a aba de criação, como
if not Assigned ( Frm_Cadastro ) then mostra a Figura 4.
Frm_Cadastro := TFrm_Cadastro.Create(Self );
Vamos agora aos nossos componentes. Temos um TPanel
Frm_Cadastro.Tabela := TMenuItem(Sender).Caption; com alinhamento allbottom. Neste nosso panel temos dois
Frm_Cadastro.ShowModal; TEdits (Edt_Tabela, Edt_Apelido), junto com dois TLabel que
finally
são os títulos dos dois edits. Temos também um TDBNavigator
FreeAndNil(Frm_Cadastro);
end; (Nvg_Setas) e temos dois TBitBtn (Btn_Criar, Btn_Sair).
end; Temos a nossa grade de criação, onde irão ser informados
os campos do cadastro. A nossa grade
TDBGrid (Grd_Cadastro) está alinhada
em toda a área da tela (allclient). E por fim
os três últimos componentes, um TMemo
(Mmo_Script) que é onde será montado o
script para a criação da tabela no banco de
dados, esse nosso memo está invisível, ou
seja, visible = false. Um TDataSource (Ds_
Dados), e um TClientDataSet (Dados), que
é onde terão os campos a serem informa-
dos para a criação do cadastro. Serão um
total de cinco campos, (NOME, COLUNA,
TIPOCAMPO, TAMANHO, RELATORIO),
todos do tipo TStringField.
Na segunda aba temos três imagens, mos-
trando um exemplo para a criação de um
Figura 4. Form de Criação de Cadastros cadastro, como vemos nas Figuras 5 a 7.
procedure InserirTabela; CODIGO: Campo do tipo inteiro, é a chave primária da nossa tabela, informamos o mesmo com a função
RetornaCodigo;
TABELA: Aqui é o nome da nossa tabela e não o seu apelido (Titulo);
CAMPO: O nome do campo (interno), e não o título que será mostrado na grade e no relatório;
COLUNA: É o título do campo, aquele que será mostrado na grade e no relatório;
TIPO: Qual é o tipo do campo (Texto, Numero, Data, Hora, Data e Hora, Observação, Moeda);
RELATORIO: Só aceita dois valores (S / N) para controlar se o campo deverá ou não aparecer no relatório.
Este procedimento remove os espaços em branco do nome da tabela e dos campos. E também remove os
procedure ArrumarNomes;
caracteres acentuados.
Aqui apenas fazemos algumas limpezas.
procedure LimpaGrade; Apagamos todos os dados no TclientDataSet (Dados). Limpamos o conteúdo do nosso script de criação
(Mmo_Script). E apagamos o nome da tabela e o seu apelido.
Esta função verifica algumas coisas para que não haja erro na hora de criarmos nossa tabela. A verificação
começa ao chamarmos o procedimento (ArrumarNomes), para que o script fique adequado. Em seguida é
function VerConsistencias: Boolean;
verificado se já não existe um cadastro com o mesmo nome, se foi informado os tipos dos campos, se foi
informado o tamanho do campo no caso se o tipo for texto, etc.
Esta função retorna um inteiro que selecionamos do nosso banco de dados, para que nossa chave primária
function RetornaCodigo(Generator: string): Integer;
seja um valor único.
function RemoveAcentos(Texto: String):String; Uma função que irá remover todos os acentos dos caracteres, retornando o texto sem os caracteres acentuados.
function RemoveEspaco(Texto: String ):String; Já esta função irá tirar os espaços em brancos, contidos nos textos.
e clicamos na propriedade Columns. Será apresentada uma janela procedure TFrm_CriaCadastro.Grd_CadastroKeydown(Sender: TObject;
que é a janela das colunas, então na parte superior desta clicamos var Key: Word; Shift: TShiftState);
no segundo botão (Add All Fields). Isso faz que os campos sejam begin
if (Shift =[ssctrl]) and (key = vk_delete) then abort;
listados nesta janela. if (key = vk_Up)then abort;
Selecionamos então o campo TIPOCAMPO e clicamos na pro- if (key = vk_down)then abort;
priedade PickList do mesmo. Feito isto será apresentada a janela if (key = vk_Cancel)then abort;
if (key = vk_Escape)then abort;
para informamos a lista que esse campo deve conter. No nosso if (key = vk_Insert)then abort;
caso serão os tipos dos campos, então basta informar os tipos end;
Então um a um é adicionado no nosso Mmo_Script, verificando correto e possa ser executado no banco de dados, é chamado o
se for campo do tipo texto, utiliza-se o procedimento Add_Texto, procedimento Replace_Campos.
se não for do tipo texto, utiliza-se o procedimento Add_Camps. Bom agora que estamos com o nosso script correto, então é ne-
Para finalizar, é removida a última vírgula que foi adicionada cessário criar a tabela. A Listagem 19 mostra o método CriaTabela,
após o último campo, fechando a sentença então com os caracteres ele irá jogar o conteúdo do Mmo_Script no componente de script
“‘);” e o AlteraTabela. Então para que o script fique totalmente do formulário principal (Script: TuniScript) e então executará o
mesmo, obviamente avisando se houve alguma falha.
Listagem 18. Procedure MontaScript
Listagem 19. Procedure CriaTabela
procedure TFrm_CriaCadastro.MontaScript;
var procedure TFrm_CriaCadastro.CriaTabela;
CriaTabela, begin
AlteraTabela, try
NomeTabela, Frm_Principal.Script.SQL.Text := Mmo_Script.Lines.Text;
UltLinha, Frm_Principal.Script.Execute;
Codigo: String; except
I, Virgula: Integer; on E:exception do
begin begin
try Screen.Cursor := crDefault;
NomeTabela := Edt_Tabela.Text;
Application.MessageBox(PAnsiChar(‘Erro Ao Criar Tabela:’ +#13+ E.message),
CriaTabela :=’CREATE TABLE ‘ + NomeTabela + ‘ (‘; ‘Business Inteligence’, MB_OK + MB_ICONERROR);
Exit;
Codigo := ‘CODIGO CHAR(6) not NULL,’; end;
end;
AlteraTabela :=’ALTER TABLE ‘+ NomeTabela + end;
‘ ADD CONSTRAINT PK_’ + NomeTabela + ‘ PRIMARY KEY (CODIGO);’;
Dados.First;
O procedimento InserirTabela mostrado na Listagem 20 é res-
Mmo_Script.Lines.Add(CriaTabela); ponsável por criar o cadastro em si. Aqui inserimos o nome do
Mmo_Script.Lines.Add(Codigo); cadastro junto com o seu apelido na tabela (TABELA_USUPER).
while not Dados.Eof do Por exemplo, Nome (CONSVET), Apelido (Consulta Veterinária).
begin Posteriormente são cadastrados todos os campos referente a esse
if (Dados.FieldByName(‘TIPOCAMPO’).AsString =’Texto’)then cadastro, percorrendo o conteúdo do TClientDataSet Dados.
Add_Texto
else A cada registro encontrado é realizada uma inserção na tabela
Add_Camps; TAB_CAMPOS. Os campos dessa nossa tabela são:
• CODIGO: campo do tipo inteiro, é a chave primária da tabela,
Dados.Next;
end; informamos o mesmo com a função RetornaCodigo);
• TABELA: é o nome da tabela e não o seu apelido (Titulo);
I := Mmo_Script.Lines.Count - 1;
UltLinha := Mmo_Script.Lines[I];
• CAMPO: o nome do campo (interno) e não o título que será
Virgula := Pos(‘,’, UltLinha); mostrado na grade e no relatório;
• COLUNA: aqui sim é o título do campo, aquele que será mos-
if Virgula > 0 then
begin
trado na grade e no relatório;
Delete(UltLinha, Virgula, Length(UltLinha)); • TIPO: indica o tipo do Campo, Texto, Numero, Data, Hora,
Insert(‘);’, UltLinha, Virgula); Data e Hora, Observação, Moeda;
Mmo_Script.Lines[I]:= UltLinha;
end; • RELATORIO: Só aceita dois valores (S/N) para controlar se o
campo deverá ou não aparecer no relatório.
Mmo_Script.Lines.Add(AlteraTabela);
Em seguida outras verificações necessárias são realizadas. Por Montando o formulário dos cadastros
exemplo, se já não existe um cadastro com o mesmo nome, se foi O formulário de cadastro possui sete componentes referente a
informado o tipo dos campos, se foi informado o tamanho dos parte de relatórios. São eles:
campos, no caso se o tipo for texto se foi informado se o campo irá • FrxPDF (TfrxPDFExport), usado para exportar o relatório para
ou não aparecer no relatório. o formato PDF;
0,05 e a propriedade Height para 2,76. Agora clicamos na primei- da tela AllBottom. Dentro deste Pnl_Botao foram colocados dois
ra coluna, onde está escrito Columm. Mudamos a propriedade TBitBtn (Btn_Excluir, Btn_Sair). Obviamente que o primeiro é para
HAlign para hacenter e a propriedade VAlign para vacenter. excluir um cadastro e o segundo é para fechar o formulário. E por
Colocamos sua fonte com o estilo Negrito e mudamos a sua cor último um TListBox (Lst_Cadastros) que por sua vez é alinhado
para cl3DLight, ou outra desejada. em todo o restante da tela (AllClient), nele é onde serão listados os
Agora clicamos na parte de baixo, onde está o 0 (zero). Mudamos cadastros existentes. Após a sua montagem, ele deverá ficar com
a propriedade color para clWhite, a sua propriedade HAlign para a aparência da Figura 13.
haLeft, na propriedade Frame no BottonLine mude a propriedade
color para clMenuText.
Banda do Rodapé
Aqui também é uma simples banda, ela informará o número
da página e o total de páginas. Clicamos novamente no botão de
inserir banda, escolhemos a banda Rodapé de Página, a nomeamos
para BdPgFoote). Agora inserimos um objeto Texto, nomeamos
para MmoLinhaFooter. Na propriedade Frame mudamos o Width
para 2, e seu Top para 0. Também alteramos a propriedade Width
para 2, em TopLine, RightLine, LeftLine, BottonLine mudamos a
propriedade Type para ftTop como true.
Agora inserimos um objeto (Texto do Sistema), na sua tela que
aparecerá marcamos a opção de Texto, que está na parte de baixo
da tela. Nela escrevemos o seguinte texto, logo em baixo na sua cai-
xa de texto: (Página [PAGE#] de [TOTALPAGES#]). Agora basta dar
ok e nomear o mesmo para SmmoPagina, mudamos também a sua
propriedade Top para 0,10 e o Left para 0. Ajustamos o seu tamanho
para que caiba todo o texto e aí finalizamos a montagem. Figura 13. Montagem do Form de Excluir Cadastros
Procedimentos e funções do fomulário dos cadastros Listagem 23. Seção Private e Public do Formulário
Na seção Uses declaramos o formulário principal e em seguida
private
declaramos uma constante, que será usada para o status do ca-
Apelido,
dastro, conforme mostra a Listagem 22. Nome,
Sql: String;
Frm: Tform;
Listagem 22. Adicionando Units ao Uses, e Declarando uma Constante MmoGrade: TMemo;
ListApelidos: TStringList;
unit Unt_Cadastro; OldStateCad: TDataSetState;
interface procedure MontaCadastro;
Uses procedure DataHoraText(Sender: TField; const Text: String);
Unt_Principal, DBAccess, Uni //etc procedure MemoText(Sender: TField; var Text: String; DisplayText:
Const Boolean);
dsEditModesStr: array [1..3] of String = (‘Consultando’, ‘Editando’, ‘Inserindo’); procedure FecharClick(Sender: TObject);
procedure ConfirmarClick(Sender: TObject);
function fZerosLeft(Str: String; Tam: Word): String;
Na seção private e public temos algumas variáveis, procedi- function fCodDefault(Qry: TUniQuery; Chave, Tab: String; nInc: Integer;
lZerosLeft: Boolean; Condicao: String = ‘’; Tabela: TDataSet = nil;
mentos e funções. São elas que irão montar o nosso cadastro Edit: TCustomEdit = nil): String;
e auxiliar em diversas outras rotinas. Olhamos com atenção { private declarations }
public
a seção public, nela está declarada uma variável Tabela. É o
Tabela: String;
formulário principal que irá passar para essa variável qual é o { public declarations }
cadastro escolhido e a ser montado. Vejamos como ficam essas end;
seções na Listagem 23 e sua implementação está disponível no
código fonte do artigo.
No evento Create do formulário selecionamos todos os campos
Programando o formulário de excluir cadastros da tabela TABELA_USUPER, que é onde ficam as informações
O formulário Frm_ExcCadastro é o mais simples todos os outros. dos cadastros criados. Percorremos o resultado da consulta e
São apenas quatro componentes e quatro procedures. Na sua adicionamos no TListBox o Cadastro (TABELA) e o seu apelido
montagem foi utilizado um TPanel (Pnl_Botao) alinhado em baixo (APELIDO), como mostra Listagem 24.
procedure TFrm_ExcCadastro.ExcTabela;
var Params.ParamByName(‘PTABELA’).AsString := Tabela;
Tabela, Execute;
TApelido: String; end;
begin
try with Frm_Principal.Qry_Tabelas do
TApelido := Lst_Cadastros.Items.Strings[Lst_Cadastros.ItemIndex]; begin
Tabela := Copy(TApelido, 0, Pos(‘-’, TApelido )-1); Close;
SQL.Clear;
Screen.Cursor := crSQLWait; SQL.Add(‘DROP TABLE ‘+ Tabela);
Execute;
with Frm_Principal.Qry_Tabelas do end;
begin
Close; Frm_Principal.RemoveMenu(TApelido);
SQL.Clear; Lst_Cadastros.Items.Delete(Lst_Cadastros.ItemIndex);
SQL.Add(‘DELETE FROM TAB_CAMPOS TC ‘); Screen.Cursor := crDefault;
SQL.Add(‘WHERE (TC.TABELA = :PTABELA)’);
except
Params.ParamByName(‘PTABELA’).AsString := Tabela; on E:exception do
Execute; begin
end; Screen.Cursor := crDefault;
Nela, repare que temos duas variáveis (Tabela, TApelido), que Autor
servem para pegar o nome e o apelido da tabela a ser excluída. Vanderson Cavalcante de Freitas
Em seguida apagamos os campos pertencentes a essa tabela, que vanderson.freitas@ig.com.br
estão na tabela TAB_CAMPOS. Logo após apagamos o cadastro Analista Desenvolvedor Delphi há mais de 5 anos, com experi-
desta tabela, que se encontra na tabela TABELA_USUPER. Feito ência em médias e grandes empresas de São Paulo. Formado
isso apagamos essa tabela com o comando Drop Table Agora em técnico em informática no ano de 2003, com diversos cursos em
sim no nosso banco de dados não há mais nada referente a esse formação específica, como Oracle, Delphi e C#.
cadastro.
Na sequência chamamos a procedure RemoveMenu, passando Você gostou deste artigo?
como parâmetro a variável apelido, e tiramos o cadastro desta
lista e do menu. Dê seu voto em www.devmedia.com.br/clubedelphi/feedback
Permitir que um usuário possa criar cadastros simples e seus
Ajude-nos a manter a qualidade da revista!
respectivos relatórios é uma funcionalidade que garante flexi-
bilidade e pode até mesmo ser um diferencial comercial para
qualquer produto.