Você está na página 1de 257

Delphi 4.

0 Total
Esta apostila é melhor visualizada com resolução de 800x600 e no mínimo 256 cores.

© Direitos Autorais 1997, 1998 para Griaule


(razão social: I.A. Pesquisa & Desenvolvimento de Sistemas Ltda.)
Nenhuma parte desta publicação poderá ser reproduzida ou transmitida, no todo ou em parte, em
qualquer forma ou meio, seja eletrônica ou mecânica, seja qual for o propósito, sem a expressa
autorização por escrito da Griaule.
Delphi, Borland, ReportSmith, dBASE e InterBase são marcas registradas da Borland International, Inc.
Microsoft, MS, Windows, Windows 95, Microsoft SQL Server são marcas comerciais ou marcas
registradas da Microsoft Corporation.
Paradox é uma marca registrada da Ansa Software, uma empresa da Borland.
TrueType é uma marca registrada da Apple Corporation.
Oracle é uma marca registrada da Oracle Corporation.
Todas as outras marcas e nomes de produtos são de propriedade de seus respectivos portadores.
Conteúdo:
Márcio Pontes
Marla C. Ávila
Conversão para mídia eletrônica: Renato de A. Martins
Capítulo 1
Iniciando o Delphi

Princípios da Programação Windows

Iniciando o Delphi

Criando um Primeiro Programa


Princípios da Programação Windows
Antes de começar a trabalhar com o Delphi, é importante ter algumas noções do que está envolvido na
programação Windows e no Delphi em particular. Algumas coisas tornam a tarefa de programação no
Windows bem diferente de outros ambientes:
Independência do Hardware:No Windows, o acesso aos dispositivos de hardware é feito com intermédio
de drivers fornecidos pelo fabricante de hardware, o que evita que o programador tenha que se preocupar
com detalhes específicos do hardware.
Configuração Padrão: O Windows armazena centralmente as configurações de formato de números,
moeda, datas e horas, além da configuração de cores, livrando o programador de se preocupar com esses
detalhes específicos.
Multitarefa: No DOS, um programa geralmente toma o controle da máquina só para si, e outros
programas não rodam enquanto isso. Já no Windows vários programas são executados de maneira
simultânea e não há como evitar isso.
Controle da Tela: No DOS geralmente um programa ocupa todo o espaço de telas, e o usuário vê e
interage apenas com aquele programa. Já no Windows [janelas], todas informações mostradas e todas
entradas recebidas do usuário são feitas por meio de uma janela, é uma área separada da tela que pode
ser sobreposta por outras janelas (de outros programas por exemplo).
Padrões de Interface: No Windows, todos os elementos de interface aparecem para o usuário e interagem
da mesma forma. Além disso, existem padrões definidos pela Microsoft que são recomendados para
conseguir a consistência entre aplicativos. Falaremos de alguns deles no curso, mas a melhor forma de
aprendê-los é praticar com os aplicativos Windows mais usados do mercado.
Cooperação com o Sistema: Num programa DOS, a execução segue uma ordem prestabelecida pelo
programador, e o programa só chama o sistema operacional quando precisa de alguma coisa dele. Já no
Windows, o programa deve responder a eventos, que são ativados pelo Windows quando alguma coisa
acontece externamente (ou seja, uma ação do usuário, ou uma aviso do sistema). Isso, como veremos,
afeta radicalmente o estilo de programação e a forma de pensar no programa. A sequência de execução
no programa. A sequência de execução do programa depende da sequencia de eventos.
Iniciando o Delphi

Inicie o Delphi através do seu ícone no Windows, se você já tiver um atalho para o programa na sua
área de trabalho. Senão, clique no menu do Windows e depois em Programas|Borland Delphi
4|Delphi 4.
A tela básica do Delphi tem os seguintes elementos:

Ao pressionar F12 irá aparecer a seguinte tela:


Nota: para criar um atalho para o Delphi, consulte a documentação do Windows. Localize o arquivo
executável DELPHI32.EXE sob o diretório de instalação.
• Formulário (Form1): Um formulário é a janela do seu programa, o local onde você projeta a sua
interface com o usuário. Durante o projeto, o formulário aparece de forma praticamente idêntica ao que o
usuário final verá, mas permite que você faça alterações no posicionamento dos componentes (elementos
de tela) e em suas propriedades (características);
• Speedbar: Uma barra de botões na janela principal do Delphi, que contém botões para executar os
comandos mais usados do Delphi;
• Paleta de Componentes: Também na janela principal, é dividida em várias páginas, onde cada uma
contém vários tipos de componentes que você pode escolher para colocar no formulário;
• Object Inspector: [inspetor de objeto]: Dividido em duas páginas, onde uma contém as propriedades do
componente selecionado, e a outra uma lista de eventos associados a este componente, que define as ações
externas as quais você pode tratar;
• Menu Principal: Contém os comandos do ambiente de programação do Delphi, por exemplo, para
tratamento de arquivos, definição de opções, etc.
• Code Editor: (Aparece atrás do formulário, inicialmente). Contém o código-fonte do projeto. Com ele,
você pode facilmente mudar entre os vários arquivos utilizados no projeto ou mesmo abrir um arquivo de
texto qualquer.
Criando um Primeiro Programa
No Delphi você inicia o projeto da sua aplicação escolhendo componentes, que são os objetos de
interface com o usuário e alterando as características desses componentes como por exemplo, cor,
tamanho, fonte de caracteres, através das propriedades. Por último, você decide quais eventos você quer
tratar e escreve o código de programa que é executado por esses eventos.
Nosso primeiro programa irá mostrar uma lista de nomes e permitirá ao usuário adicionar itens na lista,
limpar a lista e ordenar a lista em ordem alfabética. O programa terá uma interface como a seguir, com
uma lista de itens, um quadro de texto que permite digitar o próximo item da lista, e botões para
manipular a lista:

Para criá-lo, precisamos alterar propriedades do formulário e colocar componentes em cima dele.

Propriedades do Formulário
Propriedades do formulário são as características que definem sua aparência, e algumas vezes, seu
comportamento. O Object Inspector lista todas as propriedades do formulário, que são inicializadas com
valores padrão quando o projeto é criado. Por exemplo, o título que aparece no formulário (inicialmente
Form1) é uma propriedade que você pode alterar no Object Inspector.

Selecione a propriedade Caption no Object Inspector. Clique na coluna da direita (que contém o
valor da propriedade) e digite o texto Primeiro Programa Delphi. À medida que você digita, o
formulário reflete essa alteração na sua barra de título.

Colocando Componentes
Um formulário vazio não é muito útil, portanto vamos colocar componentes nesse formulário. A Paleta
de Componentes contém vários ícones, onde cada um representa um tipo de componente que você pode
colocar no formulário. Ela também é dividida em páginas, que podem ser acessadas clicando no
marcador de página correspondente. Quando você passa o cursor do mouse por cima de um ícone, ela
mostra o nome daquele componente.
Clique na página Standard e depois clique no ícone (Edit). Agora clique no formulário. Vai
aparecer um quadro de edição chamado "Edit1". Depois iremos alterar esse nome padrão. Para mudá-lo
de posição no formulário, posicione o cursor em cima dele, pressione e segure o botão do mouse, e
arraste-o para outra posição, ao arrastar o componente será mostrado as coordenadas (x, y) indicando a
posição do componente no formulário . Ou, se preferir, usando o teclado, segure a tecla [Ctrl] e use as
teclas de seta para movimentá-lo.
Agora precisamos de um quadro de lista. Clique no ícone (ListBox) da paleta de
componentes, na mesma página Standard, depois clique no formulário, um pouco abaixo do quadro de
texto (não se preocupe com alinhamento). Para aumentar seu tamanho lateral, clique na alça mais à
direita, segure o botão do mouse e arraste-o para a direita. Ou, usando o teclado, segure [Shift] e
pressione a seta para a direita.
Vamos colocar três botões no formulário. Como são vários controles do mesmo tipo, podemos
fazer o seguinte: segure [Shift] e clique no ícone (Button) da paleta de componentes. Agora clique
três vezes no formulário, colocando um botão abaixo do outro. Não se preocupe com o alinhamento por
enquanto Depois clique no ícone do ponteiro ( ) . O formulário deve ficar parecido com o seguinte:

Clique no botão (Run) da SpeedBar. O Delphi irá compilar o programa, gerando um arquivo
executável e vai iniciar sua execução. Durante a execução, você pode digitar texto no quadro de texto, e
clicar nos botões. Depois tecle [Alt+F4] para terminar o programa (ou feche a janela com o mouse).
Repare que o Delphi cuida de toda a parte de interface com o usuário. Você não precisa escrever código
para mostrar uma janela na tela nem seus componentes. Mas para definir o que o programa vai fazer, isto
é, como ele reage aos eventos externos, você precisa escrever código.

Alterando as Propriedades dos Componentes


Para mudar a aparência dos componentes, alteraremos suas propriedades. É importante notar que o
Object Inspector disponibiliza as propriedades do componente que estiver selecionado, ou seja, com as
alças de marcação. Quando você clica em um componente com o mouse, ele é selecionado e você pode
alterar suas propriedades. Quando você clica numa região vazia do formulário (ou pressiona [Esc]), você
pode alterar as propriedades do formulário.
Selecione o quadro de texto e depois clique na propriedade Text no Object Inspector. Apague o
valor atual (que é Edit1). Assim esse componente não vai mais começar mostrando "Edit1" no seu
conteúdo.
Agora clique no primeiro botão (Button1) e altere sua propriedade Caption para Adicionar. Isso define o
texto do botão.
Clique no segundo botão (Button2) e altere Caption para "Ordenar" e no terceiro botão, altere Caption
para "Limpar".

Respondendo a Eventos
Todo o código no Delphi é executado, direta ou indiretamente, em resposta a eventos. Quando você (ou o
usuário) clica em um botão por exemplo, isso provoca um evento. Se existir um trecho de programa
associado a esse evento (um procedimento de evento), esse trecho será executado. Senão, o evento não
vai fazer nada.
Clique no botão Adicionar para selecioná-lo. No Object Inspector, clique no marcador de página
"Events". Essa página lista quais eventos são reconhecidos pelo componente. O primeiro deles, OnClick,
acontece quanto o botão recebe um clique do mouse. Clique duas vezes na coluna da direita, e o Delphi
vai abrir o editor de código, contendo o seguinte texto:
procedure TForm1.Button1Click(Sender: TObject);
begin
end;
O cursor está posicionado entre os delimitadores begin e end, indicando onde você deve escrever
o código, que será executado ao clicar no botão Adicionar. Escreva o seguinte (diferenças entre
maiúsculas e minúsculas não importam):
ListBox1.Items.Add(Edit1.Text);
Edit1.Clear;
Pressione a tecla [F12] para voltar ao formulário. Clique no botão Ordenar e depois clique duas
vezes no valor do evento OnClick. Isso vai gerar um tratador de evento para o botão Ordenar. Escreva o
seguinte entre o begin e o end:
ListBox1.Sorted := True;
Finalmente retorne ao formulário, selecione o botão Limpar e siga o mesmo processo. Digite o
seguinte código:
ListBox1.Clear;
ListBox1.Sorted := False;
Testando o Programa
Agora execute o programa clicando no botão . Você pode digitar um valor no quadro de texto,
e clicar no botão Adicionar (ou teclar [Enter]). O item digitado será acrescentado à lista. Depois de
acrescentar alguns itens, clique em Ordenar. A lista será colocada em ordem alfabética. Para esvaziar a
lista, clique em Limpar.

Salvando o Programa
Para que você possa usar esse programa mais tarde, clique em File|Save All (menu File, item Save
All) ou no botão da SpeedBar. O Delphi vai pedir um nome para o arquivo do formulário. Digite
AULA1 e clique Ok. Depois o Delphi vai perguntar o nome do arquivo de projeto. Digite AULA1P e
clique Ok (não use o mesmo nome que o formulário).
Nota: Você também pode clicar no formulário ou no editor de código com o mouse, mas geralmente o
método mais rápido de alternar entre os dois é usar a tecla [F12].
Capítulo 2
Noções Básicas

Ambiente de Programação

Eventos e Procedimentos

Componentes

Propriedades

Métodos

Object Pascal e Bibliotecas

VCL- Visual Component Library

Objetos

Resumo

Projetos

Grupos de Projetos

Edições do Delphi
Ambiente de Programação
O Delphi possui um ambiente integrado de desenvolvimento (IDE - Integrated Development
Environment) onde você utiliza a maioria das funções de desenvolvimento. Nesse ambiente você projeta
o programa e depois executa o programa para testar o seu funcionamento.
Sempre que você executa o programa, o Delphi compila o programa, isto é, gera um programa executável
com código de máquina a partir do programa fonte que você criou. Esses programas executáveis se
tornam independentes do Delphi e podem rodar separadamente.

Programação Orientada a Objeto (POO)


Para compreendermos melhor ambiente de desenvolvimento do Delphi é necessário que você entenda os
conceitos de Programação Orientada a Objetos(POO).
A POO é um forma de gerenciar a complexidade do programa, de forma que facilite a manutenção e a
reutilizacão de partes do programa. O programa é dividido em partes relativamente isoladas que podem
ser alteradas de forma independente e podem ser reutilizadas mais facilmente em outros programas.Estas
partes são chamadas de Classes (Iremos discutir em Objetos).
Eventos e Procedimentos
Programas DOS convencionais seguem um fluxo seqüencial, em que um comando segue o outro de
forma definida pelo programador. O usuário deve agir de acordo com os "modos" de operação do
programa e saber o momento certo para entrar com comandos.
Programas em uma interface multitarefa, como o Windows, apresentam todas as opções ao usuário, para
que ele possa selecioná-los na ordem em que desejar. O Windows intercepta as ações realizadas pelo
usuário e informa ao programa onde, como e quando elas acontecem. O programa deve reagir de forma
apropriada nos momentos determinados pelo usuário. Cada resultado de uma ação do usuário é chamada
de evento, por exemplo: clique do mouse, digitação de uma tecla, etc.
Como conseqüência, o programa não é mais um bloco único, mas é dividido em pequenas seções, cada
uma responsável pela tarefa a realizar em resposta a um determinado evento. Essas seções são os
procedimentos de evento do Delphi.
Cada evento é associado a um componente e cada componente tem uma lista de eventos que são
acionados por ele em situações específicas (o Object Inspector mostra esta lista na página Events). Com
isso, o projeto de um programa é feito iniciando pelo projeto de sua interface (seus componentes) e
depois escrevendo-se os procedimentos de evento. Não é obrigatório definir todos os eventos para um
componente. Se não existe um procedimento para um evento específico, simplesmente não acontece nada
para aquele evento.
Como veremos mais tarde, além dos procedimentos de evento, você pode dividir o seu programa, usando
rotinas auxiliares.
Nota: para melhor utilização do ambiente de programação e da linguagem, consulte o help do Delphi.
Os procedimentos de evento são armazenados na unidade, que é um arquivo de texto associado ao
formulário. Você pode editar todas as unidades no editor de código do Delphi.
Componentes

A paleta de componentes lista tipos de componentes que podem ser utilizados no programa, como botões
de pressionar, barras de rolagem, grades de dados e editores de texto. Para criar um componente, você
seleciona o ícone correspondente e depois clica no formulário para colocá-lo. Você também pode criá-lo
já com o tamanho desejado, pressionando o botão do mouse sobre o formulário e arrastando para definir
o tamanho.
Nota: todos os componentes são descritos em detalhe no Help do Delphi, com todas suas propriedades,
métodos e eventos. Clique em um ícone de componente e pressione F1 para ver o texto de help sobre ele.
Note que há mais páginas do que cabe na tela em dado momento. Para ver as páginas restantes, como
Dialogs, Midas, Win 3.1, Samples e ActiveX, clique nos botões que ficam na extremidade direita
da janela do Delphi.
Alguns componentes não aparecem para o usuário durante a execução do programa, apesar de aparecer
no formulário, e serem manipulados pelo código do programa. São os componentes não-visuais (veremos
alguns deles no decorrer do curso). Mas a maioria aparece como um objeto de interface durante a
execução. São os controles (ou componentes visuais). É importante lembrar que todo controle é um
componente, mas nem todo componente é um controle.
O próprio formulário também é considerado um componente, embora seja tratado de forma diferente, e
geralmente contém vários outros componentes.

Visão Geral dos Componentes


A paleta de componentes se divide em várias páginas para facilitar a organização. Nós veremos alguns
deles no curso, mas é importante ter uma noção geral do conteúdo dessas páginas.
Standard: componentes padrão da interface do Windows, usados para barras de menu, exibição de texto,
edição de texto, seleção de opções, iniciar ações de programa, exibir listas de itens etc. Geralmente são os
mais usados.
Additional: componentes especializados que complementam os da página Standard. Contém botões com
capacidades adicionais, componentes para exibição e edição de tabelas, exibição de imagens, gráficos etc.
Win32: componentes comuns de interface que são fornecidos pelo Windows 95/NT para os programas.
Contém componentes para dividir um formulário em páginas, edição de texto formatado, barras de
progresso, exibição de animações, exibição de dados em árvore ou em forma de ícones, barras de status e
de ferramentas etc.
System: componentes que utilizam funções avançadas do sistema operacional, como temporização,
multimídia, OLE e DDE.
Internet: componentes para acesso aos recursos e protocolos da Internet, como criação de páginas Web
dinâmicas, acesso aos protocolos FTP, NNTP, POP, SMTP, TCP, UDP.
Data Access: componentes para acesso a banco de dados.
Data Controls: componentes visuais para mostrar dados em aplicações de bancos de dados. São
semelhantes aos componentes padrão (Standard).
Decision Cube (apenas Delphi Client/Server): componentes para análise multidimensional de dados, com
capacidades de tabulação cruzada [crosstab], criação de tabelas e gráficos etc.
QReport: QuickReport é um gerador de relatórios que acompanha o Delphi. Os componentes desta
página permitem desenhar o seu relatório dentro do Delphi.
Dialogs: O Windows tem caixas de diálogo comuns, como veremos, que
facilitam mostrar uma interface padrão dentro do seu programa para as tarefas comuns, como abrir e
salvar arquivos, impressão, configuração de cores e fontes etc. Esta página tem componentes que
permitem utilizar essas caixas de diálogo comuns.
Midas (apenas Delphi Client/Server e Enterprise) : componentes utilizados para criar aplicações
multi-tiered.
Win3.1: Esta página contém controles considerados obsoletos, que estão disponíveis apenas para
compatibilidade com programas antigos. Não crie programas novos que utilizam esses controles.
Samples: contém exemplos de componentes para que você possa estudá-los e aprender a criar seus
próprios componentes. O código fonte desses exemplos está no subdiretório SOURCE\SAMPLES do
diretório de instalação do Delphi.
ActiveX: um componente ActiveX é um tipo de componente que pode ser criado em outra linguagem e
utilizado no Delphi. Esta página contém alguns exemplos de componentes ActiveX prontos para utilizar,
que têm funções de gráficos, planilha, etc.
Propriedades
Componentes têm propriedades [properties], que definem suas características específicas e eventos, que
são acontecimentos externos, geralmente relacionados com ações do usuário. Por exemplo o formulário
tem como propriedades o título da janela (Caption), sua cor (Color), sua posição na tela (Left e Top).
Um quadro de texto tem uma propriedade que define o texto que este contém (Text), entre outras.
Quando o usuário clica com o mouse em um controle, isso causa um evento OnClick, e quando ele
pressiona uma tecla, causa um evento OnKeyDown (entre outros).
Para alterar propriedades durante o projeto da interface, você seleciona o componente desejado (por
exemplo Edit1), depois seleciona a propriedade desejada (por exemplo, Text) no Object Inspector e
digita seu valor. Durante a execução do programa, o código pode alterar uma propriedade dinamicamente
(em resposta a um evento). Para isso, basta inserir no código de programa um comando como o seguinte:
Edit1.Text := 'Bem-vindo ao Delphi';
Nota: se você clicar em uma propriedade e teclar [F1], o Delphi mostrará o texto de help relacionado
àquela propriedade.
Métodos
Métodos são nomes para ações executadas por um componente. No programa anterior, por exemplo,
usamos o método Clear, que quando executado, limpa o conteúdo de um controle. Por exemplo, no
programa anterior, o procedimento de evento do botão Adicionar faz:
Edit1.Clear;
Quando esse comando é executado, o controle é esvaziado. Repare que todo método é chamado
usando-se primeiro o nome do componente, depois o nome do método, da mesma forma que uma
propriedade. Métodos só tem utilidade no código do programa, por isso não aparecem no Object
Inspector (mas veremos mais tarde que é possível consultar a lista de métodos disponíveis).
Object Pascal e Bibliotecas
Todo código de programa no Delphi é escrito em Object Pascal, que é a linguagem de programação
usada por ele. Essa linguagem é derivada da linguagem Pascal original, e tem sido aperfeiçoada pela
Borland por vários anos, nas diversas versões do Turbo Pascal.
A definição da linguagem contém as regras de sintaxe e os tipos de dados básicos que você pode usar. A
maioria das rotinas úteis do Delphi não fazem parte da linguagem em si, mas são implementados pela
biblioteca de tempo de execução [run-time library] do Delphi.
VCL - Visual Component Library
Os componentes, suas propriedades, eventos e métodos são implementados pela biblioteca visual de
componentes [Visual Component Library], também chamada VCL. O código fonte da VCL é fornecido
com o Delphi e permite você modificar os componentes ou estudar como eles foram feitos.
Você também pode criar novos componentes, escrevendo-os em Object Pascal. O próprio Delphi foi
criado em Delphi, utilizando-se a linguagem Object Pascal e versões sucessivas do ambiente.
Nota: se você digitar apenas o nome do componente e um ponto, o CodeInsight do Delphi mostrará a
lista das propriedades e métodos disponíveis. Ao continuar digitando, é feita uma busca incremental
nessa lista.
Nota: para obter ajuda sobre qualquer elemento do Object Pascal ou da VCL, clique na palavra desejada,
no editor de código e pressione a tecla [F1].
Objetos
Componentes são apenas um tipo de objetos, que é um termo bastante utilizado no Delphi. O próprio
nome Object Pascal indica que se trata de uma linguagem orientada a objetos.
Um objeto, numa definição prática, é uma área de memória separada, contendo dados (variáveis) e o
código que manipula esses dados. No caso de um componente, os dados aparecem como propriedades e o
código que os manipula é composto de métodos. Basicamente todo objeto contém propriedades e
métodos, mesmo que ele não possa ser colocado num formulário e editado. Mais tarde veremos vários
exemplos de objetos que não são componentes.
Resumo
Para dominar a programação em Delphi, é preciso conhecer os componentes da VCL, os objetos das
bibliotecas do Delphi e a linguagem Object Pascal, que é o fundamento no qual tudo se baseia.
Projetos
O Delphi trata cada aplicativo que você cria como um projeto, que é uma lista dos arquivos necessários
para construir um programa. Para saber quais são os formulários, as unidades correspondentes, etc., o
Delphi nos fornece o Project Manager, que pode ser acessado através do Menu View, opção Project
Manager. A janela de título do Delphi sempre tem, no título, o nome Delphi - Nome do projeto. Um
projeto em Delphi se compõe de:
· Formulários: um formulário é uma janela que projetamos no Delphi. Normalmente toda aplicação
contém um ou mais formulários. Cada formulário tem uma unidade associada, que é um arquivo
contendo todo o código associado a eventos;
· Unidades independentes: uma unidade independente contém código do Delphi que não está
necessariamente associado a nenhum formulário ou controle em particular. Unidades independentes
geralmente contém variáveis e rotinas usadas por toda aplicação.
Lembre-se que um projeto é apenas uma lista dos arquivos usados para construir o programa, mas não os
contém propriamente.

Salvando o Projeto em Disco


Ao gravar o projeto em disco, o Delphi cria vários arquivos: dois para o projeto em si, e outros para cada
formulário e cada unidade independente usada. Os arquivos têm as seguintes extensões:
Projeto: .DPR e .OPT (opções)
Formulários: .DFM e .PAS (unidade associada)
Unidades independentes:.PAS

Além disso, o Delphi gera outros arquivos no diretório do projeto, a maioria deles quando você compila
o programa (por exemplo, automaticamente ao executar):
Unidade compilada: nome-da-unit.DCU
Arquivo de recursos: nome-do-projeto.RES
Programa compilado:nome-do-projeto.EXE
Arquivos de backup: *.~PA, *.~DF, *.~DP

Para salvar o projeto, juntamente com seus arquivos, use o item de menu File|Save All do Delphi. Da
primeira vez que você salva o projeto, o Delphi pede o nome do arquivo de formulário (o padrão é
"Unit1.PAS") e o nome do arquivo de projeto (o padrão é "Project1.dpr"). Não é necessário digitar as
extensões, pois o Delphi cuida de acrescentá-las.
Quando você salva um arquivo de formulário, o Delphi salva dois arquivos: a unidade associada ao
arquivo [unit], com uma extensão .PAS, que é um arquivo contendo todo o código de programa
associado aos eventos, bem como todo o código gerado pelo Delphi, e outro arquivo contendo as
propriedades do formulário e de todos os componentes, com o mesmo nome de arquivo, mas com a
extensão .DFM. O arquivo de projeto é um arquivo .DPR, que corresponde ao código principal do
programa executável. A partir desse arquivo, o Delphi localiza os outros arquivos necessários.
Nota: o nome do arquivo de projeto deve ser diferente de qualquer unidade do projeto.
Comandos para trabalhar com Projetos
Vários comandos do menu File do Delphi são usados para trabalhar com os projetos e os arquivos que
fazem parte deles:
New Application Cria um novo projeto vazio
New Form Cria um novo formulário e sua unidade
Open... Abre um projeto ou unidade existente.
Reopen Mostra os arquivos utilizados recentemente e permite reabrir qualquer um deles.
Salva o arquivo atual (unidade). Se esta é a primeira vez que está sendo salvo,
Save
pergunta pelo nome.
Save As... Salva uma cópia do arquivo atual com outro nome ou em outro diretório.
Save Project As...Salva o arquivo de projeto. Sempre pede o nome.
Salva todos os arquivos do projeto e o próprio arquivo de projeto. Só pede nomes de
Save All
arquivos para os que não foram salvos ainda.
Close Fecha o arquivo atual.
Close All Fecha todos os arquivos e o projeto atual.

Além desses comandos, existem ícones na SpeedBar que podem ser usados para essas tarefas:

(Open project) Equivale a File|Open, mas mostra apenas os arquivos de projeto.

(Save all) O mesmo que File|Save all

(Open Project) O mesmo que File|Open Project...

(Save ) O mesmo que File|Save

(Add file to project) Adiciona um arquivo ao projeto atual.

(Remove file from project)Remove um arquivo do projeto atual.


Grupos de Projetos
O delphi 4.0 permite trabalhar com mais de um projeto aberto, esses projetos podem ser definidos em um
grupo. No menu View|Project Manager é informado os projetos que pertencem a um determinado
grupo.O arquivo de um grupo tem a extensão ".BPG"

Comandos para tabalhar com grupos de projetos


Vários comandos do menu Project são usados para trabalhar com grupos de projetos , os principais são:
Add New Project Adiciona um novo projeto ao grupo.
Add Existing Project Adiciona um projeto existente ao grupo
Compile (Projeto Atual)Compila somente as units que foram modificadas, do projeto ativo.
Compila todas as units do projeto ativo, independente se foram ou não
Build ( Projeto Atual)
modificadas.
Compila somente as units que foram modificadas, dos projetos pertencentes ao
Compile All Projects
grupo.
Compila todas as units dos projetos pertencentes ao grupo, independente se
Build All Projects
foram ou não modificadas.
Edições do Delphi
O Delphi está atualmente na versão 4. Essa versão é geralmente compatível com programas
desenvolvidos nas versões anteriores, embora alguns necessitem de modificação para funcionar,
especialmente os feitos para a versão 1.0.
É importante saber também que existem várias edições diferentes do Delphi, desde a Standard (a mais
simples), passando pela Professional, que tem mais recursos, até a edição Client/Server Suite, mais
sofisticada e com recursos úteis para grandes empresas:
Standard: muito limitada em recursos. Indicada apenas para quem tem interesse em desenvolvimento de
pequenos programas ou para fins de aprendizado.
Professional: inclui mais recursos que a edição Standard. Recomendada para desenvolvedores
profissionais.
Client/Server : inclui mais recursos que a Professional, especificamente voltados para grandes
desenvolvedores que utilizam bancos de dados no padrão cliente/servidor.
Note que o Delphi 4 só funciona para ambientes de 32 bits, ou seja, Windows 95/98/etc. e Windows NT.
Se você quiser desenvolver programas compatíveis com Windows 3.x, você deve utilizar a versão 1.0.
Capítulo 3
Exemplos Adicionais

Exemplo: Calculadora
Exemplo: Calculadora
O nosso objetivo é criar uma calculadora simples, que apenas soma dois números fornecidos e mostra o
resultado, semelhante à figura:

Criando os Componentes
Para começar, inicie o Delphi ou, se o Delphi estiver aberto, crie um novo projeto com File |New
Application e altere a propriedade Caption da janela principal (Form1) para "Calculadora". Agora grave
o projeto usando File | Save All no menu do Delphi. Dê os nomes de CALC ao formulário e CALCP ao
projeto.
Isso vai salvar três arquivos em disco, como já vimos:
CALC.PAS
A unidade associada ao formulário
CALC.DFM
Contém a definição do formulário e seus controles.
CALCP.DPR
O arquivo de projeto, que lista os nomes dos outros.
Dê dois cliques no ícone Edit para criar um quadro de edição no formulário. Mova-o até o
topo do formulário e altere a sua propriedade Name, para "editOperando1". A propriedade Name
determina o nome que será usado no programa para manipular esse componente, que será usado para
receber o primeiro operando digitado.
Repita as mesmas ações para criar outro quadro de texto para o segundo operando. Ele deverá ficar
logo abaixo do primeiro. Dê o nome "editOperando2".

Agora crie um botão de comando, com um clique duplo na ferramenta . Altere o seu tamanho
para ficar igual ao dos quadros de texto.
Nota: Quando você cria um componente, ele recebe um nome default como Edit1, Button1, etc. É
recomendável dar um nome mais descritivo para que seja mais fácil entender o programa mais tarde.
Nota: No curso adotaremos um padrão para nomes de componentes: um prefixo de três ou quatro letras
que diz o tipo de componente (edit para Edit, btn para Button, etc.) seguido de um nome descritivo (no
caso, Operando1). Veremos que esse padrão facilita a compreensão dos programas.

Altere sua propriedade Caption para "=" (sinal de igual). Altere Name para "btnCalcula".
Finalmente crie um quadro de texto, posicionando-o abaixo do botão de comando, que vai conter o
resultado. Dê o nome de "editResultado".
Agora, para mostrar o sinal de "+" vamos usar um componente Label (rótulo), que apenas mostra
um texto para o usuário, não permitindo alteração. Selecione o ícone e desenhe um rótulo à
esquerda do editOperando2, como na figura. Altere sua propriedade Caption para conter um sinal de +.
Para fazer com que esse sinal fique maior, selecione a propriedade Font, que corresponde às
características do texto, e clique duas vezes do seu lado direito. Selecione "MS Sans Serif" na lista de
fontes e um tamanho maior na lista "Tamanho". Depois clique Ok.

Tratando os Eventos
A interface da calculadora está pronta, mas ela ainda não executa a função desejada. Precisamos
acrescentar código que leia os dois operandos, some os seus valores e coloque o resultado no último
quadro de texto.
Clique duas vezes no botão 'btnCalcula', no formulário. Isso vai criar um tratador de eventos para o
evento padrão do botão, que é o evento OnClick. O Delphi vai gerar o seguinte código:
procedure TForm1.btnCalculaClick(Sender: TObject);
begin
end;
Para fazer o cálculo precisaremos de variáveis. Uma variável é uma área de memória que tem um nome e
armazena um determinado valor. No Delphi, toda variável também tem um tipo, que determina quais os
valores que ela pode conter. Depois veremos todos os tipos de dados em detalhes, mas por enquanto,
usaremos o tipo de dados 'double', que permite armazenar valores numéricos com parte fracionária, com
a precisão de 15 a 16 dígitos significativos.
Nota: Você pode também selecionar o evento no Object Inspector e criar o tratador a partir do nome
dele, mas para o evento padrão, é mais rápido um clique duplo no próprio componente.
Variáveis são declaradas com a palavra var, informando-se o nome e o tipo das variáveis. Antes da
palavra reservada begin, acrescente o seguinte:
var
op1, op2: double;
res: double;
Isso declara três variáveis: 'op1', 'op2' e 'res', todas do tipo double. Repare que duas variáveis do mesmo
tipo podem ser declaradas separando-as por vírgula antes do tipo ou em uma declaração separada.Não é
necessário repetir o var para várias declarações, pois elas terminam quando se encontra o begin.
Agora vamos utilizar essas variáveis para obter os valores digitados pelo usuário. Para ler o valor
digitado em um controle Edit, devemos ler a propriedade Text, só que essa propriedade tem um valor
alfanumérico (pode conter letras e números), logo precisamos convertê-la para numérico. Isso é feito
com a função StrToFloat:
op1 := StrToFloat(editOperando1.Text);
op2 := StrToFloat(editOperando2.Text);

Depois basta somar as variáveis e mostrar o resultado, utilizando novamente uma função de
conversão:
res := op1 + op2;
editResultado.Text := FloatToStr(res);
O resultado final, com todo o código acrescentado, será o seguinte:
procedure TForm1.btnCalculaClick(Sender: TObject);
begin
op1 := StrToFloat(editOperando1.Text);
op2 := StrToFloat(editOperando2.Text);
res := op1 + op2;
editResultado.Text := FloatToStr(res);
end;
Repare que foi utilizada a identação (deslocamento à direita) dos comandos, mas isso não é obrigatório
no Delphi. Foi utilizado apenas para facilitar a legibilidade.
Nota: Nomes de variáveis em Object Pascal podem ter até 63 caracteres e podem conter letras (A-Z,
a-z), números e o caractere '_'. O primeiro caractere não pode ser número.
O Object Pascal não diferencia maiúsculas de minúsculas (não é case-sensitive).

Executando o Programa
Execute o programa com o botão ou a tecla [F9]. Digite um número no editOperando1 e outro
no editOperando2 e depois clique no btnCalcula.
Repare que você pode usar a tecla [Tab] para passar de um controle para outro utilizando o teclado (e não
[Enter], como no DOS). O controle que responde às teclas em determinado momento tem o foco de
teclado. Cada controle indica o foco de forma diferente. Por exemplo, um controle Edit mostra o cursor
de texto quando ele tem o foco, já um controle Button mostra um retângulo tracejado em volta do texto.
Como veremos, esta ordem de foco pode ser alterada.

Melhoras de Interface
Alguns detalhes podem ser melhorados: primeiro, o quadro de edição 'editResultado' não deveria
receber o foco quando o usuário pressiona [Tab] e não deveria permitir edição do texto, pois não faz
muito sentido. Para isso, selecione o controle e altere sua propriedade TabStop para False. Você pode
fazer isso selecionando o valor a partir da lista (com o botão de seta) ou com um clique duplo no valor da
propriedade, que alterna de True para False. Essa propriedade, quando é False, faz com que o controle
seja "pulado" pela tecla [Tab].
Também altere a propriedade ReadOnly para True, o que faz com que o controle não permita
edição de seu valor.
Outra mudança: como o usuário não usará a tecla [Enter], podemos fazer com que ela acione o
botão "=". Basta alterar a propriedade Default do botão 'btnCalcula' para True (verdadeiro). Isso faz com
que ele seja o botão default, que é sempre acionado quando o usuário tecla [Enter]. Execute o programa e
teste as modificações.
Ao executar o projeto podemos observar que os quadros de edição 'editOperando1',
'editOperando2' e 'editResultado' aparecem com seus respectivos nomes , o ideal seria aparecer
inicialmente sem nenhuma informação, para fazer esta modificação vamos retornar ao projeto e alterar a
propriedade Text de cada componente edit . É a propriedade Text que indica o conteúdo do componente
naquele momento, ao criar o componente edit e modificar a propriedade Name o delphi automaticamente
coloca o mesmo conteúdo desta propriedade na propriedade Text, caso esta não tenha sido modificada
antes.

Salvando o Projeto
Para salvar o projeto e todos os arquivos, basta usar File|Save All ou clicar no botão da
SpeedBar. Com isso o Delphi vai salvar todas as alterações feitas no projeto, como foi informado o
nome do projeto e da unit ele não irá pedir novamente.
Capítulo 4
Variáveis e Tipos de Dados

Tipos de Dados

Funções de Conversão

Operações Aritméticas

Tipos Ordinais

Escopo de Variáveis

Constantes
Tipos de Dados
Toda variável tem um tipo, que determina quais os valores que você pode colocar nessa variável e quais
as operações que podem ser executadas sobre ela. Uma variável de um tipo numérico, por exemplo, só
pode receber valores numéricos. Esses valores podem ser constantes, valores de outras variáveis, ou
expressões com operações sobre valores. Para colocar um valor em uma variável, usa-se o operador de
atribuição: ':=' (dois-pontos-igual), que é lido como "recebe". Por exemplo:
procedure TForm1.Button1Click(Sender: TObject);
var
a: integer; { 'a' é uma variável do tipo Integer }
b: integer; { 'b' idem }
begin
a := 2;
b := a;
b := b + 3;
end;
Geralmente, com algumas exceções, uma variável de um determinado tipo só pode receber valores deste
tipo. Não é permitido, por exemplo, o seguinte:
a := 'Nome';
...pois apenas valores numéricos podem ser colocados na variável 'a'.
Existem vários tipos de dados numéricos, e cada um tem uma determinada faixa de valores e ocupa um
certo espaço de memória. Não é permitido colocar em uma variável um valor que esteja fora da faixa do
seu tipo.

Tipos Numéricos
A linguagem Object Pascal tem vários tipos de dados para variáveis numéricas, que são os seguintes:
Tipo Bytes Faixa de Valores Precisão(dígitos)
Tipos Inteiros
Shortint 1 -128 a 127 N/A
Byte 1 0 a 255 N/A
Smallint 2 -32768 a32767 N/A
Word 2 0 a 65535 N/A
-2 bilhões a 2 bilhões
Integer 4 N/A
(-2.147.483.648 a 2.147.483.647)
Longint 4 -2.147.483.648 a 2.147.483.648 N/A
Cardinal 4 0 a 4.294.967.295 N/A
Longword 4 0 a 4.294.967.295 N/A
Int64 8 - 2 63 a 2 63 N/A
Tipos Reais
Single 4 1.5x10-45 a 3.4x1038 7-8
Real48 6 2.9x10-39 a 1.7x1038 11-12
Real 8 5x10-324 a 1.7x10308 15-16
Double 8 5x10-324 a 1.7x10308 15-16
Comp 8 -263 a 263 19-20
Currency 8 -922337203685477.5808 a 922337203685477.5807 19-20
Extended 10 3.6x10-4951 a 1.1x104932 19-20

*Nota: No Delphi 4, o tipo Real é idêntico ao tipo Double. No Delphi 3, o tipo Real tem uma faixa de valore menor que o
Double e equivale ao tipo Real48 do Delphi 4. Não é recomendável utilizar Real48, porque ele é muito mais lento para
cálculos.

A coluna "bytes" diz quantos bytes de memória são ocupados por uma variável do tipo. Quanto maior a
faixa de valores, em geral, maior o espaço de memória.
Os tipos numéricos se dividem em tipos inteiros (ou integrais), que só permitem valores inteiros e tipos
reais (ou de ponto flutuante [floating point]), que permitem valores com parte fracionária. Uma forma de
entender a diferença é que tipos inteiros são usados para contagem de elementos, enquanto tipos reais são
usados para medir alguma coisa. E essa medida nem sempre é exata. Ao fazer cálculos com variáveis
reais, nem sempre o resultado é igual ao esperado. Por exemplo:
var
fracao, a, b: real; {'real' é um tipo real}
begin
a := 1.0; { o mesmo que 1 }
fracao := a / 3;
b := fracao * 3;
if a = b then
... { faz alguma coisa }
end;
Teoricamente no programa, 'a' é igual a 'b', mas como tipos reais não representam valores exatos, a
igualdade não é exata. A variável 'b' pode ter um valor de 0.99999999..., que ao ser comparado, não é
igual a 1.0.
Cada tipo real tem uma certa precisão, indicada na tabela, que diz quantos dígitos significativos (dígitos
excetuando zeros à esquerda e à direita) a variável pode guardar, sem perda de precisão nos cálculos.
Assim, é importante lembrar que os valores guardados em um desses tipos nem sempre é exato.
Geralmente os tipos inteiros são mais eficientes para cálculos, e também mantém valores exatos para
qualquer operação, desde que o resultado não saia da sua faixa de valores.
Os tipos mais utilizados são smallint, para valores inteiros pequenos, integer ou Longint para valores
inteiros maiores, e Double para valores reais com uma precisão razoável. Os tipos Shortint e Byte são
utilizados quando é preciso guardar um contador bem pequeno e é necessário economizar
memória.Quando os tipos integer ou longint não são suficientes podemos usar os tipos longWord e
int64 que existe somente nesta versão.
O tipo Comp é considerado como real, mas ele permite apenas valores inteiros. Ele é útil para valores
monetários.

O tipo Char
O tipo Char permite criar variáveis que guardam caracteres individuais, como letras, dígitos, sinais de
pontuação e caracteres de controle. Cada variável do tipo Char só pode conter um caractere. Caracteres
comuns (imprimíveis) são representados entre aspas simples (apóstrofos):
var minhaVariavel: Char;
...
...
minhaVariavel := 'A';
Um caractere também pode ser representado através do código ASCII/ANSI correspondente. Isso é útil
principalmente com caracteres de controle, que não podem ser impressos nem digitados normalmente no
programa, como caracteres de backspace, fim de linha, retorno de carro etc. Por exemplo, para guardar o
caractere 13 (retorno de carro), usa-se a sintaxe:
minhaVariavel := #13;
Se o código estivesse numa variável em vez de uma constante, não pode ser usada essa sintaxe. Nesse
caso, pode-se usar a função Chr:
codigoASCII := 13;
minhaVariavel := Chr(codigoASCII);
Nota: para ver os caracteres da tabela ANSI (exceto os de controle), use o aplicativo Mapa de Caracteres do Windows
(em Iniciar | Programas | Acessórios).

O tipo String
Para guardar mais de um caractere, é preciso usar o tipo String. Um valor constante do tipo String é
representado entre apóstrofos, como por exemplo:
var nome: string;
begin
nome := 'João da Silva';
Uma variável string normalmente não tem limite de tamanho definido. O espaço de memória que ela
ocupa muda dinamicamente de acordo com o conteúdo atribuído a ela, por exemplo:
nome := '123'; {3 caracteres}
nome := 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; {26 caracs.}
nome := ''; {zero caracteres}
No último caso, tem-se uma string vazia, que é representada por '' (apóstrofos sem caracteres dentro), que
ocupa zero caracteres. Ela é usada para "limpar" o conteúdo de uma variável.
Nota: O Windows usa uma tabela de caracteres chamada tabela ANSI, que associa um código numérico
a cada caractere.
Nota: o Windows permite caracteres acentuados e especiais, como Â, Ç, ©, ½, «, ¶, etc.
Mas uma variável string pode ser declarada com um tamanho fixo, da seguinte forma:
var
nome: string[50];
telefone: string[10];
Nesse caso, a variável ocupa um espaço fixo de memória, de acordo com o tamanho declarado. No
exemplo, nome ocupa 50 caracteres e telefone ocupa 10. Se você tentar atribuir mais caracteres do que
cabe na variável, os caracteres restantes são ignorados. Se você atribuir menos, o tamanho lógico da
string passa a ser menor que o tamanho físico. O Object Pascal usa um byte a mais para armazenar o
tamanho lógico.
Você pode concatenar (juntar) duas ou mais strings com o operador +. Por exemplo:
var prefixo, linguagem, titulo: string;
begin
prefixo := 'Curso';
linguagem := 'Delphi';
titulo := prefixo + ' de ' + linguagem;
{ O resultado é uma string 'Curso de Delphi' }
Para obter caracteres individuais de uma string, você pode usar o nome da variável e mais um valor entre
colchetes. Por exemplo, se você quiser guardar o 7º caractere do nome numa variável char, basta fazer:
c := nome[7];
Existem várias propriedades que também são do tipo String, como Caption e Text, por exemplo. Assim,
é possível atribuir uma constante ou variável do tipo String para elas, por exemplo:
btnIniciar.Caption := '&Iniciar';
memoTempoTotal.Text := s;
Nota: existem várias funções de manipulação de strings, por exemplo, para obter uma parte de uma
string. Consulte o help do Delphi e procure por 'String manipulation functions'.

O tipo Boolean
Várias propriedades (e variáveis também) podem conter os valores True (verdadeiro) e False (falso),
indicando uma condição lógica ou um "flag" (ligado/desligado, habilitado/desabilitado, sim/não). Para
isso utiliza-se o tipo Boolean, que só tem dois valores possíveis: True e False. Por exemplo:
tmrMarca.Enabled := False;
O Delphi também utiliza o tipo Boolean internamente, ao fazer qualquer comparação lógica, em um
comando if, por exemplo. E uma variável do tipo Boolean sozinha já é uma condição lógica, portanto
pode ser usada em um comando if diretamente, por exemplo:
var ValorCorreto: Boolean;
begin
...
if ValorCorreto then ...
...
{ Enabled é uma propriedade do tipo Boolean }
if tmrMarca.Enabled then ...
...
end;

O tipo TDateTime
Para trabalhar com datas em Delphi, você pode usar o tipo TDateTime. Veremos as operações de data
mais em detalhe em Manipulação de datas.
Funções de Conversão
Qualquer variável numérica pode receber um valor do mesmo tipo ou de um outro tipo numérico, exceto que uma
variável inteira (Integer, Longint, Word etc.) não pode receber um valor real, pois este pode conter uma parte
fracionária. Por exemplo:
var
a: integer;
b: longint;
x: double;
y: extended;
begin
b := 10;
a := b; {ok: inteiro recebe inteiro}
x := a; {ok: real recebe inteiro}
y := x; {ok: real recebe real}
x := 3.7;
b := x; {errado: inteiro recebe real}
end;
Nesse caso é preciso usar uma função de conversão, que ou descarta a parte fracionária (função trunc) ou
arredonda o valor (função round):
x := 3.7;
a := trunc(x); {a vai valer 3}
b := round(x); {b vai valer 4}
Existem várias funções que convertem valores de tipos numéricos para o tipo String e vice-versa. Nós já vimos
algumas em programas-exemplo:
var
varReal: double;
varInteira: integer;
varString: string;

begin
{ de String para numérico: }
varReal := StrToFloat(varString);
varInteira := StrToInt(varString);
varInteira64 := StrToInt64(varInteira64);
{ de numérico para String: }
varString := FloatToStr(varReal);
varString := IntToStr(varInteira); {VarInteira pode ser de qualquer tipo
inteiro,inclusive int64 }
end;
Além dessas, existe a função Str, do Pascal padrão, que converte um número qualquer para String, permitindo
especificar uma largura de campo (o número é alinhado à direita com espaços) e a quantidade de casas depois da
vírgula. Por exemplo:
segundos := 34.749;
Str(segundos:7:2, s);
A variável 's' vai ser preenchida com uma string 'oo34.75' (cada o representa um espaço em branco). Ou seja, vai
conter no mínimo 10 caracteres, com os dígitos alinhados à direita e duas casas depois da vírgula (casas a mais são
arredondadas).
Operações Aritméticas
Os tipos de dados inteiros suportam as operações aritméticas usuais:
soma: a := x + y;
subtração: a := x - y;
multiplicação: a := x * y;
divisão: a := x / y;

Se os dois operandos são do mesmo tipo, o resultado é desse tipo. Se um deles tem uma faixa de valores
maior (por exemplo, um Double e um Integer), o resultado será do tipo deste operando (no exemplo,
Double).
Para os números inteiros o resultado será do tipo int64 somente se tiver uma variável deste tipo, caso
contrário o resultado será do tipo integer.
Se os dois operandos são inteiros, o resultado será inteiro e pode ser atribuído para uma variável inteira.
Uma exceção é que no caso da divisão, o resultado sempre é real, mesmo quando os dois operandos são
inteiros. Portanto, não é permitido fazer:
a := b / c;
quando 'a' é uma variável inteira. Mas pode-se utilizar uma função de conversão, como trunc:
a := trunc( b /c );
ou usa-se o operador de divisão inteira div (os dois operandos dever ser inteiros), por exemplo:
b := 13;
c := 4;
a := b div c; {retorna a parte inteira da divisão = 3}
Existe também o operador mod, que calcula o resto da divisão:
x := b mod c; {retorna o resto de 13 div 4, que é = 1}
Expressões podem ser combinadas com vários operadores e operandos. Multiplicação e divisão são
executadas primeiro, depois soma e subtração, a menos que sejam usados parênteses, por exemplo:
x := a + b * c; {multiplica 'b' e 'c' e soma com 'a'}
y := (a + b)*c; {soma 'a' e 'b' e multiplica por 'c'}
Tipos Ordinais
Um tipo de dados é considerado um tipo ordinal quando existe uma seqüência definida entre seus
elementos, ou seja, a partir de um elemento é sempre possível passar para o próximo elemento do tipo.
Os tipos ordinais predefinidos pela linguagem (veremos que você pode criar outros) são:
· Todos os tipos inteiros (Integer, Word, Cardinal, Longint,Enumerados...);
· O tipo Char (os caracteres são ordenados pela tabela ASCII/ANSI);
· O tipo Boolean (só tem dois elementos: False,True nessa ordem).
Notas: Os tipos reais não são considerados tipos ordinais.
Algumas operações podem ser feitas com qualquer tipo ordinal. A função succ retorna o próximo
elemento do tipo, enquanto pred retorna o anterior:
var
c1, c2: char;
x, y: integer;
a, b: boolean;
begin
c1 := 'G';
c2 := succ(c1); {vai ser = 'H'}
c2 := pred(c1); {vai ser = 'F'}
x := 299;
y := succ(x); {succ(x) = x + 1 e pred(x) = x - 1}
a := False;
b := succ(a); {vai ser = True}
end;
Se você chamar succ para o último elemento da seqüência ou pred para o primeiro elemento, isso vai
causar um erro de execução (por exemplo, 'succ(True)' não é permitido).
A função ord retorna a posição numérica do elemento dentro do tipo. No caso de um número inteiro,
retorna o próprio número, portanto não tem utilidade prática. No caso de um caractere, retorna o código
ASCII (ANSI) do caractere, que é sua posição na tabela ASCII (ANSI):
x := ord('G'); {x vai ser 71}
No caso do tipo Boolean, ord(False) = 0 e ord(True) = 1. Isso pode ser usado para converter uma
condição lógica em valor numérico.
Mais tarde veremos outras operações que podem ser executadas em tipos ordinais.
Notas : As funções succ e pred podem ser utilizadas para variáveis do tipo int64, mas o mesmo não é
válido para a função ord.
Escopo de Variáveis
O lugar onde é declarada uma variável determina o escopo de uma variável, ou seja, qual a região do
programa onde a variável pode ser acessada e o tempo de vida da variável, ou seja, qual o intervalo de
tempo durante o qual ela mantém seu valor. Qualquer variável só pode ser utilizada no programa depois
do ponto onde foi declarada.

Variáveis Locais
Uma variável declarada dentro de um procedimento é uma variável local. Ela só pode ser utilizada
dentro do procedimento (o escopo da variável é o corpo do procedimento).
O tempo de vida de uma variável local é o tempo durante a execução do procedimento. Quando a
execução do procedimento é iniciada, a variável está indefinida, ou seja, seu valor pode ser qualquer
dado que existia anteriormente na memória. Se esse valor for utilizado, os resultados são imprevisíveis.
Depois que o procedimento termina, a memória ocupada pela variável é liberada, e seu valor anterior é
perdido.
Como já vimos, variáveis locais são declaradas com a palavra var, logo após o cabeçalho do
procedimento, mas antes do begin que inicia os comandos:
procedure btnCalculaClick(Sender: TObject);
var op1, op2, res: double;
begin
...
end;

Variáveis Globais da Unidade


Se uma variável precisa ser usada em vários procedimentos, mantendo o seu valor, é preciso declarar a
variável como global, ou seja, fora de qualquer procedimento. O escopo dessa variável passa a ser desde
o ponto onde foi declarada até o fim da unidade. Por exemplo:
implementation
{$R *.DFM}
var VariavelGlobal: integer;
Variáveis globais são inicializadas com valores padrão: zero para numéricas, False para booleans, ''
(texto vazio) para strings, etc. Elas mantêm seus valores durante toda a execução do programa.
Ao declarar uma variável global, você pode fornecer um valor inicial diferente do padrão. Para isto, a
variável deve ser declarada em separado de outras (não em uma lista) e depois do tipo de dados, você
deve colocar um "=" e o valor inicial, que deve ser constante. Por exemplo:
var
VariavelGlobal: integer = 1;
Titulo: string = 'Título inicial';
AtivaTimer: boolean = true;
Variáveis Globais do Projeto
Uma variável global, se declarada na parte implementation, só pode ser acessada dentro daquela
unidade. Se for declarada na parte interface, pode ser usada por outra unidade, desde que essa outra
tenha uma cláusula uses para a unidade que contém a variável. Veremos mais sobre isso em Estrutura
das Unidades.
Constantes
Constantes são valores que podem ser colocados em variáveis, usados em expressões etc. Uma constante
literal é um valor sem nome, escrito explicitamente, como os seguintes:
20 3.5 (constantes numéricas - integer e double)
'Testando' (constante do tipo string)
False True (constantes do tipo boolean)
'x' #8 (constantes do tipo char)
O tipo de dados de uma constante é determinado pela forma desta. Números sem parte fracionária são
considerados constantes inteiras (isso se o número couber na faixa de valores de um tipo inteiro). Uma
constante inteira também pode ser especificada como um valor hexadecimal, prefixado por '$', por
exemplo:
x := $2F3;
Números hexadecimais contém também os dígitos A até F, além de 0 a 9.
Uma constante real também pode ser escrita em notação científica, especificando-se um valor e uma
potência de 10:
y := 3.5e4; { = 3.5 x 104 = 35000 }
z := 2.78e-6; { = 2.78 x 10-6 = 0.00000278 }
Você pode criar constante especificando o tipo que esta constante irá retornar , por exemplo:
const Inteiro64 = int64(17)
Desta forma o tipo que a constante irá retornar é int64.
Você também pode criar constantes nomeadas, que são simplesmente substituídas pelos valores
correspondentes durante a compilação do programa. Elas facilitam a manutenção do programa, quando
um valor é utilizado em vários lugares. Para isso, usa-se a declaração const:
const
TAMANHO = 30;
METADE = TAMANHO/2;
PI = 3.1415926536;
MENS_ERRO = 'Valor incorreto. Digite novamente.'; >
Constantes nomeadas são utilizadas como qualquer outra constante:
x := TAMANHO * 8;
angulo := PI*graus/180;
Mas não são variáveis, portanto não podem receber outro valor:
TAMANHO := 15; { erro de compilação }
Da mesma forma que variáveis, uma constante pode ser local (se declarada em um procedimento) ou
global (se declarada fora).
Capítulo 5
Estruturas de Controle

Inserindo Comandos com o CodeInsight

Estruturas de Decisão

Estruturas de Laços

Outros Comandos
Inserindo comandos com o CodeInsight
Para facilitar a digitação dos comandos da linguagem e reduzir os erros, o CodeInsight do Delphi possui
um recurso chamado modelos de código [code templates], que permite inserir rapidamente um comando
da linguagem contendo apenas a estrutura básica, que depois você pode preencher. Por exemplo, digite
casee no editor de código e tecle [Ctrl+J]. O CodeInsight irá inserir um comando case com uma cláusula
else e espaços em branco para você completar:
case of
: ;
: ;
else ;
end;
O cursor ficará posicionado entre case e of. Você pode completar com um nome de variável, valores
constante e os comandos a serem executados.
Para ver uma lista completa dos modelos de código, tecle [Ctrl+J]. Veja por exemplo, os modelos ife, ifb,
ifeb, ifs, forb, fore, whileb, whiles, caseb, cases. Note que os outros usam comandos do Object Pascal
que ainda não foram vistos. ]
Nota: você pode alterar os modelos de código ou criar novos. Para isso clique em Tools|Environment
Options e na página CodeInsight. Nesta mesma página, você pode também desmarcar a opção "Code
Completion" se quiser. Essa opção mostra a lista de propriedades e métodos de um componente quando
você digita o nome do componente e um ponto.
Estruturas de Decisão
O Comando if...then...else...
Nós já vimos o comando if em várias situações. A sua sintaxe básica é:
if condição then comando1 else comando2;
{ OU sem o else: }
if condição then comando1;
A identação de comandos, ou a separação do comando em várias linhas não são obrigatórias em
nenhuma situação no Object Pascal. Mas geralmente um comando if é escrito em várias linhas, para
maior legibilidade:
if condição then
comando1
else
comando2;
Dica: No editor de código, para identar um bloco de comandos (deslocar à direita), use [Ctrl+Shift+I].
Para deslocar à esquerda, use [Ctrl+Shift+U].
Nessa sintaxe, condição é qualquer expressão que gere um resultado do tipo Boolean (True para
verdadeiro, False para falso), podendo ser uma única variável booleana, embora na maioria das vezes
seja construída usando operadores de comparação, como '=', '>' etc.
Note que não é permitido o ';' (ponto-e-vírgula) logo depois do then ou logo antes ou depois do else. Só
pode haver ';' depois do fim do comando if. Se não houver o else, o ';' é colocado depois do comando1.
Por exemplo:
if memoEditor.SelLength <> 0 then
menuEdiRecortar.Enabled := True
else
menuEdiRecortar.Enabled := False;
Quando, em lugar de comando1 ou comando2 é preciso executar vários comandos, usa-se os
delimitadores begin e end para criar um comando composto. Um comando composto é tratado pelo
Object Pascal como um único comando, por exemplo:
if condição then
begin
comando1;
comando2;
end
else
begin
comando3;
comando4;
end;
Dentro do comando composto, os comandos são separados por ';', mas fora dele se aplicam as regras do
if.
Um comando if pode ser usado dentro de outro, mas isso pode dificultar o entendimento se um deles
inclui uma cláusula else. Nesse caso o else fica ligado ao if mais próximo.
Mais de uma condição pode ser usada no if, combinando-as com and (e), or (ou) e not (não). Mas nesse
caso, são obrigatórios parênteses em volta das condições:
if (Key < '0') or (Key > '9') then
Key := #0;
O operador and tem precedência sobre o or, e o not tem precedência sobre os dois, portanto é preciso
levar isso em conta em condições mais complexas, e utilizar parênteses para especificar o que se deseja
avaliar primeiro:
if ((Key < '0') or (Key > '9')) and (Key <> #8) then
Key := #0;
E o operador not pode ser usado para inverter o resultado de uma condição:
if ((Key < '0') or (Key > '9')) and not (Key = #8) then
Key := #0;
Dica: para encontrar um fecha-parênteses perdido, coloque o cursor antes do abre-parênteses e tecle
[Ctrl+Q+[ ] (ctrl+Q+abre-colchetes).

O Comando case...of...
Muitas vezes é preciso comparar uma variável com vários valores. Isso pode ser feito com vários
comandos if aninhados:
if dia = 1 then
desconto = 30
else if dia = 2 then
desconto = 20
else if dia = 3 then
desconto = 10;
Mas o mesmo pode ser feito com o comando case, especificando a variável, e seus valores possíveis:
case dia of
1: desconto = 30;
2: desconto = 20;
3: desconto = 10;
end;
A sintaxe geral desse comando é:
case expressão of
rótulo1: comando1;
rótulo2: comando2;
...
else comandoElse;
end;
Onde expressão pode ser qualquer variável ou expressão que retorne um valor de um tipo ordinal. Cada
rótulo pode ser uma constante sozinha, várias constantes separadas por vírgulas, ou uma faixa de valores.
Por exemplo:
case dia of
1: nome := 'primeiro'; { constante }
2: nome := 'segundo';
3..10: nome := 'terceiro'; { valores entre 3 e 10 }
11,13,15: nome := 'quarto'; { valores 11, 13 e 15 }
end;
Cada comando na sintaxe pode ser um comando simples ou um comando composto formado pelos
delimitadores begin e end.
A parte do else é opcional e indica um comando a ser executado se nenhum dos valores corresponde ao
valor da expressão.
Estruturas de Laços
Estruturas de laço permitem repetir um conjunto de comandos. Cada um dos comandos de laço especifica
uma forma diferente de iniciar e testar o laço.

O comando for
O comando for permite repetir um laço um número especificado de vezes, incrementando ou
decrementando uma variável a cada passagem do laço. Você deve especificar o valor inicial e final da
variável. A forma de uso é a seguinte:
for variável := valor_inicial to valor_final do
comando;
O comando, como antes, pode ser um comando simples, ou um comando composto. A variável, o valor
inicial e o valor final devem ser todos de um tipo ordinal. Se no início da execução o valor inicial é maior
do que o final, o comando nem chega a ser executado.
O seguinte laço mostra os valores numéricos de 1 até 20 dentro de um componente ListBox:
var valor: integer;
s: string;
begin
for valor := 1 to 20 do
begin
s := IntToStr(valor);
ListBox1.Items.Add(s);
end;
end;
A variável também pode ser do tipo Char, pois este é um tipo ordinal:
for letra := 'A' to 'Z' do
...
Para decrementar a variável durante o laço, ao invés de incrementar, usa-se downto no lugar da palavra
to:
for letra := 'Z' downto 'A' do
...
for valor := 20 downto 1 do
...
Note que o incremento ou decremento é sempre 1.

O comando while...do...
O laço while repete um comando enquanto determinada condição é verdadeira. Sua sintaxe de forma
geral é:
while condição do comando;
Por exemplo:
i := valorAnt;
while i < 1000 do
begin
Processar(i);
i := i + incremento;
end;
Antes da primeira iteração do laço, a condição é verificada. Se ela for falsa, o comando não chega a ser
executado. Se for verdadeira, o comando é executado, e a condição é verificada novamente e assim por
diante.

O comando repeat..until
O repeat..until é semelhante ao while, mas testa a condição no final, depois de executar o comando, e
termina a execução se a condição for verdadeira (o contrário do while). Sua sintaxe geral é:
repeat
comando1;
comando2;
...
until condição;
Por exemplo:
i := valorAnt;
repeat
Processar(i);
i := i + incremento;
until i >= 1000;
O comando repeat sempre executa uma iteração do laço pelo menos. Note que ele não precisa de begin e
end para delimitar a lista de comandos, pois as próprias palavras repeat e until já formam delimitadores.
Outros Comandos
Comandos para sair de laços
Durante a execução de um laço, é comum precisar terminar sua execução antes do tempo. Para isso, pode
ser usado o comando break. Este comando sai imediatamente do laço mais interno (seja for, while, ou
repeat) e vai para o próximo comando depois do laço. Por exemplo:
for i := 1 to 1000 do
begin
encontrou := ProcuraValor(i);
if Encontrou(i) then
break;
end;
{ o break salta para esta linha }
Além desse, existe o comando continue. Ele força a execução da próxima iteração (repetição) do laço,
ignorando os comandos restantes dentro do bloco do laço. Por exemplo, o próximo laço mostra em uma
list box os números de 1 a 300, exceto os múltiplos de 7:
for x := 1 to 300 do
begin
if x mod 7 = 0 then { é um múltiplo de 7? }
continue; { vai para o próximo }
s := IntToStr(x);
ListBox1.Items.Add(s);
end;

O comando exit
Para sair imediatamente de um procedimento, usa-se o comando exit. Use-o quando ocorre alguma
condição de erro, por exemplo:
if valorErrado then
begin
mostrarMensagem;
exit;
end;
Capítulo 6
Propriedades e Eventos Comuns

Tipos de Propriedades

Propriedades Mais Usadas

Eventos Comuns

Detalhes do Tratemnto de Eventos


Tipos de Propriedades
Existem várias formas de editar o valor de uma propriedade, dependendo da propriedade específica. Nós
já vimos a maioria delas, que são basicamente as seguintes:
Propriedades que aceitam qualquer valor: você precisa digitar o valor diretamente. Exemplo: Caption,
Text. A propriedade Name só aceita um identificador válido (mesmas regras que nomes de variáveis).
Propriedades numéricas: O Delphi permite digitar qualquer coisa, mas quando você teclar [Enter] ou
sair, ele vai verificar se é um valor numérico. Exemplo: Left, Top, MaxLength (do componente Edit).
Propriedades de lista: Um botão aparece ao lado do valor. Ao clicar nele, abre-se uma lista de
valores, e é possível selecionar um deles:

Exemplo: BorderStyle, Color, WindowState, Align. Geralmente o valor só pode ser um dos
disponíveis na lista, mas algumas propriedades permitem digitar outros valores, como Color.
Propriedades booleanas: uma propriedade do tipo Boolean, que só tem dois valores possíveis: False e
True. Exemplo: Enabled, Visible, Ctl3D. Você pode selecionar um deles na lista, mas é mais fácil usar ,
com um clique duplo no valor, o conteúdo será alternado entre True e False ou clicar no botão .

Propriedades expansíveis: um sinal de "+" aparece ao lado da propriedade e, clicando duas vezes no
nome, ela se expande em sub-propriedades. Exemplo: Font.

Para fechar a lista de sub-propriedades, clique duas vezes no nome da propriedade. Para usar essa
propriedade em tempo de projeto, utiliza-se um ponto a mais e o nome da sub-propriedade, por exemplo:
memoEditor.Font.Color := clBlack;
Propriedades de conjunto: São parecidas com propriedades expansíveis. As "sub-propriedades" que
aparecem são todas booleanas (True/False). Exemplo: BorderIcons, Font.Style. O valor que aparece é
um conjunto de valores, que como veremos mais tarde, exige uma sintaxe diferente para ser usado no
programa.
Para alterar a propriedade dentro do programa, use por exemplo:
BorderIcons := [biSystemMenu, biMaximize];
Propriedades com editor: Algumas propriedades mostram um valor como (TFont) ou (TStringList), que
não pode ser editado, e um botão , que quando clicado mostra um editor de propriedade especializado.
Exemplo: Lines (do componente Memo), Font (que também pode ser editada abrindo as
sub-propriedades). O editor também pode ser chamado com um clique duplo na coluna de valor.
Propriedades Mais Usadas
Propriedades para Todos os Componentes
Duas propriedades existem para qualquer componente (inclusive para o formulário, que é considerado
um componente): são Name (o nome do componente) e Tag. Esta última é uma propriedade numérica
(tipo integer) que o Delphi não utiliza. Ela pode ser usada para guardar uma informação qualquer
associada ao componente, apenas para uso interno.

Propriedades de Tamanho e Posição


Você pode alterar o tamanho e a posição do formulário, ou de um controle, movendo-o na tela ou
arrastando suas bordas. Mas você também pode digitar suas coordenadas de posição e tamanho
manualmente. Essas coordenadas são medidas em pixels, que são os pontos físicos da tela, e são
especificadas pelas propriedades Left, Top, Width e Height:

Para um formulário, Left é a distância deste à extremidade esquerda da tela, Top é a distância em relação
ao topo da tela. Para um controle, essas medidas são feitas em relação à área interna do formulário
(excluindo as bordas). O tamanho do formulário ou controle é especificado por Width (na horizontal) e
Height (na vertical).
Todas essas propriedades podem ser alteradas também através de comandos de programa, para alterar
dinamicamente a posição e tamanho de um controle.

Propriedades do Formulário
Outras propriedades alteram o posicionamento e tamanho do formulário. A maioria dessas propriedades
só mostra efeito ao executar o programa.
A propriedade WindowState controla como o formulário vai aparecer:
wsNormal estado normal
wsMinimized janela minimizada (aparece na forma de um ícone)
wsMaximizedjanela maximizada (ocupando a tela inteira)
A propriedade Position determina se o Windows pode alterar a posição ou o tamanho que você definiu
para o formulário. O valor padrão é 'poDesigned':
poDesigned posição e tamanho de acordo com o projetado
poScreenCenter centraliza o formulário na tela
poDefault o Windows pode mudar a posição e tamanho
poDefaultPosOnly o Windows só pode mudar a posição
poDefaultSizeOnlyo Windows só pode alterar o tamanho

A propriedade BorderStyle controla o estilo da borda do formulário:


bsSizeable permite o usuário alterar o tamanho da janela
bsSingle não permite alterar tamanho (estilo de borda fina)
bsDialog não permite alterar tamanho (estilo de borda dupla)
bsNone nenhuma borda, nem barra de título (não permite mover)
bsToolWindowcomo bsSingle, mas com título menor
bsSizeToolWincomo bsSizeable, mas com título menor

O valor padrão é 'bsSizeable'. A diferença entre 'bsSingle' e 'bsDialog' é uma questão de padrões de
interface, como veremos depois.
A propriedade BorderIcons, que se expande em "sub-propriedades", determina quais ícones de controle
aparecem na barra de título do formulário. Cada valor correspondente aos elementos da barra, e
alterando-o para True ou False pode-se ativar ou desativar um deles (o efeito só aparece ao executar o
programa):

O outro valor, biHelp, coloca um botão de ajuda com uma interrogação na borda. Esse botão pode ser
usado para chamar uma tela de help. Só funciona se biMinimize e biMaximize estiverem desativados.
A propriedade AutoScroll, como padrão, é verdadeira (contém o valor True). Isso faz com que o
formulário mostre barras de rolagem automaticamente quando os controles não cabem na área visível da
tela. Para testar, crie um controle qualquer, como um Edit e mova-o até deixar metade dele fora do
formulário em alguma das extremidades. Vai aparecer uma barra de rolagem (horizontal ou vertical), que
você pode usar para ver a área que atualmente não é visível. Se você quiser, pode desativar AutoScroll,
colocando um valor False. Com isso, controles que estiverem fora da área do formulário não podem ser
acessados pelo usuário.

Propriedades de Controles
Controles (ou componentes visuais), além das quatro propriedades de posição (Left, Top, Width e
Height) têm propriedades que alteram sua aparência, a maioria existe também no formulário.
A propriedade Font determina as características do texto mostrado pelo controle. No caso do formulário,
ela afeta os controles contidos nele, não o próprio.
Para testar, coloque três controles Label no formulário. Altere a propriedade Font do formulário,
mudando o nome da fonte para "Times New Roman". Isso vai afetar os dois controles. Agora altere a
propriedade Font de um dos controles, "Label1", mudando Font.Name para "Arial". Só esse controle
será afetado. Se você novamente alterar a fonte do formulário, apenas "Label2" e "Label3" serão
alterados.
O que determina se um controle usa uma fonte própria ou a do formulário é a propriedade ParentFont.
Note que ParentFont é False para o "Label1" (que foi alterado) e True para os outros dois. Quando você
altera a fonte individualmente de um controle, ParentFont automaticamente é mudado para False. Se
você mudar ParentFont para True, a fonte do controle vai ser alterada para ficar igual à do formulário.
A propriedade Color determina a cor do formulário ou controle. Existem várias formas de escolher uma
cor. Você pode selecionar da lista o nome de uma cor específica (clBlack = preto, clBlue = azul, etc.) e
nesse caso, a cor será sempre a que você especificou. Outra opção é usar nomes de cores relativas à
configuração do Windows, como clWindow = cor de janela, clBtnFace = cor de botão, clWindowText =
cor de texto de janela etc. Nesse caso, a cor real que será utilizada depende da configuração de cores do
Windows no computador do usuário.
Para cada controle que tem a propriedade Color, existe também uma propriedade ParentColor, que
determina se o controle usa a cor definida pelo formulário ou sua própria cor individual (de forma
análoga a Font e ParentFont).
Como já vimos, um controle ou um item de menu pode ser habilitado ou desabilitado com a propriedade
Enabled (True ou False). Você também pode esconder um controle ou item de menu do usuário
alterando a propriedade Visible para False. Ele continua aparecendo em tempo de projeto, e você ainda
pode utilizá-lo no programa, mas o usuário não vai vê-lo durante a execução.
O texto mostrado por um controle é definido pela propriedade Caption, se ele não permite edição, ou
Text, se ele permite edição pelo usuário.
Uma facilidade que o Delphi fornece é a capacidade de colocar "dicas" [hints] que são mostradas quando
o usuário passa o mouse por cima de um controle. A propriedade Hint de um controle define qual o texto
da dica. Para que a dica seja exibida, a propriedade ShowHint deve ter o valor True. Essa propriedade
pode ser definida para cada controle individualmente, ou para o formulário (nesse caso,
ParentShowHint, em cada controle, define qual das duas é usada).
A propriedade Cursor define qual o tipo de ponteiro (ou cursor) do mouse que será mostrado. O valor
padrão (crDefault) diz para utilizar o cursor padrão atual (geralmente uma seta). Outros valores
determinam ponteiros diferentes, por exemplo crArrow (seta), crHourglass (ampulheta).
Eventos Comuns
Crie um novo projeto. Nesse projeto, veremos os eventos mais utilizados pela maioria dos componentes, e também
algumas propriedades vistas anteriormente.

Eventos de Mouse
O evento OnClick, como já vimos, ocorre quando o botão do mouse é pressionado e solto sobre o controle (mas alguns
controles, como botões, também acionam este evento quando são ativados pelo teclado). Esse evento não fornece
nenhuma informação sobre as coordenadas do mouse. Além desse, existe o evento OnDblClick, que informa um clique
duplo (dois cliques em seqüência rápida).
Os eventos OnMouseDown e OnMouseUp são acionados quando um botão do mouse é pressionado ou liberado,
respectivamente, quando o cursor do mouse está posicionado sobre o controle. Esses eventos informam a posição do
mouse, qual botão foi pressionado e quais das teclas de modificação ([Shift], [Ctrl] e [Alt]) estavam pressionadas no
momento. O evento OnMouseMove ocorre quando o cursor do mouse é movimentado sobre o controle. Ele informa a
posição atual do cursor do mouse.

Vamos tratar o evento OnClick para o formulário. Digite o seguinte no procedimento de evento:
procedure FormClick(Sender: TObject);
begin
if Cursor = crDefault then {cursor é o padrão?}
Cursor := crHourglass {muda para ampulheta}
else Cursor := crDefault; {senão muda pra default}
end;
É importante notar que Cursor é uma propriedade do formulário. Nesse caso, não é preciso escrever o nome do
formulário na frente (o que seria Form1.Cursor), basta o próprio nome. Execute para testar. Agora quando você clicar no
formulário, o ponteiro vai mudar para uma ampulheta. Clicando novamente ele retorna ao valor padrão.

Eventos de Teclado
Existem três eventos de teclado, que são acionados em momentos diferentes. Cada um deles fornece informação sobre a
tecla pressionada, mas de formas diferentes.
Para cada tecla que é pressionada, o evento OnKeyDown é acionado, e quando a tecla é solta, OnKeyUp é acionado.
Ambos informam para o procedimento de evento o código da tecla pressionada. Esse código identifica a tecla, mas não o
caractere digitado. Por exemplo, não é possível distinguir entre maiúsculas e minúsculas, ou entre dois caracteres na
mesma tecla. Além disso, um caractere pode ser gerado pelo pressionamento de mais de uma tecla.
Quando é necessário distinguir os caracteres digitados, e não as teclas, pode ser utilizado o evento OnKeyPress. Ele
informa o caractere digitado como uma variável do tipo Char. Note que nem toda tecla aciona um evento OnKeyPress.
Teclas que não geram digitação, como [F1], [Insert], [Home], [Ctrl], acionam apenas os eventos OnKeyDown e
OnKeyUp.

Vamos tratar esses eventos para o formulário. Crie o seguinte procedimento de evento, associado ao OnKeyPress:
procedure TForm1.FormKeyPress(Sender: TObject;
var Key: Char);
begin
if Key = '*' then
Close;
end;
Nota: A variável 'Key' é um parâmetro do procedimento de evento, que é preenchido pelo Delphi. Alguns eventos têm
parâmetros, que contém informações adicionais fornecidas pelo Delphi.
Com isso, quando for pressionada a tecla ('*'), o formulário será fechado, usando o método Close.

Agora vamos tratar as teclas de seta, fazendo a movimentação do formulário na direção da seta utilizada.
Inicialmente, crie um procedimento de evento para OnKeyDown:
procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
begin

end;
Repare que o procedimento tem vários parâmetros: 'Key', que desta vez é numérica (tipo Word) informa o código da tecla
pressionada e 'Shift' informa o estado das teclas de modificação. Para tratar as setas, precisamos comparar o valor de Key
com cada um dos códigos de teclas de seta. Na verdade não é preciso saber o valor numérico de todos os códigos. O
Delphi tem constantes predefinidas correspondentes aos valores.

Entre o begin e o end digite o seguinte:


case Key of
vk_Left: Left := Left - 10;
vk_Right: Left := Left + 10;
vk_Up: Top := Top - 10;
vk_Down: Top := Top + 10;
end;
O comando case..of.., como veremos, compara uma variável com vários valores numéricos e executa um comando
dependendo do valor. No caso os valores são dados pelas constantes 'vk_Left', 'vk_Right',... As propriedades Left e Top
determinam a posição do formulário na tela. Para mover o formulário, basta alterar seus valores.

Execute e teste as teclas de seta para movimentar e a tecla '*' para fechar o formulário. Salve esse projeto como
EVENTOS.PAS e EVENTOSP.DPR.

Eventos do Formulário
Outros eventos do formulário correspondem a situações importantes de serem tratadas, por exemplo:
OnCreate: Quando o formulário é criado, ou seja, na inicialização do programa. É executado antes de qualquer outro
evento do formulário. Você pode usar para inicializar componentes.
OnDestroy: Quando o formulário está sendo destruído, ou seja, a sua área de memória será liberada. Ocorre geralmente
quando o programa está terminando.
OnShow: Logo antes do formulário ser mostrado na tela (pode ocorrer várias vezes durante a execução).
OnCloseQuery: O usuário está tentando fechar o formulário. Você pode cancelar o fechamento (fazendo CanClose :=
False) ou, por exemplo, confirmar se ele deseja salvar os dados.
OnClose: O formulário está sendo fechado. Você pode cancelar o fechamento ou determinar uma ação diferente, como
minimizar, ou destruir o formulário.
OnActivate: O formulário passou a ser a janela ativa (recebeu o foco).
OnDeactivate: O formulário passou a ser uma janela inativa.
OnResize: Quando o usuário altera o tamanho do formulário.
OnPaint: Útil para desenho de gráficos.
Outros Eventos
Os controles que podem receber o foco de teclado têm eventos que são acionados quando o foco de teclado muda. O
evento OnEnter é acionado quando o controle recebe o foco de teclado, depois de um clique do mouse, por exemplo, ou
depois que o usuário tecla [Tab] a partir do controle anterior. Já o evento OnExit é acionado quando o controle perde o
foco de teclado, depois que o usuário passa para outro controle por exemplo.
Controles de edição e similares possuem um evento OnChange. Esse evento é acionado quando ocorre uma modificação
no conteúdo do texto, por exemplo, acrescentar ou apagar um caractere.
Detalhes do Tratamento de Eventos
Vamos retornar ao projeto CALCP. Acione o menu File|Open... ou o ícone da SpeedBar e
abra o CALCP.DPR.

Tratando um Evento para Vários Controles: OnKeyPress


Como vimos, existem três eventos relacionados ao teclado, que permitem interceptar cada tecla digitada
pelo usuário. Podemos usar o evento OnKeyPress para validar a digitação e só permitir digitação de
números.
Nota: existem formas bem mais fáceis de fazer validação de teclas no Delphi, como usar o componente
MaskEdit da página Additional, mas faremos dessa forma apenas para fins de aprendizagem.
Mas o tratamento deve ser o mesmo para os dois componentes Edit. Isso quer dizer que os dois acionarão
um mesmo procedimento de evento.

Selecione no formulário os controles 'editOperando1' e 'editOperando2'. No Object Inspector, abra


a página de eventos, clique em OnKeyPress e clique duas vezes no valor do evento.
Lembre-se: para selecionar dois controles, clique no primeiro, segure [Shift] e clique no outro.
O Delphi vai criar um procedimento da forma:
procedure editOperando1KeyPress(Sender:TObject;
var Key: Char);
begin

end;
O nome do procedimento ("editOperando1KeyPress") é baseado no primeiro controle, mas isso não
importa. Quando você cria um procedimento de evento dessa forma, ele fica associado a todos os
componentes selecionados e vai ser executado quando o evento acontece com qualquer um deles — no
caso, quando o usuário digita uma tecla em um deles.

O parâmetro "Key" que é passado contém a tecla digitada. Vamos verificar se ela é numérica, e se
não for, rejeitar a tecla:
procedure TForm1.editOperando1KeyPress(
Sender: TObject; var Key: Char);
begin
if (Key < '0') or (Key > '9') then
Key := #0; { rejeita a tecla }
end;
O caractere #0 não é a mesma coisa que '0'. O primeiro é o chamado caractere nulo (código ASCII 0)
que não corresponde a nenhum caractere imprimível. O outro é o dígito zero (código ASCII 48). Quando
o procedimento altera 'Key' para o valor #0, isso faz com que seja cancelada a digitação.
Execute o programa e verifique que apenas caracteres numéricos aparecem. Outros são
simplesmente ignorados. Mas existe um problema: a tecla [Backspace] não funciona mais. Isso acontece
porque [Backspace] gera um evento OnKeyPress, com o código ASCII 8 (caractere #8). Para corrigir
isso, altere o if para o seguinte:
if ((Key < '0') or (Key > '9')) and (Key <> #8) then
Key := #0; { rejeita a tecla }

Renomeando um Procedimento de Evento


O nome de um procedimento de evento aparece no código depois de "procedure TForm1.". Esse nome é
criado pelo Delphi juntando o nome do componente que foi selecionado para tratar eventos, com o nome
do evento (sem o "On"). Mas quando um mesmo procedimento é acionado por vários eventos (ou
componentes) é melhor mudar esse nome. Quando você percorre o arquivo da unidade, o nome ajuda a
reconhecer a função daquele procedimento.

Para mudar o nome, selecione qualquer um dos dois componentes, clique no Object Inspector, no
evento OnKeyPress. Substitua o nome atual "editOperando1KeyPress" por "TratarTecla" e tecle [Enter].
Depois clique duas vezes nesse nome e verifique que na unidade, o Delphi renomeou o procedimento:
procedure TForm1.TratarTecla(Sender: TObject;
var Key: Char);
begin
if ((Key < '0') or (Key > '9')) and (key <> #8) then
Key := #0; { rejeita a tecla }
end;
Você não deve mudar o nome diretamente no texto da unidade. Se fizer isso, o Delphi não conseguirá
localizar mais o procedimento e mostrará várias mensagens de erro.

Associando um Procedimento Existente


Se depois de criar um procedimento, você criar um novo componente, ou se você esqueceu algum
componente de fora ao criar o procedimento, você pode ligá-lo a um procedimento já existente.

Selecione no formulário apenas o componente 'editResultado' (clique nele sem usar o [Shift]).
Clique no evento OnKeyPress e no botão de seta . Aparece na lista o nome do procedimento de
evento "TratarTecla". Selecione esse nome. Basta isso para associar o código dos procedimentos com os
eventos correspondentes do controle.

Nesse caso, não é útil associar esse procedimento, porque o "editResultado" não aceita digitação (é
somente leitura). Clique no evento OnKeyPress novamente e apague o nome do procedimento. Isso
desfaz a associação.
Eliminando um Procedimento de Evento
Se você quiser eliminar um procedimento de evento, não apague o corpo dele no código. Apague apenas
os comandos entre o begin e o end , é necessário também excluir as declarações de variáveis, se houver.
Quando você compila o programa, o Delphi nota que o procedimento está vazio, e o remove
automaticamente do programa.

Só para teste, clique duas vezes no componente 'editOperando1'. Isso vai criar um procedimento
para o evento OnChange, que é executado quando o conteúdo do controle é alterado. É comum criar um
procedimento por acidente, dessa forma. Agora salve o projeto. Repare que o procedimento vai
desaparecer automaticamente.
Nota: os procedimentos que aparecem na lista são apenas aqueles compatíveis com o evento. Um
procedimento é compatível quando a lista de parâmetros (entre parênteses, no cabeçalho) está de
acordo com o que o evento espera.
Capítulo 7
Usando Vários Formulários

Caixas de Diálogo

Funções de Mensagem

Exemplo: CalcData

Gerenciando os Arquivos do Projeto

Definindo o Formulário Principal


Caixas de Diálogo
Um programa em Delphi geralmente tem um formulário principal, que aparece inicialmente, e com o
qual o usuário interage. A partir desse formulário, o usuário pode abrir outros formulários do programa.
Os formulários auxiliares, chamados a partir do principal, geralmente são caixas de diálogo. Uma caixa
de diálogo é uma janela com a qual o usuário interage e depois retorna ao principal. Algumas funções do
Delphi mostram caixas de diálogo predefinidas e você não precisa criar um formulário para usá-las. Mas
geralmente, quando você precisa de alguma coisa a mais, você deve criar um formulário e utilizá-lo
como uma caixa de diálogo.
Funções de Mensagem
Mensagens Informativas

O procedimento ShowMessage permite mostrar uma mensagem simples para o usuário, contendo
apenas um botão de Ok. O título da aplicação aparece no título da mensagem. Por exemplo, a chamada:
ShowMessage('Erro de digitação no campo Nome. ' +
'Digite novamente.');
mostra uma mensagem como a seguinte (supondo que o título da aplicação é 'Meu Programa'):

Nota: para definir o título da aplicação, abra o menu Project|Options, clique no marcador
"Application", e digite o novo título em Title. Esse título será usado em todas as mensagens de agora em
diante.
Esse procedimento geralmente é usado para mensagens curtas, que não precisam de nenhum retorno do
usuário. Note que se o texto da mensagem é muito longo, você pode dividir a string em partes, usando o
operador "+". Se você precisar de quebras de linha na mensagem, você pode inserir os caracteres de fim
de linha, #13 e #10. Por exemplo:
ShowMessage('Erro de digitação no campo Nome. '#13#10 +
'Digite novamente.');

Mensagens de Confirmação
A função MessageBox, que é um método do objeto Application, mostra uma mensagem com botões que
o usuário pode clicar para responder. De forma geral, a sintaxe para usar essa função é:
variável := Application.MessageBox(mensagem, título, flags);
onde:
mensagem: é o texto da mensagem
título: é o título que aparece na janela de mensagem.
flags: é uma combinação de valores que determina quais botões ou ícones são usados na janela de
mensagem. (Ver abaixo)
variável:recebe um valor inteiro que indica qual botão foi pressionado.
Esse valor pode ser um dos seguintes:
IDOK botão Ok
IDCANCELbotão Cancelar
IDABORT botão Anular
IDRETRY botão Repetir
IDIGNORE botão Ignorar
IDYES botão Sim
IDNO botão Não

O valor de flags pode ser uma combinação de um ou mais dos seguintes valores:
MB_OK 0 só botão Ok (default)
MB_OKCANCEL 1 botões Ok e Cancelar
MB_ABORTRETRYIGNORE2 botões Anular, Repetir e Ignorar
MB_YESNOCANCEL 3 botões Sim, Não e Cancela
MB_YESNO 4 botões Sim e Não
MB_RETRYCANCEL 5 botões Repetir e Cancelar

MB_ICONERROR 16
sinal de erro crítico

MB_ICONQUESTION 32
sinal de pergunta

MB_ICONEXCLAMATION 48
sinal de advertência

MB_ICONINFORMATION 64
sinal de informação
MB_DEFBUTTON1 0 primeiro botão tem o foco (default)
MB_DEFBUTTON2 256segundo botão tem o foco
MB_DEFBUTTON3 512terceiro botão tem o foco

Veremos exemplos dessas funções no próximo projeto.


Exemplo: CalcData
Vamos criar um pequeno programa que permite mostrar um calendário e executa a calculadora do Windows . Crie um
novo projeto. No título do formulário coloque "Menu Principal" e na propriedade Name, troque "Form1", que é o nome
default, por "FormPrincipal".Para o formulário ficar centralizado altere a propriedade Position para "poScreenCenter". Em
outros projetos, não colocamos nome no formulário porque ele não era utilizado dentro do código do programa.

Coloque no formulário três componentes "Button" ( ) , um componente "Edit"( ) e um componente "Label" (

) . Altere as seguintes propriedades:

"Button1"
Name btnCalendario
Caption Calendário
"Button2"
Name btnCalculadora
Caption Calculadora
"Button3"
Name btnFechar
Caption Fechar
"Edit"
Name editData
Text
"Label"
Caption Data

O formulário irá ficar da seguinte forma:

Pedindo Confirmação ao Usuário


Agora vamos tratar o evento OnClick do item do "Button" Fechar . No procedimento de evento digite o seguinte:
if Application.MessageBox(
'Tem certeza que quer sair?', 'Confirmação',
MB_IconQuestion + MB_YesNo) = idYes
then
Close;
Isso chama a função MessageBox, informando o texto da mensagem. O último argumento, no caso 'MB_IconQuestion +
MB_YesNo', diz para mostrar um ícone de pergunta e especifica quais os botões da mensagem (no caso, "Yes" e "No"). A
função retorna um valor indicando qual o botão pressionado. Esse valor pode ser 'idYes' ou 'idNo' no caso. Se for idYes,
executamos o método Close para fechar o formulário. Execute o programa e clique no botão "Fechar".
Notas:Para alternar do formulário para o editor de código pressione a tecla F12.
Então ao clicar no botão "Fechar" irá aparecer a mensagem perguntando se deseja sair do formulário. Mas esta não é a única
forma de fechar o formulário, o usuário pode usar as teclas [ALT+F4] ou clicar no ícone .
Mas ao fechar o formulário, são gerados os eventos, OncloseQuery e o OnClose. No primeiro podemos cancelar a tentativa
do usuário de fechar o formulário, mas no segundo o formulário foi fechado. Para mostrar a mensagem independente do modo
que o usuário irá fechar , coloque o seguinte procedimento no evento OnCloseQuery do formulário "frmPrincipal" :
if Application.MessageBox('Tem certeza que quer sair?', 'Confirmação',MB_IconQuestion
+ MB_YesNo) = idNo then
CanClose := false
A variável CanClose é passada como parâmetro pelo evento OnCloseQuery, ao sair do procedimento se esta variável for
False, quer dizer que você esta interrompendo a tentativa do usuário de fechar o formulário.O default dessa variável é True.
Vamos retirar a mensagem que aperece quando clicar o botão "Fechar", pois se deixarmos como está, se o usuário clicar neste
botão irá aparecer duas mensagens perguntando se deseja fechar o formulário, neste procedimento iremos deixar somente o
método close.

Criando um Novo Formulário

Para criar um novo formulário no projeto, use o item File|New Form no menu do Delphi ou use o botão New Form:
na SpeedBar. O novo formulário vai aparecer como "Form1" e ainda não tem nenhum componente. Altere o seu nome
(Name) para "formCalendario" e o título (Caption) para "Calendário". Altere também a propriedade Position do formulário
para "poScreenCenter".

Coloque o componente "MonthCalendar"( ) que fica na pasta "Win32", altere sua propriedade Name para "MtCData".
Para mostrar mais de um mês é necessário alterar o tamanho deste componente, podemos também alterar o valor da
propriedade Width para 377.
Este componente mostra um calendário. Seu formulário irá ficar como a figura abaixo:

Agora vamos acrescentar dois botões neste formulário, vamos usar o componente "BitBtn" da página "Additional" - é o
primeiro ícone da página, . Esse tipo de componente permite mostrar figuras junto com o texto.

Para o primeiro botão, defina a propriedade Kind [espécie] com o valor 'bkOk'. Isso altera automaticamente as propriedades
Caption (para "Ok"), Default (para True), ModalResult (para 'mrOk') e Glyph (para conter uma imagem correspondente ao
Ok).
Para o outro botão, defina Kind com o valor 'bkCancel'. Isso vai alterar Caption para "Cancel", Cancel para True,
ModalResult para mrCancel, e Glyph para conter o desenho de um "x". Altere Caption para o valor "Cancelar".
A propriedade ModalResult de um botão (de um Button "normal" ou de um BitBtn) é importante com caixas de diálogo.
Quando ModalResult contém um valor diferente de 'mrNone' (o default), o botão fecha o formulário e retorna esse valor para
quem "chamou" o formulário. Esse valor é numérico, mas existem constantes predefinidas para facilitar, como 'mrOk' e
'mrCancel'. A propriedade Default, como já vimos, faz com que o botão seja acionado por [Enter] e a propriedade Cancel faz
com que o botão seja acionado pela tecla [Esc].
Notas: A propriedade no componente "BitBtn" que permite colocar imagem é a propriedade Glyph.

Salvando o Projeto

Salve o projeto nesse ponto para dar nome aos arquivos. O Delphi vai pedir os nomes na ordem segundo formulário,:
formulário principal e projeto. Chame-os respectivamente de:
EXECCALEN.PAS (era "Unit2.PAS", segundo formulário)
EXECPRIN.PAS (era "Unit1.PAS", primeiro formulário)
CALCDATA.DPR (era "Project1.DPR", arquivo de projeto)
Em projetos com mais de um formulário, não usaremos o mesmo padrão de nomes dos outros, que era acrescentar um "P" ao
nome do projeto. É melhor salvar o projeto agora porque vamos precisar dos nomes de unidades do projeto bem definidos para
o próximo passo.

Executando um Arquivo
O botão "Calculadora" vai executar o calculadora do Windows , cujo arquivo é "Calc.exe". No procedimento de evento
OnClick , faça o seguinte:
begin
WinExec('calc.exe', SW_SHOWNORMAL);
end;
A função WinExec é uma função do Windows que executa um programa. Ela recebe como parâmetros o nome do programa e
um valor que indica como a janela do programa deve aparecer. Para isso existe um comando que converte a string em PChar.
Execute o programa e verifique o resultado.
Vamos retornar ao formulário principal, para fazer isto clique no ícone ou [Shift+F12] ou menu View/forms, irá aparecer
a seguinte tela:

Escolha a opção "formPrincipal" e clique em "OK". Ele irá mostrar o formulário principal, no evento OnClick do botão
"Calendário" iremos chamar o formulário "formCalendário". Para mostrar um outro formulário basta usar o método Show ou
o método ShowModal, da seguinte forma:
nome_do_formulário.Show;
{ ou }
nome_do_formulário.ShowModal;
A diferença entre os dois é que ShowModal mostra o formulário de forma modal. Quando uma janela modal está na tela, o
usuário não pode utilizar outras janelas do programa. Ele deve terminar de executar a tarefa atual e fechar a janela modal para
poder retornar à principal. Se uma janela não é modal, o usuário pode alternar livremente entre ela e outras janelas não modais.

Uma janela modal é a interface recomendada para caixas de diálogo,


por isso utilizaremos ShowModal. No código do procedimento criado coloque:
if FormCalendario.ShowModal = mrOk then
editData.Text := DatetoStr(FormCalendario.MtCData.Date);
Primeiro o método ShowModal é chamado e o valor retornado é verificado. Se esse valor for 'mrOk', o botão "Ok" foi
pressionado. Se ele for 'mrCancel', o botão "Cancelar" foi pressionado. Quando o valor é 'mrOk', o componente 'editData' vai
receber a data que foi escolhida , essa valor iremos obter pela propridade Date do componente MtCData no formulário
formCalendario. Repare que isso é feito com a sintaxe:
FormCalendario . MtCData . Date

Mas para esse código funcionar falta uma coisa. Se um formulário precisa acessar outro que está em outra unidade, é
preciso adicionar uma referência à unidade, através de uma cláusula uses. No arquivo da unidade EXECPRIN.PAS, procure a
palavra implementation, e acrescente o seguinte logo depois do {$R *.DFM}:
uses
ExecCalen;
"ExecCalen" é o nome da outra unidade, que contém o formulário "FormCalendario". Se não houvesse essa cláusula, o código
não conseguiria acessar o outro formulário.
Como a propriedade Text do componente "Edit" é do tipo String e a propriedade Date do componente MonthCalendar e do
tipo TDate, temos que converte o conteúdo da propriedade Date para String, por isso foi necessário usar a função StrtoDate.
Execute o programa e clique no botão "Calendário". O segundo formulário aparece como uma janela modal(se você clicar no
formulário principal, não vai conseguir usá-lo). Para retornar ao principal clique no botão "Ok".
Salve novamente o projeto. Mantenha ele aberto no Delphi, pois vamos analisar algumas opções do Delphi utilizando esse
mesmo projeto.
Gerenciando os Arquivos do Projeto
Um projeto grande, com vários formulários, pode ficar difícil de compreender e manter. Para facilitar o
tratamento de projetos, o Delphi tem vários recursos. Note que para utilizar esses recursos com
eficiência, você deve dar nomes descritivos a todos os formulários e unidades.

Usando a Speedbar ou Menus

Para alternar entre os formulários do projeto, clique no botão (Select form from list) da
Speedbar. Ele vai mostrar uma janela com os nomes de todos os formulários (no caso "FormPrincipal" e
"FormCalendario"). Escolha um deles e clique Ok, ou clique duas vezes no nome dele. No teclado, você
pode usar [Shift+F12].

Para ver a lista de unidades disponíveis, em vez de formulários, use o botão (Select unit from
list). Quando você selecionar uma unidade, ela vai aparecer no editor de código. Repare que aparece um
nome a mais, que é o nome do projeto ("CalcData"). Essa opção traz o arquivo de projeto (DPR), que
analisaremos mais tarde. No teclado, você pode usar [Ctrl+F12].
Nota: Se você tentar executar o programa sem fazer essa alteração, o Delphi automaticamente se
oferece para adicionar uma referência à unidade do outro formulário, pedindo uma confirmação.
Depois que as unidades foram abertas, você pode alternar livremente entre elas clicando nas abas do
editor de código (ou usando [Ctrl+Tab]):

E você também pode alternar para uma unidade e, a partir dela, abrir o formulário associado. Para isso

use a tecla [F12] ou o botão (Toggle Form/Unit) da SpeedBar. É claro que se você puder ver os dois
(ou mais formulários) ao mesmo tempo na tela, você pode alternar entre eles com um clique de mouse.
Você também pode adicionar um arquivo ao projeto (como um formulário que você criou em outro
projeto) usando o botão da SpeedBar ou o item de menu Project|Add to project.... Para remover
um arquivo do projeto, use o botão ou o item Project|Remove from project... e selecione qual você
quer remover.
Nota: Quando você adiciona um arquivo ao projeto, o Delphi não faz uma cópia do arquivo. Se outros
projetos estiverem utilizando o arquivo, ele será compartilhado entre eles.
Quando você remove um arquivo do projeto, o arquivo não é excluído do disco. Ele continua no
diretório do projeto e pode ser utilizado novamente no mesmo projeto ou em outros projetos.

Usando o Project Manager


O Delphi também inclui um pequeno utilitário que permite ter uma visão geral do projeto. Para usá-lo,
clique no item View|Project Manager no menu do Delphi. O Project Manager [gerente de projeto] tem
uma aparência como a seguinte:

O Project Manager mostra uma tabela com os nomes de cada unidade do projeto e ao expandir essas
unidades ele mostra o formulário correspondente , a não ser no caso de uma unidade independente. Na
coluna "Path", aparece o diretório do arquivo, isto se não estiver no mesmo diretório do projeto.
Podemos trabalhar com mais de um projeto ao mesmo tempo . Isso é muito útil , se você quer
desenvolver um desenvolver um programa EXE e uma DLL que trabalham em conjunto. Os arquivos
podem ser salvos nos "grupo de projetos", que serve para manter agrupados todos os itens adicionados.
Podemos adicionar um novo projeto e também um projeto já existente, para isso clique com o botão
direito do mouse na palavra ProjectGroup1(caso não tenha mudado o nome do grupo). Com isso irão
aparecer as seguintes opções:
Estas opções são utilizadas para:
Add New Project : Adicionar um novo projeto ao grupo.
Add Existing Project : Caso o projeto já exista , podemos adicioná-lo ao grupo através desta opção.
Save Project Group : Salva todas as alterações feitas neste grupo.
Save Project Group As: Salva as configurações deste grupo com outro nome.
View Project Group source : Para cada grupo criado é gerado um arquivo com a extensão bgp, e neste
arquivo contém todos os projetos associados ao grupo.Esta opção mostra o conteúdo do arquivo cuja
extensão é bgp.
ToolBar: Se está opção estiver marcada , é mostrado uma barra de ferramento onde apareçem as opções:

Status Bar: Mostra o nome e o caminho do arquivo de projeto.


Dockable: Permite encaixar a janela atual em outra janela, como por exemplo, o editor de código, o
object inspector, etc..
Definindo o Formulário Principal
Esse exemplo que criamos possui um formulário principal e um formulário Calendário. O formulário
principal de um projeto é o que aparece inicialmente para o usuário, e a partir do qual os outros são
chamados. Inicialmente o formulário principal é o primeiro Formulário criado no projeto, mas você pode
mudar isso a qualquer momento.

Para mudar, acione o menu Project|Options.... Na página Forms , lista "Main Form", selecione o
nome do formulário que você quer que seja o principal, por exemplo, "FormCalendario" e clique "Ok".
Quando você executar o programa, esse formulário vai aparecer em vez do "FormPrincipal". Depois
retorne para a opção anterior.
Capítulo 8
Estrutura das Unidades

Estrutura Geral das Unidades

O Código Gerado pelo Delphi

Unidades Associadas e Independentes

Compilação de Unidades

Usando uma Unidade

Resolvendo Conflitos de Nomes

Arquivo de Projeto
Quando você cria um formulário no Delphi, automaticamente é criada uma unidade associada, que
contém algum código gerado automaticamente pelo Delphi, além do código que você acrescenta nos
procedimentos de evento. Você também pode criar uma unidade independente, para usar como uma
biblioteca de funções e procedimentos, por exemplo. Neste capítulo veremos como é a estrutura do
código gerado pelo Delphi, e qual a forma geral da estrutura de uma unidade.

Estrutura Geral das Unidades


Todo arquivo de unidade é dividido basicamente em uma seção de interface e uma seção de
implementação. Em geral, o que é declarado na seção de interface (variáveis, constantes, procedimentos
etc.) é visível externamente, em outras unidades. O que for declarado apenas na seção de implementação
é visível apenas para uso dentro da própria unidade:
unit Execprin;

interface
{ itens visíveis externamente }
...
implementation
{ itens para uso interno }
...
end.
O Código Gerado pelo Delphi
Você pode criar suas próprias unidades, mas para começar, vamos ver a estrutura do código que o Delphi gera
automaticamente num arquivo de unidade. No projeto anterior (CALCDATA.DPR) abra a unidade EXECPRIN.PAS, que
está associada ao formulário principal (FormPrincipal). Verifique que o cabeçalho da unidade contém uma linha:
unit Execprin;
A primeira linha contém a palavra unit seguida do nome da unidade (que
corresponde ao nome do arquivo). Depois vem a palavra interface, que inicia a seção de interface da unidade.

Seção de Interface
interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,StdCtrls;

A seção de interface começa com uma cláusula uses, que contém uma lista de nomes de outras unidades que são utilizadas
por esta. Nesse caso esses nomes são de unidades do Delphi (Windows, SysUtils, ...) que compõem a biblioteca do Delphi.
Essa lista é mantida automaticamente, mas você pode acrescentar outros nomes à lista. Após a cláusula uses, você pode
acrescentar declarações de tipos de dados, variáveis, procedimentos e funções.
A primeira declaração, colocada automaticamente, é a da classe de formulário. A classe é uma definição dos componentes
que o formulário contém, e dos procedimentos de evento definidos nele. Nós veremos a classe de formulário mais em
detalhes posteriormente.
type TformPrincipal = class(TForm)
btnCalendario: TButton;
btnCalculadora: TButton;
btnFechar: TButton;
editData: TEdit;
Label1: TLabel;
procedure btnFecharClick(Sender: TObject);
...
private
{ Private declarations }
public
{ Public declarations }
end;

Com a palavra end, termina a definição da classe. Depois o Delphi acrescenta uma declaração de variável para o formulário.
var
FormPrincipal: TFormPrincipal;
Depois dessas declarações, vem a seção de implementação da unidade, que contém o seguinte:
implementation

{$R *.DFM}

uses execcalen;

procedure TformPrincipal.btnFecharClick(Sender: TObject);


begin
....
end;

procedure TformPrincipal.FormCloseQuery(Sender: TObject;var CanClose: Boolean);


begin
....
end;

...

end.
A declaração {$R *.DFM} não é um comentário, apesar de parecer com um. Esse tipo de declaração é chamada de diretiva
do compilador e é uma instrução para o compilador interno do Delphi. No caso, o que ela faz é associar o arquivo de
formulário (EXECPRIN.DFM) com o arquivo de unidade. Essa diretiva é necessária para o funcionamento do programa,
portanto não a remova.
Depois vem o corpo de cada um dos procedimentos de evento, Note que em cada um deles, o nome da classe de formulário
("TFormPrincipal") é incluído como prefixo no cabeçalho do procedimento.
Após todos os procedimentos existe uma linha contendo apenas "end." (end seguido de ponto final). Essa linha sinaliza o
final do arquivo da unidade. O compilador ignora tudo que for digitado depois dessa linha, mesmo sem especificar
comentários.
Unidades Associadas e Independentes
No Delphi, uma unidade associada a um formulário sempre é tratada e utilizada em conjunto com o
formulário, e o código da unidade geralmente está ligado (através de eventos) com os componentes do
formulário.
Mas você pode também criar unidades independentes, que contém apenas rotinas de código e nenhum
componente. Vamos ver alguns exemplos mais tarde, mas o processo de criar uma unidade independente

é simples: selecione o item de menu File|New... no Delphi, clique no ícone Unit e clique Ok. Com
isso, é criada uma unidade contendo apenas o esqueleto das seções, como a seguinte:
unit Unit2;

interface

implementation

end.
Quando você salva o arquivo da unidade, fornecendo um outro nome, o Delphi vai alterar a declaração
no cabeçalho para refletir esse nome.
Compilação de Unidades
O Delphi compila o seu projeto quando você executa o programa, ou quando você aciona
Project|Compile (nome do projeto ativo). Caso queira compilar todos os projetos existente no grupo
escolher Project|Compile All Projects. Para compilar o programa, o Delphi reúne todas as unidades do
projeto e cria um arquivo executável, com o mesmo nome do projeto e extensão EXE. Essa compilação é
feita em várias fases.
Da primeira vez que o projeto é compilado, para cada arquivo de unidade (.PAS), é gerado um arquivo
de unidade compilada (.DCU), e esses arquivos DCU são mantidos no disco. Depois todos os arquivos
DCU, mais os arquivos (RES) e o arquivo DPR são combinados em um único arquivo EXE
independente.
Da próxima vez que o projeto for compilado, apenas as unidades alteradas desde a última compilação
são recompiladas (.PAS -> .DCU). O Delphi verifica isso comparando a data/hora do arquivo PAS com a
data/hora do arquivo DCU correspondente. Se o arquivo fonte for mais recente, ele recompila.
Você pode também forçar o Delphi a recompilar todas as unidades sem verificar a data. Para isso, use o
comando Project|Build (nome do projeto ativo) , caso queira recompilar todos os projetos pertencentes
ao grupo escolher Project|Build All Projects.
O processo de compilação pode ser resumido com o diagrama:
Usando uma Unidade
Uma unidade sempre é utilizada por outra unidade (ou pelo programa principal, como veremos). Na
cláusula uses, são especificadas quais as unidades utilizadas. Por exemplo:
unidade FRMTESTE.PAS:
unit FrmTeste;

interface
uses Forms, Controls,
... Rotinas; {nome da outra unidade}
...

implementation

procedure...
begin
Contador := 30;
AumentaCont;
end;

...
end.
unidade ROTINAS.PAS:
unit Rotinas;

interface

var Contador: integer;

procedure AumentaCont;

implementation
...

end.
Quando uma unidade (FrmTeste), utiliza outra (Rotinas), ela passa a ter acesso a todos os identificadores,
como variáveis e procedimentos, que estão declarados na seção de interface da outra (ela não tem acesso
a identificadores declarados apenas na seção de implementação da outra).
Existem dois lugares onde pode ser colocada a cláusula uses: no início da seção de interface, ou no início
da seção de implementação. Os identificadores da outra unidade só se tornam visíveis a partir do ponto
onde a outra unidade foi referenciada. Se você colocar a referência à outra unidade na cláusula uses da
interface, então os identificadores da outra podem ser usados já na parte de interface. Se você referenciar
a outra unidade na cláusula uses da parte de implementação, os identificadores só podem ser usados a
partir da parte de implementação.
Se duas unidades dependem uma da outra através da seção de interface, então o Delphi recompila a
unidade dependente (gera o arquivo .DCU) sempre que a outra é alterada. Nesse exemplo, toda vez que a
unidade ROTINAS é alterada, a unidade FRMTESTE, que depende dela, é recompilada primeiro.
Duas unidades podem se "utilizar" mutuamente, isto é, pode haver também uma cláusula uses em
ROTINAS, que referencia FRMTESTE. Mas se ambas as referências estiverem na parte interface das
unidades, o Delphi não consegue determinar a ordem de compilação das duas, e gera uma mensagem de
erro ("Circular unit not allowed"). Por isso, é sempre melhor acrescentar a referência para a outra
unidade na seção implementation.
Só é realmente necessário colocar a referência na interface quando você precisa usar algo da outra
unidade ainda dentro da parte interface (só acontece no caso de constantes e tipos de dados que sejam
usados por outros tipos de dados ou constantes).
Resolvendo Conflitos de Nomes
Pode acontecer de você ter um identificador que tem o mesmo nome na unidade atual e em outra que está
sendo utilizada. Isso gera uma ambiguidade quando você usa o nome. Por exemplo, suponhamos que
FRMTESTE.PAS contenha uma variável chamada 'Contador', e utiliza a unidade ROTINAS.PAS, que
também contém uma variável chamada 'Contador'. Se você colocar na unidade FRMTESTE um
comando:
Contador := 34;
o Delphi vai dar prioridade à variável da unidade atual ('Contador' em FRMTESTE). Mas para acessar a
variável da outra unidade (ROTINAS), você pode usar um identificador qualificado, isto é, prefixado
com o nome da outra unidade, por exemplo:
Rotinas.Contador := 34;
Qualquer identificador pode ser usado dessa forma para resolver o conflito
de nomes.
Arquivo de Projeto
Uma parte importante do seu programa, apesar de raramente você precisar trabalhar diretamente com ela,
é o arquivo de projeto (.DPR). Quando você abre o projeto, o Delphi usa o arquivo DPR para localizar
todos os outros arquivos utilizados (formulários, unidades associadas e unidades independentes). O
arquivo DPR na verdade contém código executável gerado pelo Delphi, mas que pode ser personalizado.
Para abrir o arquivo DPR, por exemplo no projeto CALCDATA, use o item Project|View Source, no
menu do Delphi. Você vai ver no editor de código algo como o seguinte:
program calcdata;

uses Forms,
execprin in 'execprin.pas' {formPrincipal},
execcalen in 'execcalen.pas' {formCalendario};

{$R *.RES}

begin
Application.Initialize;
Application.CreateForm(TformPrincipal, formPrincipal);
Application.CreateForm(TformCalendario, formCalendario);
Application.Run;
end.
Este é o programa principal do projeto, a partir do qual os formulários e outras unidades são chamadas.
A primeira linha, program CalcData; indica que será gerado um programa executável e contém o nome
do projeto.
Depois vem uma cláusula uses, listando os nomes de todos os arquivos de unidades que compõem o
projeto. A cláusula in especifica o nome do arquivo de unidade, que pode estar em um outro diretório. Se
um diretório não é especificado, o arquivo é procurado no mesmo diretório do arquivo DPR.
A diretiva de compilação {$R *.RES} diz ao compilador para reunir ao executável todos os arquivos com
extensão RES. Esses arquivos são gerados a partir dos arquivos de formulário (.DFM).
Entre o begin e o end está o código executado no início da execução do programa. Para cada formulário
do projeto, o Delphi acrescenta uma chamada a 'Application.CreateForm', que cria um objeto de
formulário baseado na classe de formulário. O primeiro da lista será o formulário principal do programa.
A última linha, 'Application.Run', executa o laço de processamento de eventos do Delphi.
Quando você usa comandos de manutenção de projeto, o Delphi automaticamente atualiza o arquivo
DPR. Raramente você precisa alterar o arquivo DPR manualmente, mas você pode, por exemplo,
acrescentar comandos dentro do begin e end para fazer alguma inicialização do programa mesmo antes
que o formulário principal seja mostrado.
Capítulo 9
Outros Tipos de Dados e Depuração

Tipos Enumerados

Tipos Faixa

Vetores

Exemplo: Cálculo de Média

Conjuntos

Depuração
Tipos Enumerados
Um tipo enumerado é composto por uma lista seqüencial de valores simbólicos. Uma variável desse tipo
pode assumir qualquer um desses valores. Para criar um tipo enumerado, usa-se uma declaração type:
type
EstadoCivil = (Solteiro, Casado, Desquitado, Viuvo);
Após criar um tipo de dados, podem ser criadas variáveis desse tipo, com uma declaração de variáveis
normal:
var
estCiv: EstadoCivil;
A variável 'estCiv' só vai poder assumir um dos valores do tipo: Solteiro, Casado, etc. Esses valores não
são numéricos e não são strings de texto. São apenas constantes simbólicas que representam uma
informação. Por exemplo:
estCiv := Casado;
...
if estCiv = Viuvo then
begin
...
end;
Tipos enumerados melhoram a legibilidade do programa, evitando que você precise codificar uma
variável com valores numéricos (p.ex.: 0 para Solteiro, 1 para Casado, etc.). Mas eles são manipulados de
forma eficiente pelo Object Pascal, pois internamente são tratados como constantes numéricas.

Operações com Ordinais


Um tipo enumerado também é um tipo ordinal, como os tipos inteiros, o tipo Char e o tipo Boolean. Por
isso, também se aplicam a ele todas as operações que podem ser feitas com tipos ordinais, vistas no
Capítulo sobre Variáveis e Tipos de Dados e outros:
· Para obter o próximo elemento da seqüência: usa-se a função succ. Por exemplo:
estInicial := Casado;
estCiv := succ(estInicial); {o valor será Desquitado}
· Para obter o elemento anterior: usa-se a função pred. Por exemplo: pred(Casado) retorna o valor
Solteiro.
· Para saber a posição do elemento na seqüência: usa-se a função ord. Ela retorna um valor indicando a
posição do elemento na lista, e pode ser usada para converter um valor enumerado em numérico. O
primeiro elemento tem a posição zero. Por exemplo: ord(Solteiro) = 0, ord(Casado) = 1, ord(Desquitado)
= 2 e ord(Viuvo) = 3 e ord(estCiv) retorna um valor dependendo do valor atual da variável.
· Para percorrer uma faixa de elementos: pode ser usado o comando for (visto em Estutura de Controle)
para fazer um laço:
for estCiv := Solteiro to Viuvo do
...
Além disso, tipos ordinais podem ser usados para índices de vetor (como veremos adiante) ou para criar
conjuntos (como veremos adiante) e podem ser usados com o comando case que foi visto anteriormente.

Tipos Enumerados no Delphi


A própria VCL do Delphi utiliza tipos enumerados em vários lugares. A propriedade BorderStyle do
formulário, por exemplo, é de um tipo enumerado (TFormBorderStyle) que contém quatro valores
possíveis. A declaração desse tipo, no código fonte da VCL, é:
type
TFormBorderStyle = (bsNone, bsSingle, bsSizeable, bsDialog);
Cada valor possível representa um estilo de borda do formulário, como vimos antes. Se BorderStyle
fosse uma propriedade numérica em vez de enumerada, os valores possíveis teriam que ser codificados
como números (0,1,2,3) e isso tornaria difícil a escrita e manutenção do programa. Se eles fossem
codificados como strings de texto ('bsNone' etc.), seria muito ineficiente o armazenamento e manipulação
dos valores.
Tipos Faixa
Se você sabe que determinada variável só pode pertencer a uma faixa de valores definida, pode utilizar
um tipo faixa para que o próprio Delphi se encarregue de verificar isso automaticamente. Um tipo faixa é
uma restrição sobre os valores de um tipo ordinal qualquer. Por exemplo:
type
EstadoCivil = (Solteiro, Casado, Desquitado, Viuvo);
DiaMes = 1..31;
LetraMaiuscula = 'A'..'Z';
EstadoNaoViuvo = Solteiro..Desquitado;
O tipo faixa é declarado com o valor inicial e final da faixa, separados por '..' (um ponto seguido de
outro). Os valores inicial e final devem pertencer a um tipo ordinal: inteiro, Char, ou enumerado. Depois
de declarar o tipo, você pode criar variáveis baseadas no tipo:
var
dm: DiaMes;
letra1, letra2: LetraMaiuscula;
est1: EstadoNaoViuvo;
x:integer;
Qualquer atribuição a uma variável do tipo faixa é verificada, para saber se o valor está dentro dos
limites. Por exemplo, os seguintes comandos geram erro de execução, com uma mensagem "Range
Check Error":
x := 32;
dm := x; {erro: 32 está fora da faixa}
letra1 := Chr(32); {erro: ' '(espaço) está fora de faixa}
estCivil := Viuvo;
est1 := estCivil; {erro: Viuvo está fora da faixa}

Opção "Range Checking"


Na verdade, nem sempre o Delphi verifica se um valor está dentro da faixa. Ele só faz isso quando a
opção "Range Checking" [verificação de faixa] está ativa nas opções do projeto. Para alterar essa opção,
você deve acionar o menu Project|Options... e, na página "Compiler", marcar ou desmarcar o item
"Range Checking".
Quando essa opção está marcada, ao compilar o programa o Delphi gera código para cada atribuição de
variável do tipo faixa, garantindo que ela não vai receber um valor fora da faixa. Mas esse código gerado
aumenta o tamanho do programa e pode também reduzir a velocidade de execução. Geralmente você
deve compilar com "Range Checking" ativo apenas durante o desenvolvimento do programa. Depois que
o programa já tenha sido testado, quando for gerar a versão final, desative essa opção para aumentar a
eficiência da execução.
Vetores
Um vetor (também chamado matriz) é uma variável que contém vários elementos do mesmo tipo de
dados. Cada elemento possui um índice que identifica esse elemento no vetor. Podemos criar os vetores
de duas formas : Estática e Dinâmica. Um vetor estático tem um tamanho definido, enquanto que um
vetor dinâmico não tem tamanho declarado e seu tamanho pode ser mudado durante a execução do
programa.

Vetores Estáticos
Para criar um tipo de dados que representa um vetor, usa-se uma declaração array, informando o tipo do
índice e o tipo dos elementos do vetor (ou tipo base). Por exemplo:
type
TVetor = array [1..30] of double;
Essa declaração cria um tipo de vetor, com índices na faixa de 1 a 30 (um tipo faixa) e elementos do tipo
Double. Esse tipo pode ser usado para declarar uma ou mais variáveis:
var
vetA: TVetor;
vetB, vetC: TVetor;
ind, x: integer;
Cada variável declarada é um vetor com 30 elementos do tipo Double, e seus elementos individuais
podem ser acessados informando o índice do elemento entre colchetes:
begin
vetA[3] := 1000;
x := vetA[5];
ind := 10;
vetB[ind] := x;
end;
Na verdade não é necessário definir um tipo, quando você só precisa de uma variável daquele tipo. Você
pode inserir a definição do tipo na própria declaração de variável:
var vetA: array [1..30] of double;

Verificação de Faixa para Vetores


O índice do elemento pode ser constante ou variável. De qualquer forma, esse índice deve estar na faixa
definida pelo tipo do índice. Se o índice for uma variável, o Delphi vai verificar em tempo de execução
se a variável está na faixa, mas apenas se a opção "Range Checking" estiver ativa, a mesma opção que
controla verificações de tipos faixa.
Se "Range Checking" estiver desativado, qualquer valor é admitido como índice, e com isso, é possível
acessar posições de memória fora do limite do vetor, com resultados imprevisíveis. Essa posição de
memória pode estar num espaço ocupado por outra variável, e nesse caso essa variável será modificada
para conter um valor inválido, causando erros em outras partes do programa.
Ou a posição pode estar numa área de memória controlada pelo Windows. Como o Windows restringe o
acesso à memória apenas às áreas reservadas ao programa, ele vai interceptar essa tentativa de acesso e
terminar o programa. Geralmente, antes de terminar, o Windows mostra uma mensagem ("Este aplicativo
provocou um erro e será finalizado"). Às vezes o Windows permite ao usuário ignorar o erro e continuar,
mas na maioria das vezes, a única opção é finalizar a execução.

Usando Outros Tipos de Índice


O tipo do índice do vetor é um tipo faixa, e por isso usa a mesma sintaxe. O índice não precisa começar
em um ou zero, por exemplo:
type

TVetorInt = array [-3..3] of integer;


TVetorBool = array [0..10] of boolean;
O nome do tipo não precisa começar com "T", mas isso é uma
convenção bastante utilizada para tipos de dados.
Você pode também definir um tipo faixa antes e depois utilizá-lo em um vetor:
type
DiaMes = 1..31;
TPresenca = (Presenca, Falta, Feriado);
TVetPresenca = array [DiaMes] of TPresenca;
Como o índice pode ser qualquer tipo ordinal ou tipo faixa, ele pode ser do tipo Char, Boolean ou
enumerado, por exemplo:
type
TFreqLetras = array ['A'..'Z'] of longint;
var
freq: TFreqLetras;
letra: Char; maior: Longint;
begin
for letra := 'A' to 'Z' do
freq[letra] := 0;
maior := freq['A'];
...
end;
Outros exemplos são:
type TVetIndBool = array [False..True] of integer;
TVetIndEnum = array [Solteiro..Viuvo] of string;
Vetores Dinâmicos
Para criar um vetor dinâmico também temos que utilizar a declaração array, informando o tipo dos
elementos do vetor (ou tipo base). A diferença do vetor estático é que não informamos os tipo do índice
e o seu tamanho. Essa informação é fornecida em tempo de execução através do procedimento
SetLength. O índice inicial do vetor é sempre zero.
var
Vetor : array of double;
N:integer;
begin
N := 10;
SetLength(Vetor, N); // muda o tamanho do vetor
Vetor[5] := 5; // ele tem
elemento de 0 a 9
end;
Para redefinir o tamanho do vetor , usa-se a propriedade Setlength novamente com o novo tamanho que
deseja . Por exemplo: continuando o exemplo anterior acrescente as seguinte linhas:
SetLength(Vetor, 20); // Redefine o tamanho para 20
Vetor[20] := 5;
O conteúdo do vetor na posição 20 é 5.

Vetores Multidimensionais
Um vetor também pode ter mais de uma dimensão, permitindo representar um vetor de vetores. Nesse
caso, cada elemento possui dois ou mais índices. Um vetor multidimensional pode ser criado definindo
vários tipos de índices:
type
TMatriz = array [1..10, 1..30] of double;
TMarcador = (marcaX, marcaO, marcaNada);
TJogoDaVelha = array [0..2, 0..2] of TMarcador;
TCubo = array [1..10, 1..10, 1..10] of integer;
var
m, n: TMatriz; i,j,k:integer;
jogo: TJogoDaVelha; lin,col:integer;
cubo: TCubo;
begin
{ para acessar um elemento, usa-se dois índices }
m[i,j] := 100.0;
for lin := 0 to 2 do
for col := 0 to 2 do
jogo[lin, col] := marcaNada;
cubo[i,j,k] := 0; {três índices}
end;
Podemos também criar vetores multidimensionais dinâmicos. A idéia é a mesma do anterior, mas não
informamos o tamanho do vetor durante a definição, seu tamanho é definido durante o tempo de
execução utilizando também o método SetLength e além disso é permito criar para cada linha um
número diferente de elementos, por Exemplo:
var
Matriz : array of array of double;
begin
setLength (Matriz, 2); // muda o tamanho do vetor multidimensional
setLength (Matriz[0], 2);
setLength (Matriz[1], 5);
Matriz[0,1] := 3;
Matriz[1, 4] := 5
end;
Exemplo: Cálculo de Média
Para ver na prática como utilizamos vetores, vamos criar um novo projeto. O programa vai
permitir que o usuário informe uma lista de valores, e depois calcule a média dos valores fornecidos. No
formulário altere a propriedade Caption para "Calcula Média" e crie componentes no formulário
conforme o seguinte:

Crie um rótulo (componente Label) com Caption "Número:" e ao lado dele um componente Edit
com nome "editNumero" , coloque a propriedade Text vazia . Crie os botões como mostrado, e mude
seus nomes, respectivamente para 'btnAdicionar', 'btnLimpar' e 'btnCalcular'. Ao lado do rótulo "Média:",
coloque um componente Edit que vai apenas mostrar o resultado. Defina seu nome como 'editMedia'.
Coloque sua propriedade Text vazia.Altere sua propriedade ReadOnly para True, o que vai proibir a
edição do valor. Altere também a propriedade Color escolhendo o valor 'clBtnFace'. Com isso, ele vai
ficar da mesma cor do formulário e dos botões, o que fornece ao usuário uma indicação visual de que ele
não pode ser editado. Para o 'btnAdicionar', faça a propriedade Default = True, para que ele possa ser
"pressionado" usando a tecla [Enter].

Declarando o Vetor

Antes de fazer qualquer tratamento de eventos, vamos adicionar na unidade uma declaração para o
tipo do vetor e uma declaração de variável desse tipo. Pressione [F12] para abrir o arquivo da unidade.
No início da seção de implementação, após a palavra implementation, digite o seguinte:
type
TVetorInt = array [1..3] of double;
var
Valores: TVetorInt;
ContValores: integer;
A variável 'Valores' é um vetor do tipo 'TVetorInt', ou seja, um vetor com elementos do tipo 'double' e
índices de 1 a 3. A variável 'ContValores' será um contador usado para especificar quantos elementos do
vetor estão sendo usados no momento. A cada vez que for clicado o botão "Adicionar", essa variável será
incrementada.
Adicionando um Elemento

No código do botão 'btnAdicionar', vamos adicionar o número que foi digitado (em 'editNumero')
ao vetor. A cada clique, vamos também incrementar o contador. Digite o seguinte:
procedure TForm1.btnAdicionarClick(Sender: TObject);
var numero: double;
begin
numero := StrToFloat(editNumero.Text);{conv. p/ inteiro}
ContValores := ContValores + 1; {increm. contador}
Valores[ContValores] := numero; {guarda o elemento}
{ Facilita a digitação do próximo número }
editNumero.Clear;
editNumero.SetFocus;
end;
Repare que depois de adicionar o elemento, o programa limpa o controle 'editNumero', usando o método
Clear. Isso facilita que o usuário digite
seqüencialmente os números. Também para facilitar, o programa devolve o foco de teclado para o
controle, no caso de ele ter usado o mouse para clicar no botão. Isso é feito com o método SetFocus.

Limpando a Lista

O botão 'btnLimpa' vai "limpar" a lista de valores. Na verdade não precisamos percorrer todos os
elementos do vetor, apenas zerar o contador, para indicar que não há nenhum elemento guardado. Faça o
seguinte:
procedure TForm1.btnLimparClick(Sender: TObject);
begin
ContValores := 0;
end;

Calculando a Média dos Elementos

O botão 'btnCalcula' vai fazer a tarefa de calcular a média dos elementos do vetor. Para isso, temos
que primeiro achar a soma dos elementos e depois dividir a soma pelo contador de elementos. Digite o
seguinte, por enquanto:
procedure TForm1.btnCalcularClick(Sender: TObject);
var
soma: double;
i: integer;
begin
soma := 0.0;
for i := 1 to ContValores do
soma := soma + Valores[i];
Note que o laço for é útil para percorrer elementos de um vetor. No caso, o valor inicial é sempre 1
e o valor final é variável, determinado por 'ContValores'. Após fazer a soma, devemos fazer uma divisão
dessa soma pelo contador. Mas antes, para evitar um erro de execução, vamos verificar se o contador é
zero:
if ContValores = 0 then
editMedia.Text := 'Erro'
else
editMedia.Text := FloatToStr(soma/ContValores);
end;
Note que se 'ContValores' inicialmente for zero, o laço for não fará nada (o valor final é menor do que o
inicial).

Testando o Programa

Execute o programa. Digite um número e clique no "Adicionar", ou tecle [Enter]. Repita o


processo para adicionar alguns números à lista. (Repare que, como o programa não faz ainda verificação
de erro, se você digitar um valor não numérico e clicar em Adicionar, vai ver uma mensagem de erro do
Delphi). Depois clique em "Calcular" para calcular o resultado. Se você digitar, por exemplo, 13, 28 e 34,
o resultado final será: 25.

No menu Project|Options na pasta Compiler se a opção RangeCheck estiver marcada , desmarque


esta opção e pede para recompilar todo o projeto novamente (Project|Compile Mediap). Com isso se
pedir para informar mais de 3 números para ser adicionados a média , o Delphi não irá fazer a verificação
se ultrapassou o tamanho do vetor, seu exemplo pode até continuar funcionando, vai depender da área de
memória que foi utilizada para acrescentar esses valores que foram colocados a mais, para que ocorre
esta verificação a opção RangeCheck tem que estar marcada.

Salvando o Projeto

Vamos retornar a esse projeto mais tarde, portanto salve-o como MEDIA.PAS (unidade) e
MEDIAP.DPR (projeto).
Conjuntos
O tipo de dados conjunto representa a noção matemática de conjuntos, permitindo fazer todas as
operações padrão de conjuntos. Um conjunto pode conter elementos de qualquer tipo ordinal (ou faixa).
Para criar um tipo conjunto, usa-se a declaração set:
type
TConjCarac = set of char;
TConjLetra = set of 'A'..'Z';
TConjDia = set of 1..31;
TConjEstado = set of EstadoCivil;
Para atribuir um valor (constante) a uma variável conjunto, especifica-se os elementos do conjunto entre
colchetes:
var
vogais: TConjLetra;
diasUteis: TConjDia;
begin
vogais := ['A','E','I','O','U'];
diasUteis := [1..10, 13, 15, 20..23];
Note que uma faixa de valores pode ser colocada, para evitar enumerar todos os valores.

Verificando Elementos
Você pode verificar se um elemento pertence a um conjunto, com o operador in, que equivale em
matemática à operação x c. Por exemplo:
if caractere in vogais then
texto := 'Vogal'
else
texto := 'Consoante';

if dia in diasUteis then ...

if estCivil in [Desquitado, Viuvo] then ...


Esse tipo de teste é mais eficiente do que uma série de ifs, além de mais legível.

Vamos abrir o projeto CALCP para fazer uma alteração semelhante em um dos testes. No código
associado ao evento OnKeyPress, do 'editOperando1', existe um if que testa os valores possíveis da
variável 'Key':
if ((Key < '0') or (Key > '9')) and (Key<> #8) then
Key := #0; { rejeita a tecla }

Vamos alterar esse if para utilizar um conjunto. Substitua pelo seguinte:


if not (Key in ['0'..'9', #8]) then
Key := #0;
ou seja, se a tecla não estiver no conjunto formado por '0'..'9', mais o backspace (#8), ela será rejeitada.
Esse teste é bem mais simples e mais eficiente do que o teste anterior.

Operações com Conjuntos


Suponhamos duas variáveis do tipo set of integer (conjunto de inteiros), recebendo os seguintes valores:
A := [1, 2, 3, 5, 7, 11];
B := [1, 3, 5, 7, 9];
Você pode fazer vários tipos de operações com variáveis conjunto, que são as seguintes:

· União: C = A B. O resultado da união é um conjunto que contém todos os elementos de ambos os


conjuntos. A união é feita com o operador +. Por exemplo:
C := A + B; { o resultado será [1, 2, 3, 5, 7, 9, 11] }

· Interseção: C = A B. O resultado da interseção é um conjunto que contém um elemento em comum


entre os dois conjuntos. A interseção é feita com o operador "*", por exemplo:
C := A * B; { O resultado será [1, 3, 5, 7] }
· Diferença: C = A - B. O resultado de A menos B é o conjunto com os elementos de A que não existem
em B. A diferença é feita com o operador "-":
C := A - B; { O resultado será [2, 11] }
C := B - A; { O resultado será [9] }

· Contido: A B (A está contido em B) ou B A (B contém A). Verifica se todos os elementos de um


conjunto pertencem também ao outro conjunto. No Object Pascal, o operador (está contido) é
representado por <= e (contém) por >= Por exemplo:
{verifica se os elementos 1 e 3 pertencem ao conjunto A}
if [1,3] <= A then ...
{verifica se B contém A}
if B >= A then ...

Propriedades de Conjunto
Algumas propriedades de componentes do Delphi utilizam tipos de conjunto. Por exemplo, a propriedade
BorderIcons de um formulário contém um conjunto de valores, onde cada um determina se o formulário
terá ou não um determinado item da borda. Essas propriedades aparecem no Object Inspector com um
sinal de "+" e o seu valor aparece com a sintaxe de conjunto. Quando você clica duas vezes no nome da
propriedade, aparecem subitens, que permitem adicionar ou remover elementos do conjunto.
Para atribuir um valor para este tipo de propriedade no programa, você deve usar a sintaxe de conjunto,
por exemplo:
BorderIcons := [biSystemMenu, biMinimize, biMaximize];
Para alterar o valor, por exemplo, adicionando um elemento, você deve usar os operadores de conjuntos:
BorderIcons := BorderIcons + [biMinimize];
Para verificar se um elemento está presente, você deve usar o operador in:
if biMaximize in BorderIcons then ...
Depuração
Para encontrar as causas de uma exceção, ou para detectar erros de lógica no programa (erros que só
você pode saber que estão acontecendo), você pode usar os mecanismos de depuração do Delphi. O
processo de depuração [debugging] consiste em analisar o funcionamento do programa durante e
execução, vendo os valores de variáveis, o fluxo da execução, e assim por diante.

Colocando um Ponto de Parada


Para começar a depuração do programa, você deve escolher um ponto inicial onde você vai entrar em
modo de depuração. Para isso coloque um ponto de parada [breakpoint] na linha onde você quer
começar.

No nosso caso, coloque o cursor na primeira linha após o begin, a que contém soma := 0. Para
colocar ou retirar um ponto de parada você pode usar a tecla [F5], ou clique na margem esquerda do
editor, à esquerda da linha. Em qualquer caso, o editor de código mostra a linha destacada em vermelho e
um marcador vermelho à esquerda indicando um ponto de parada.

Execute o programa, adicione uns dois ou três números e depois clique em "Calcular". Quando o
Delphi encontra o ponto de parada, ele pára a execução e destaca a linha atual. A janela principal do
Delphi agora deve mostrar no título "Delphi - Mediap [Stopped]". A palavra stopped [parado] significa
que você está em modo de depuração, com a execução parada na linha atual. Para fazer os próximos
testes, você pode precisar executar várias vezes o mesmo procedimento de evento. Depois da primeira
vez, basta clicar no botão Calcular novamente.

Nota: você também pode colocar um ponto de parada com o menu Run|Add Breakpoint|Source
Breakpoint. Nesse caso, você pode especificar uma condição de parada (Condition) e quantas vezes a
linha deve ser executada antes de fazer uma parada (Pass count).
Nota: as linhas que podem ter breakpoints estão marcadas com pontos azuis. As outras não estão
relacionadas a código executável.

Executando Passo a Passo


Quando você está em modo de depuração, você tem várias opções. Se quiser continuar a execução
normal, tecle [F9]. A janela do Delphi vai mudar para "Delphi - Mediap [Running]" indicando modo de
execução normal.

Você pode, ao invés disso, acompanhar passo a passo a execução do programa. Execute
novamente até chegar ao ponto de parada. Depois tecle [F7] ou [F8] (nesse caso tanto faz) para executar
essa linha e passar para a próxima. A cada vez que você teclar [F7] ou [F8], você vai executar uma linha
e o triângulo que aparece à esquerda se move indicando a próxima linha que será executada.
Com isso você pode acompanhar o laço for, por exemplo. O comando dentro do for vai ser executado
quantas vezes forem os números que você adicionou com o botão Adicionar.
Essas teclas também equivalem a itens de menu do Delphi ou botões da SpeedBar:
TeclaBotãoItem de Menu
[F7] Run|Trace Into
[F8] Run|Step Over

A diferença entre "Trace Into/F7" e "Step Over/F8" só é importante quando você passa por uma chamada
de procedimento. "Trace Into" entra no procedimento e executa cada linha dele passo a passo também.
Isso pode ser o que você quer, mas em alguns casos um procedimento já foi totalmente testado e você
não precisa acompanhar sua execução de novo. Nesse caso, use "Step Over", que executa o procedimento
inteiro em modo de execução normal e depois volta ao modo depuração, depois da chamada do
procedimento.

Executando Até o Cursor


Às vezes, durante a depuração você quer "pular" para uma determinada linha diretamente, executando
todo um trecho em velocidade normal de execução. Por exemplo, durante a execução, você pode querer
ir diretamente ao fim do laço for, em vez de acompanhar cada passagem.

Para isso coloque o cursor na linha onde quer parar (por exemplo, no if depois do for) e use o item
de menu Run|Run To Cursor ou sua tecla de atalho, [F4].
Nota: se a execução nunca chegar à linha do cursor, o programa entrará em modo de execução normal
("Running") e não voltará automaticamente ao modo de depuração.

Vendo Valores de Variáveis


Em modo de depuração, você pode ver qual o valor atual de uma variável ou, até mesmo, alterar o valor
da variável, afetando o funcionamento do programa da forma que você quiser. Para isso, basta passear o
cursor por cima do texto do programa. Quando o cursor estiver em cima do nome de uma variável, o
valor desta será mostrado. Esse recurso é chamado de avaliação instantãnea.
Nota: a avaliação instantânea, como outros recursos de depuração, funciona apenas em modo
depuração [Stopped] e não em modo de execução normal [Running].

Para ver outras variáveis ou alterar seus valores, use o comando Run |Evaluate/Modify... ou sua
tecla de atalho, [Ctrl+F7]. Se você clicar no nome de uma variável e teclar [Ctrl+F7], a janela
"Evaluate/Modify" aparece, mostrando o valor atual da variável e permitindo você digitar um novo valor
em "New value:":
Você também pode digitar o nome de outra variável ou uma expressão qualquer em "Expression", por
exemplo, "soma * 10 / 7", usando a janela como uma calculadora. Ou você pode digitar o nome de uma
constante (para saber o valor de uma constante predefinida do Delphi por exemplo), ou uma propriedade
de um componente. Ao clicar em "Evaluate", você verá o valor da expressão, variável ou constante. Se
for uma variável, você terá a opção de modificar o valor.

Monitorando o Valor de uma Variável Dinamicamente


Se você quer acompanhar o valor de uma variável a cada momento da execução, você pode adicionar um
"watch" [sentinela], que é um mostrador permanentemente atualizado com o conteúdo da variável.

Para adicionar um "watch" para a variável 'soma', por exemplo, clique no nome da variável e use
Run |Add Watch... ou a tecla [Ctrl+F5]. Existem várias opções nesta janela, mas mantenha as opções
padrão e clique Ok. O Delphi vai mostrar a janela "Watch List", que contém todos os "watches"
adicionados. Você pode movê-la para uma posição qualquer da tela onde não cubra outras janelas. Agora
à medida que você usar Trace Into/F7 ou Step Over/F8, o "watch" vai mostrar o valor da variável sendo
alterado.

Para editar um "watch", clique duas vezes em cima dele na janela "Watch List" e altere suas
opções. Para removê-lo, clique na lista e pressione [Delete]. Para adicionar um novo, com a janela
"Watch List" aberta, clique duas vezes na linha vazia depois do fim da lista.
Forçando a Finalização do Programa

Quando, durante a depuração, você quiser terminar a execução do programa, o melhor a fazer é
continuar sua execução normal e depois fechar a janela do programa. No entanto, às vezes é necessário
forçar a terminação do programa. Para fazer isso, use o item de menu Run |Program Reset ou tecle
[Ctrl+F2] (essa tecla de atalho só funciona se você estiver com o foco em uma janela do Delphi).

Outras Ferramentas de Depuração


Enquanto você está em modo de depuração, você pode chegar a executar um procedimento que foi
chamado por outro, que por sua vez foi chamado por outro e assim por diante. Para esse tipo de programa
mais complexo, você pode visualizar a pilha de chamadas [call stack], que mostra toda a seqüência de
chamadas de procedimento feitas até o ponto atual, desde o primeiro procedimento de evento executado.
Para isso, use o menu View|Debug Windows|Call Stack[Ctrl+Alt+S].
As outras opções que aparecem em View|Debug Windows|Call são:

Threads opção para verificar multiplas linhas de execução (threads).


Modules visualizar DLLS , que o programa utiliza.
CPU mostra o código assembler de dlls e programas, indenpendente se possui o código fonte.

Se você se perder durante a depuração e quiser encontrar a linha que está sendo executada agora, use o
menu Run|Show Execution Point.
Capítulo 10
Procedimentos e Funções

Procedimentos Gerais

Unidades Independentes

Funções

Passagem de Parâmetros
Procedimentos Gerais
Um procedimento é um trecho de programa que tem um nome e pode ser utilizado (chamado) por outras
partes do programa. Um procedimento de evento é um procedimento que está diretamente ligado a um
evento e é chamado diretamente por um componente quando acontece aquele evento. Como já vimos,
procedimentos de evento geralmente são criados pelo Delphi de forma automática. Mas você pode criar
também procedimentos gerais, que não são associados a nenhum componente, e só são chamados quando
você determinar.
Procedimentos gerais tornam o programa mais legível e evitam repetição desnecessária de código,
facilitando a manutenção do programa. Em muitos casos, eles também contribuem para a redução do
tamanho do programa. Eles também podem ser reutilizados em vários projetos, evitando "reinventar a
roda" a cada novo programa que você desenvolve.

Criando um Procedimento Geral

Abra novamente o projeto da calculadora, CALC.DPR. Nesse projeto temos uma validação de
tecla, feita pelo procedimento de evento 'tratartecla'. Mas vamos transformá-la em um procedimento
geral, para poder reutilizar essa validação em outros programas.
Quando você cria um procedimento de evento, o Delphi gera automaticamente o esqueleto do
procedimento, ou seja, um cabeçalho, mais o begin e end, inicialmente vazios. Mas um procedimento
geral deve ser criado manualmente, ou seja, você deve digitar todo o esqueleto.

Na unidade CALC.PAS, coloque o cursor depois da palavra implementation e do comentário {$R


*.DFM}. Digite todas as linhas a seguir:
procedure ValidarTecla(var tecla: char);
begin

end;
O cabeçalho do procedimento contém a palavra procedure, o nome do procedimento (vamos chamá-lo
de 'ValidarTecla') e, entre parênteses, os parâmetros do procedimento. Parâmetros são variáveis que
recebem informações externas, passadas por quem chamar o procedimento.

Agora, dentro do procedimento 'TForm1.TratarTecla', selecione todo o texto entre o begin e o end
e tecle [Ctrl+X] para retirá-lo e guardar na área de transferência do Windows. Substitua o texto anterior
por:
ValidarTecla(Key);
Isso vai chamar o procedimento, substituindo o parâmetro 'tecla' pelo argumento 'Key'. Argumentos são
informações passadas para o procedimento para substituir os parâmetros.

Volte ao procedimento 'ValidarTecla' e, dentro dele, use [Ctrl+V] para inserir o código que foi
copiado antes. Faça a seguinte alteração no código: substitua as referências a 'Key' por 'tecla'. O
procedimento deverá ficar como o seguinte:
procedure ValidarTecla(var tecla: char);
begin
if not (tecla in ['0'..'9', #8]) then
tecla := #0;
end;
O argumento (Key) e o parâmetro (tecla) na verdade poderiam ter o mesmo nome. Nós colocamos nomes
diferentes apenas para que o procedimento tenha uma forma mais geral. Execute o programa e ele deve
continuar funcionando como antes.
Unidades Independentes
Da forma que o procedimento está, ele só pode ser usado na unidade CALC.PAS, associada ao
formulário da calculadora. Se quisermos que o procedimento seja usado por outras unidades do projeto,
devemos colocar uma declaração do procedimento na interface da unidade.
Mas essa ainda não é a solução ideal, pois para utilizar uma unidade associada em outro projeto, é
necessário também utilizar o seu formulário, mesmo que ele não seja necessário. O melhor é colocar o
procedimento em uma unidade independente, ou seja, uma unidade não associada a nenhum formulário.
Unidades independentes facilitam a reutilização, especialmente entre vários projetos.

Criando uma Unidade Independente

Para criar uma unidade independente, use o item de menu File|New... do Delphi, escolha o ícone

"Unit" ( ) e clique Ok. O Delphi vai abrir a nova unidade no editor de código, contendo apenas um
esqueleto do mínimo que uma unidade deve ter (como vimos no capítulo 9):
unit Unit2;

interface

implementation

end.

Salve o projeto para dar um nome à nova unidade. Chame-a de GERAL.PAS (porque ela vai
conter procedimentos gerais). O cabeçalho da unidade automaticamente vai mudar para 'unit Geral;'.

Transferindo o Procedimento

Voltando à unidade Calc (use as abas do editor de código para mudar entre as duas), selecione todo
o procedimento 'ValidarTecla', inclusive o cabeçalho. Tecle [Ctrl+X] para recortá-lo da unidade. Agora
alterne para a unidade Geral, coloque o cursor depois da palavra implementation e "cole" o texto,
usando [Ctrl+V].

O corpo do procedimento deve ficar na seção de implementação, mas se ele precisar ser usado
externamente, o cabeçalho do procedimento deve ser copiado para a seção de interface. Coloque o cursor
na seção de interface e tecle [Ctrl+V]. Apague do begin ao end, porque só é necessário (e só é permitido)
o cabeçalho. A unidade ficará assim:
unit Geral;

interface

procedure ValidarTecla(var tecla: char);


implementation

procedure ValidarTecla(var tecla: char);


begin
if not (tecla in ['0'..'9', #8]) then
tecla := #0;
end;

end.

Usando a Unidade no Mesmo Projeto

Para utilizar os procedimentos da unidade 'Geral' em outra unidade, você deve acrescentar 'Geral' à
cláusula uses da unidade. Voltando à unidade Calc, ela ainda não tem uma cláusula uses na seção de
implementação, então acrescente uma, logo após o {$R *.DFM}, contendo o seguinte:
uses Geral;

Execute o programa e ele deverá funcionar como antes. Salve o projeto novamente.

Usando a Unidade em Outros Projetos

Abra o projeto MEDIAP.DPR. A unidade Geral, que criamos no outro projeto, é um único arquivo
fonte (GERAL.PAS), que pode ser reutilizado quando necessário em qualquer outro projeto. Primeiro
acrescente a unidade ao projeto atual, usando File|Add to project... ou o botão da SpeedBar e
escolhendo GERAL.PAS. Esse passo não é estritamente necessário(caso a unit GERAL esteja no mesmo
diretório que o projeto MEDIAP), mas é importante para facilitar o gerenciamento do projeto, como
veremos.

Depois, você deve acrescentar o nome da unidade à cláusula uses da unidade MEDIA.PAS,
associada ao formulário. A forma mais fácil de fazer isso é a seguinte: selecione a unidade "Media" no
editor de código, e use o item de menu File|Use unit.... Na lista, clique em "Geral" e depois em Ok. O
Delphi irá adicionar o seguinte à seção de implementação da unidade:
uses Geral;

Agora crie um procedimento de evento para o controle 'editNumero', evento OnKeyPress. Dentro
do procedimento, coloque uma chamada a 'ValidarTecla'. Inicialmente digite apenas o seguinte e deixe o
cursor posicionado após o parêntese:
ValidarTecla(
Repare que o Delphi mostra uma descrição dos parâmetros do procedimento (no caso apenas var tecla:
char). Essa é uma característica do Code Insight que permite saber facilmente quais são os parâmetros
que um determinado procedimento exige e o CodeExplorer mostra o procedimento na lista. Complete a
linha como abaixo:
ValidarTecla(Key);

Ou seja, vamos passar como argumento a variável 'Key'. Execute o programa. Note que agora o
controle 'editNumero' agora tem a mesma validação de tecla, não permitindo valores não numéricos.

Gerenciando um Projeto com Unidades Independentes


Nós vimos anteriormente que o Project Manager é usado para facilitar o gerenciamento de projetos com
vários arquivos. Abra o Project Manager novamente e repare que a unidade independente 'Geral' aparece
na lista de arquivos do projeto. Ele nào permite expandir o arquivo GERAL.PAS, obviamente, pois essa
unidade não está associada a um formulário.
Além do Project Manager, você pode usar o botão (View Unit) para ver as unidades do projeto,
incluindo unidades independentes.
Se você não adicionar uma unidade ao projeto, mas apenas adicionar uma cláusula uses para ela, você
ainda pode acessá-la,desde que esteja no mesmo diretório do projeto ou no diretório LIB do Delphi),
mas ela não estará disponível nas facilidades de gerenciamento de projeto.

Criando uma Biblioteca de Rotinas


Uma unidade independente pode funcionar como uma biblioteca de rotinas mais utilizadas,
compartilhada por vários projetos. Mas nesse caso é preciso tomar certos cuidados. Por exemplo, se você
modificar um procedimento, isso pode afetar o funcionamento de outros projetos que o utilizam.
Também, para que um procedimento seja reutilizável, é preciso que ele não dependa de nada específico
de um determinado projeto, como um formulário ou uma variável global, ou outra unidade.
Uma unidade pode conter também, além de procedimentos, constantes, tipos de dados definidos e
variáveis. Se estes forem declarados na parte interface, podem ser usados também por outras unidades.
No caso de uma constante ou tipo de dados, pode ser necessário usá-la(o) ainda na seção de interface.
Nesse caso (e apenas nesse caso) é necessário acrescentar o nome a unidade à cláusula uses da seção de
interface. Em todos os demais casos, é melhor acrescentá-la na seção de implementação.
Funções
Funções são semelhantes a procedimentos, mas são chamadas geralmente dentro de uma expressão. Uma
função retorna um valor para quem chamou, e esse valor é inserido na expressão onde a função foi
chamada, por exemplo:
x := 3 * Soma(a,b);
Durante a execução, a função 'Soma' é chamada, com os seus parâmetros preenchidos de acordo com os
argumentos fornecidos (a e b). A função então devolve um resultado, que é inserido na expressão. Depois
continua o cálculo do restante da expressão.

Criando uma Função

Vamos abrir novamente o projeto CALCP. Agora dentro da unidade Geral, vamos criar uma
função. Essa função não vai fazer nada realmente útil, pois apenas soma dois números e devolve o
resultado, mas ela ilustra os detalhes mais importantes que envolvem a criação e uso funções.

Na seção de interface, digite o cabeçalho da função:


function Soma(x,y: double): double;
No cabeçalho da função, além do nome e parâmetros, é preciso declarar o tipo de retorno da função, ou
seja, o tipo de dados do valor que a função devolve. No caso, o tipo é double, declarado depois do fecha
parênteses.

Agora na seção de implementação, repita o cabeçalho da função e acrescente o corpo da função,


contendo o seguinte:
function Soma(x,y: double): double;
begin
Result := x + y;
end;

Retornando um Valor
O nome 'Result' é uma variável automaticamente criada (não precisa ser declarada) dentro de uma
função. O valor colocado nessa variável será o valor retornado pela função, que será inserido no ponto de
chamada. No caso, o valor é calculado como a soma dos valores dos dois parâmetros (x e y). O tipo dessa
variável é o tipo de retorno da função, no caso double.
Para determinar o valor de retorno, também pode ser usada a sintaxe de antigas versões do Pascal, com o
nome da função do lado esquerdo:
Soma := x + y;
A desvantagem dessa sintaxe é que não é possível usar o nome 'Soma' do lado direito de uma atribuição,
como é possível fazer com 'Result'.
Ex:
Soma := Soma + y;

Chamando a Função

No formulário principal, clique duas vezes no botão com o sinal de "=" (btnCalcula) para abrir o
procedimento de evento associado. Neste procedimento substitua a linha:
res := op1 + op2;
por uma chamada à função. Inicialmente digite apenas o seguinte e deixe o cursor após o abre parênteses:
res := Soma(
Note que o recurso de Code Insight do Delphi automaticamente mostra a descrição dos parâmetros (x:
double; y: double). Caso você não se lembre mais dos parâmetros exigidos, esse recurso facilita
bastante. Complete o restante da linha, com os argumentos:
res := Soma(op1, op2);
Note que os argumentos são separados por "," (vírgula) na chamada. Execute o programa e verifique que
ele continua funcionando como antes.
Notas: Se deseja ver o conteúdo de um procedimento ou função ,clique com Ctrl+Clique .
Notas: Se colocou o cabeçalho de um procedimento ou função na seção interface, e deseja criá-los
pressione Ctrl+Shift+C.

Notas: Para alternar da seção implementation , para seção interface pressione Ctrl + Shift + ( ),o
contrário utilize Ctrl+Shift + ( ).
Passagem de Parâmetros
Quando um procedimento ou função possui parâmetros, ele deve ser chamado com argumentos
correspondentes, na mesma ordem. Nenhum argumento deve ser omitido. (A documentação do Delphi
chama os parâmetros de formal parameters e argumentos de actual parameters). Por exemplo:
begin
ValidarTecla(Key);
end;
...
procedure
ValidarTecla(var tecla: char);

Declaração de Parâmetros e Argumentos


Quando há mais de um parâmetro, esses são separados por ";" (ponto-e-vírgula) na declaração do
procedimento, se têm tipos diferentes:
function Soma(x: double; y: integer): double;
ou podem ser separados com ",", se têm o mesmo tipo:
function Soma(x, y: double): double;
e as duas formas podem ser combinadas, exatamente como em declarações de variáveis:
procedure TextOut(x,y:integer; text:string);
Na chamada ao procedimento ou função, os argumentos são sempre separados por "," (vírgulas):
x := Soma(12, TAMANHO);
TextOut(x, 13, 'Testando');
O nome do procedimento pode ser repetido no projeto ou na unit, o que diferencia um procedimento do
outro é o número e tipo de parâmetros passados.
Por Exemplo:
A função soma pode realizar a soma entre dois , três e quatro números , sendo que quando a soma for
realizada entre quatro números os dois últimos são do tipo string. Como na Unit GERAL já existe uma
função que realiza a soma de dois números , vamos criar as outras com o mesmo nome:
function Soma( op1, op2, op3 : double): double;
begin
result := op1 + op2 + op3;
end;
function Soma( op1, op2 : double, op3, op4 : string): double;
begin
result := op1 + op2 + strtofloat(op3) + strtofloat(op4);
end;
Mas além de criar as funções ou procedimentos com o mesmo nome é necessário que durante a
declaração do procedimento especficique que eles serão sobrecarreagados, para isso temos que informar
no final da declaração a diretiva overload.
function Soma(x,y: double): double;overload;

function Soma( op1, op2, op3 : double): double;overload;

function Soma( op1, op2 : double; op3, op4 : string): double;overload;

Tipos de Passagem
Existem várias opções disponíveis para passagem de parâmetros de procedimentos ou funções. (Os
nomes entre colchetes estão como aparecem na documentação do Delphi):
• Passagem por Valor [value parameter]: o valor do argumento (que pode ser uma variável, constante ou
uma expressão) é simplesmente copiado para o parâmetro. Isso significa também que qualquer alteração
no parâmetro não afeta o argumento original, mas apenas uma cópia local. Esse é o método padrão de
passagem, ou seja, um parâmetro é passado por valor se não for especificado em contrário. A função
'Soma' usa esse método para os dois parâmetros. Se o parâmetro Tecla da função Validar tecla for do tipo
passagem por valor , o programa que chamar este procedimento não vai funcionar, porque o programa irá
escrever os caracteres inválidos, portanto , é necessário alterar o argumento original.
• Passagem por Referência [variable parameter]: o argumento deve ser uma variável do mesmo tipo de
dados do argumento. Qualquer alteração no parâmetro afeta imediatamente o argumento original. Para
indicar passagem por referência, usa-se a palavra var antes do nome do parâmetro. O procedimento
'ValidarTecla', por exemplo, usa passagem por referência, porque precisa alterar o argumento original.
• Passagem Constante [constant parameter]: um parâmetro constante (declarado com const antes do
nome) não pode ser modificado dentro do procedimento. Essa característica pode ser usada para impedir
alterações acidentais que violariam a lógica do programa. O argumento pode ser uma expressão qualquer.
No caso de parâmetros grandes, como vetores e registros, a passagem constante evita que seja feita uma
cópia do argumento, como na passagem por valor, portanto é bem mais eficiente.
No mesmo procedimento ou função, um parâmetro pode ser passado por valor e outros por referência ou
constantes, independentemente um do outro. Funções especificamente não precisam ter parâmetros por
referência, pois elas já retornam um valor através da própria chamada de função, mas em alguns casos a
passagem por referência pode ser útil.
Capítulo 11
Objetos

O que Contém um Objeto

Classes e Objetos

Herança

Formulários como Objetos

Variáveis de Objetos

Listas de strings

Objetos Predefinidos

Code Explorer

Programação Orientada a Objeto


O Que Contém um Objeto
Um objeto, numa definição prática, é uma área de memória separada do computador, contendo dados e o
código que manipula esses dados, e que é manipulada como uma única unidade. Por exemplo, qualquer
componente, um formulário, uma lista de strings, ou o objeto Clipboard.
Nos programas, os dados de um objeto aparecem como campos do objeto, ou geralmente, como
propriedades. Um objeto pode ter campos de dados que não são visíveis externamente, com isso
mantendo todo o controle sobre sua implementação interna.
O código de programa que manipula os dados é feito através de métodos. Um método tem acesso a todos
os campos de dados do objeto, mesmo aqueles que não são visíveis externamente. Um método pode ser
um procedimento, que executa alguma ação quando é chamado, ou uma função, que além de executar
uma ação, retorna um valor para quem chamou, que pode ser usado em uma expressão.Os procedimentos
de eventos criados são métodos do objeto do formulário criado. Por exemplo, com alguns métodos que já
vimos:
editNumero.SetFocus; {método procedimento: SetFocus}
Lines.SaveToFile(nome); {método procedimento: SaveToFile}
if Clipboard.HasFormat(cf_text) then
{método função: HasFormat}
Um componente, que é um tipo de objeto, pode ter também eventos, que são propriedades cujo valor é
um procedimento de evento. O procedimento que está associado a um evento é chamado quando ele é
acionado.
Classes e Objetos
Todo objeto pertence a uma classe. Uma classe é um esquema que deve como serão os objetos, quais
suas características, e quais os métodos que
podem ser usados para manipular o objeto. Cada objeto específico é uma instância da classe, criada de
acordo com o modelo definido pela definição da classe.
Por exemplo, cada ícone da paleta de componentes corresponde a uma classe. O nome dessa classe tem a

letra 'T' antes do nome que aparece. Por exemplo, o ícone (Edit) corresponde à classe TEdit.
Quando você seleciona esse ícone e clica no formulário, você está criando um objeto da classe TEdit, que
é chamado (inicialmente) de "Edit1". Você pode criar quantos objetos quiser da mesma classe, limitado
apenas pela memória do computador. Se você criar outros, eles serão chamados de "Edit2", "Edit3". O
Object Inspector sempre mostra o nome do componente e o nome de sua classe, por exemplo:

Todos os objetos de uma mesma classe têm as mesmas características (propriedades), mas cada um tem
seus próprios valores para essas características. Quando você cria um objeto, ele começa como uma
cópia idêntica do modelo definido pela classe, e suas propriedades (exceto Name, que deve ser único)
começam com os mesmos valores padrão que a classe define. Depois você pode alterar essas
propriedades, diferenciando um objeto do outro.
Você pode também criar um objeto dinamicamente, durante a execução do programa, conforme veremos.
Herança
Classes geralmente não são criadas a partir do zero. Quando você cria uma nova classe, ela pode ser
baseada em uma classe que já existe. Com isso, a classe herda todos os dados e métodos da outra classe,
mas pode acrescentar ou redefinir alguns de acordo com o que é necessário.
O mecanismo de herança [inheritance] permite que você programe apenas o que é diferente entre a sua
classe e a outra (programação por exceção). A classe da qual são herdadas as características é chamada
de classe base ou ancestral [ancestor] e a classe criada é chamada de classe derivada ou descendente
[descendent]. Herança não é apenas uma cópia estática de características. Qualquer alteração feita em
uma classe ancestral automaticamente repercute nas classes descendentes.
Quando a herança é usada sucessivamente, com uma classe derivada também tendo classes derivadas, é
criada uma hierarquia [hierarchy] de classes, que pode ser representada com uma estrutura em árvore.

Hierarquia de Classes do Delphi


Nós já vimos que "todo controle é um componente". Da mesma forma, "todo componente é um objeto".
Essas frases dizem respeito exatamente a relações entre classes ancestrais e classes derivadas no Delphi.
A classe 'TComponent' é a ancestral, direta ou indireta de todas as outras classes de componentes. Ela
contém as propriedades Name e Tag (além de outras pouco usadas), que são herdadas por todas as
outras classes descendentes.
A classe 'TControl' é uma das descendentes de TComponent. Ela contém propriedades, como Enabled,
Visible, Left, Top, Width e Height e métodos, como Refresh, que são herdados por todas as classes de
controles, por exemplo, TEdit, TButton, TLabel.
Todo formulário é também um controle. A classe 'TForm', que define as características de qualquer
formulário, é descendente indireta de TControl. Quando você cria um novo formulário, o Delphi cria
automaticamente uma classe de formulário descendente de TForm, ou seja, seu formulário tem todas as
características de um formulário padrão, além das que você acrescentar (novos campos de dados, novos
métodos etc.). O nome da sua classe de formulário é criado com 'T' seguido do nome do formulário, por
exemplo, 'TMeuForm'.
Uma visão simplificada de uma hierarquia de classes (omitindo algumas intermediárias) é a seguinte:
TComponent (ancestral de todos componentes)
TOpenDialog (um componente que não é controle)
TControl (ancestral de todos controles)
TForm (ancestral de todos formulários)
TFormPrincipal (suas classes...
TFormCalen ...de formulário)
TButton (classes de...
TEdit ...controles)
TTimer (um componente que não é controle)
Todas as classes no Delphi, inclusive TComponent, são descendentes diretas ou indiretas da classe
TObject, que contém apenas métodos básicos de manipulação de objetos.
Você pode também criar uma nova classe de componente, derivando uma classe de TComponent ou de
alguma descendente desta. Mas este é um processo mais complexo, que envolve vários detalhes de
programação, e por isso não cobriremos no curso.

O Object Browser
Para visualizar a hierarquia de classes do Delphi e da sua aplicação, existe uma ferramenta que pode ser
usada para isso. É o Object Browser, que pode ser aberto no Delphi com o menu View|Browser (só está
disponível depois que você compilou o projeto ao menos uma vez). Além de classes, o Object Browser
permite ver quais as constantes, tipos de dados e variáveis das unidades utilizadas no projeto.

Simplificando o Acesso aos Campos


Campos, propriedades e métodos de um objeto são acessados, geralmente, usando-se a sintaxe que já
vimos, com o nome do objeto, seguido de um ponto e o nome do campo, propriedade ou método:
editTamanho.Clear;
editTamanho.Enabled := True;
editTamanho.SetFocus;
Mas quando se acessa vários campos do mesmo objeto, é preciso repetir o nome do objeto em cada
comando. Para evitar isso, existe o comando with. Dentro do bloco do comando with, o nome do objeto
fica implícito:
with editTamanho do begin
Clear;
Enabled := True;
SetFocus;
end;
Formulários como Objetos
Formulários também são objetos. Quando você cria um novo formulário, o Delphi cria uma classe de
formulário e um objeto de formulário dessa classe. O nome da classe de formulário sempre inicia com um T,
seguido do nome do objeto de formulário (que você define na propriedade Name do formulário). A vantagem
de ter uma classe de formulário é que isso torna possível criar dinamicamente outros objetos de formulário,
além do inicial, e alterar suas propriedades.

A Classe de Formulário
Abra o projeto CalcData.DPR(Capítulo 8). Você verá inicialmente o formulário principal. Tecle [F12] para ver
a unidade do formulário. Na seção de interface da unidade, logo após a cláusula uses, você verá a declaração da
classe de formulário:
type
TFormPrincipal = class(TForm)
btnCalendario: TButton;
btnCalculadora: TButton;
btnFechar: TButton;
editData: TEdit;
Label1: TLabel;
procedure btnFecharClick(Sender: TObject);
procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
procedure btnCalculadoraClick(Sender: TObject);
procedure btnCalendarioClick(Sender: TObject);

private
{ Private declarations }
public
{ Public declarations }
end;

var
formPrincipal: TFormPrincipal;
A primeira linha: TFormPrincipal = class(TForm) quer dizer que a classe do formulário é baseada na classe
'TForm', que define um formulário genérico. Veremos mais sobre isso mais tarde.
Dentro da definição da classe (que vai até a palavra end), existe uma declaração para cada componente do
formulário (no caso Button ,Edit e Label). Se você adicionar, remover, ou renomear qualquer componente, o
Delphi altera automaticamente essa seção. Para testar, alterne para o formulário com [F12] e acrescente um
componente Button. Quando você volta à unidade, pode verificar que o Delphi acrescentou uma declaração para
esse componente:
Button1: TButton;
onde 'Button1' é o nome do componente e 'TButton' é o nome da classe do componente, como veremos adiante.
Volte ao formulário, remova o botão e o Delphi vai também remover essa declaração.
Depois das declarações de componentes, começam as declarações para os procedimentos de evento. Para cada
procedimento de evento, o Delphi adiciona uma declaração dentro da classe, como:
procedure btnFecharClick(Sender: TObject);
O corpo do procedimento, que fica na seção de implementação, contém os comandos que implementam
realmente o procedimento.
O código até a palavra private é mantido automaticamente pelo Delphi e você não deve alterá-lo. Geralmente
não existe motivo para fazer alterações manuais. As seções private e public dentro da definição da classe são
onde você pode adicionar mais elementos. O Delphi não troca o que você colocar em uma dessas seções. O que
fica na seção private é acessível apenas na unidade atual, mas o que for declarado na seção public é visível
externamente em outras unidades.
Com a palavra end, termina a definição da classe. Depois o Delphi acrescenta uma declaração de variável para
o formulário. Essa declaração cria um formulário baseado na classe definida antes:

Procedimentos na classe de formulário


Vamos criar um método para a classe desse formulário, como um método é um procedimento criado dentro de
um classe, podemos criar um procedimento na unit e declara-lo em uma das seções private ou public, por
exemplo:
procedure AbrirFormulario;
E o corpo do procedimento, com os seus comandos, seria colocado na parte implementation, por exemplo:
procedure TFormPrincipal.AbrirFormulario;
begin
Show;
end;
Quando definimos que um procedimento faz parte de uma determinada classe na criação do procedimento
temos que especficar o nome da classe , como foi feito no exemplo anterior. No exemplo citado o
procedimento não faz muita coisa útil. Mas a vantagem principal de criar um procedimento como parte da
classe de formulário é que ele pode acessar diretamente qualquer componente que faz parte do formulário, sem
indicar o nome do formulário. Isso torna mais fácil manipular os componentes.
Vamos abrir o projeto EVENTOS.DPR (Capítulo 6), podemos verificar que no procedimento abaixo que utiliza
a propriedade Cursor do formulário, não foi necessário especificar o nome do objeto que referencia a classe do
formulário criado, isso não foi necessário porque o procedimento faz parte da classe do formulário especifico.
procedure TForm1.FormClick(Sender: TObject);
begin
if Cursor = crDefault then
Cursor := crHourglass
else
Cursor := crDefault;
end;
Variáveis de Objeto
Uma variável de objeto é declarada utilizando o nome da classe como um tipo de dados, como já vimos:
var
btnCalendario: TButton;
Edit1:TEdit;
formPrincipal: TFormPrincipal;
O conteúdo de uma variável de objeto é uma referência a um objeto, internamente armazenada como o
endereço de memória onde o objeto fica armazenado.
Inicialmente o conteúdo da variável é lixo ou, no caso de uma variável global, ela é inicializada com o
valor nil. Esse é um valor especial que significa no caso "nenhum objeto". Se você tentar usar uma
variável que contém nil, isso vai causar um erro de execução.
Você pode inicializar a variável, criando um objeto. Para isso, use o método Create (o construtor da
classe), com o nome da classe, por exemplo:
lista := TStringList.Create;
O construtor aloca um espaço de memória para conter os dados do objeto e retorna o endereço dessa
memória. Dependendo da classe, o construtor pode ter parâmetros usados para inicializar o objeto.
Algumas classes têm construtores com outros nomes ao invés de Create.
Para liberar a memória usada pelo objeto, use o método Free (definido na classe TObject e herdado por
todas as classes). Se você não liberar, o espaço de memória do objeto continua alocado, mesmo que não
existam mais variáveis para acessá-lo. Por exemplo, depois de terminar de usar uma string list, você deve
liberar a memória ocupada por ela:
lista.Free;
Quando você atribui uma variável de objeto a outra, não é feita uma cópia física do objeto. A única coisa
copiada é a referência ao objeto. Depois da atribuição, ambas as variáveis referenciam o mesmo objeto,
por exemplo:
var btn: TButton;
begin
btn := btnDuplicar;
btn.Caption := 'Duplicar de novo';
{afeta btnDuplicar indiretamente}
...
end;
Vamos criar um exemplo, que quando for clicado o mouse no formulário, vamos criar componente
TButton na posição do cursor.
No formulário, altere o nome para formCriaComp e o título para "Criação de Componentes". Coloque
dois componente Button , faça as seguintes alterações:
Button1
Name btnCriar
CaptionCriar
Button2
Name btnDestruir
CaptionDestruir

Vamos criar uma propriedade na classe de formulário 'TformCriaComp', para isto basta criar uma
variável nesta classe, como iremos armazenar nesta variável a quantidade de botões criados,esta variável
será publica. Na classe de formulário 'TformCriaComp' na parte public , acrescente:
TForm1 = class(TForm)
.......
private
{ Private declarations }
public
{ Public declarations }
QuantBotao:integer;
end;
No evento OnClick do botão "btnCriar", faça o seguinte:
var botao : TButton;
begin
quantBotao := quantBotao + 1;
botao := TButton.create(self);
with botao do
begin
parent := self;
caption := 'Botão' +
inttostr(quantBotao);
left : = 0;
top := 0;
visible := true;
end;
end;
Antes de criar o Botão iremos adicionar o valor 1 a propriedade quantBotao.
Quando você cria um componente,usa o construtor Create da classe TComponent. Esse construtor exige
como parâmetro o formulário onde o componente será criado. No caso, passamos 'Self'(o formulário
atual), que é o mesmo que 'FormCriaComp' nesse caso.
O formulário 'Self' será o dono[owner] do componente.Mas no caso de um controle(componente visual) é
preciso dizer também quem será o pai[parent] desse controle. Se for o próprio formulário, o controle fica
sobre o formulário. Mas se houver outro controle que é capaz de conter controles, o pai pode ser esse
outro controle. Por Exemplo: O controle Panel, se colocar esse controle sendo o pai do Botão, este será
criado em cima do Panel.
Quem determina o pai do componente é a propriedade Parent. Depois a propriedade caption é alterada
para 'Botão' mais o conteúdo da propriedade quantBotao, e a posição do componente(propriedades Top e
Left) é definida com o valor Zero.A propriedade Visible faz com que o componente apareça no
formulário.
No evento OnClick do botão "btnDestruir", vamos destruir o botão criado, o método utilizado para
liberar a memória é o Free. Então acrescente:
var botao :TButton;
begin
botao.free
end;
Execute o programa. Para testar clique no botão 'Criar'. A cada clique no botão será criado um
componente da classe TButton, mas quando for destruir o componente não irá funcionar , pois, a
referencia que existia para os botões que foram criados anteriormente foram perdidas. Um dos problemas
ocorre porque a variável que contém a referência do objeto da classe TButton é local, existindo somente
no procedimento que foi criada. Para solucionar este problema vamos criar a variável botao sendo global,
então vamos retirar a declaração desta variável nos procedimentos de eventos TCriaComp.btnCriarClick
e TCriaComp.btnDestruirClick. E na seção implementation acrescente a declaração desta variável.
Implementation

{$R *.DFM}

var botao:TButton;
Mesmo colocando esta variável sendo global o problema ainda não foi resolvido , pois, o botão 'Criar'
continua permitindo a criação de mais de um botão, com isso a variável botao sempre irá conter a
referência para o último botão criado. Vamos considerar que queremos criar somente um botão de cada
vez, inicialmente o botão 'Destruir' vai aparecer desabilitado, porque não temos nenhum botão criado, ao
criar iremos habilitar o botão 'Destruir' e desabilitar o botão 'Criar' e quando destruir o botão vamos
desabitilitar o botão 'Destruir' e habilitar o botão 'Criar'.
Na propriedade Enabled do botão 'Destruir' altere seu valor para false. No envento OnClick do botão
'Criar' acrescente o código a seguir:
btnDestruir.enabled := true;
btnCriar.enabled = false;
E no evento OnClick do botão 'Destruir' acrescente:
btnDestruir.enabled := true;
btnCriar.enabled = false;
Vamos salvar o projeto como CRIACOMP.PAS e CRIACOMPP. Execute e teste o projeto.
Listas de Strings
O Delphi possui um tipo de objeto que é útil quando se trabalha com vetores de strings. São as chamadas
listas de strings [string lists]. É importante saber trabalhar com esse tipo de objeto, pois várias
propriedades de componentes são do tipo lista de strings, como a propriedade Items dos controles
ListBox e ComboBox, a propriedade Lines de um controle Memo, entre outras.
Vamos abrir o primeiro projeto criado, AULA1P. Nesse projeto, existe um componente ListBox
chamado 'ListBox1' (o nome default). No código do botão Adicionar, o programa faz o seguinte:
ListBox1.Items.Add(Edit1.Text);
Nesse código, 'ListBox1.Items' é uma string list, e nesse caso, está sendo utilizado o método Add da
string list, que adiciona um elemento. Como essa string list está associada a 'ListBox1', o item adicionado
também aparece na lista.

Propriedades da String List


Você pode usar a propriedade Strings, que é um vetor, para acessar os elementos da lista (o índice do
vetor começa de zero), por exemplo:
nome := ListBox1.Items.Strings[i];
ou simplesmente, omitindo o nome da propriedade, pode-se acessar a lista como um vetor:
nome := ListBox1.Items[i];
A propriedade Count informa quantos elementos existem na lista, por exemplo:
for i := 0 to ListBox1.Items.Count - 1 do
Writeln(arquivo, ListBox1.Items[i]);
A propriedade Sorted, quando é True (verdadeiro), faz com que os elementos da lista sejam classificados
automaticamente.

Métodos da String List


O método Add adiciona um elemento no final da lista ou na posição em ordem alfabética, dependendo
do valor de Sorted. Você pode adicionar um elemento no meio da lista, usando Insert. Para remover um
elemento, usa-se o método Delete, informando a posição numérica. Para remover todos os elementos,
basta usar o método Clear. Além disso, Move move um elemento para outra posição e Exchange troca
dois elementos de lugar. Resumindo, os métodos são usados da forma:
Add(novoElemento); {adiciona}
Insert(posição, novoElemento); {insere}
Delete(posição); {exclui um elemento}
Clear; {exclui todos}
Move(posição-ant, posição-nova);{move}
Exchange(pos1, pos2); {troca os elementos}
Para procurar um elemento na lista, usa-se IndexOf. Se o elemento não for encontrado, retorna -1. Por
exemplo:
pos := ListBox1.Items.IndexOf(Edit1.Text);
if pos <> -1 then
{achou...}
No programa EDITORP, nós utilizamos dois métodos que permitem salvar ou ler uma string list para um
arquivo, que são LoadFromFile e SaveToFile. Por exemplo:
memoEditor.Lines.LoadFromFile(dlgArquivo.FileName);
Isso pode ser feito porque 'memoEditor.Lines' é uma string list. De forma geral, esse métodos são usados
como:
LoadFromFile(nomeArquivo); {carrega do arquivo}
SaveToFile(nomeArquivo); {salva para o arquivo}

Criando String Lists Independentes


Você também pode criar string lists independentemente de qualquer componente, declarando uma
variável do tipo TStringList, e usando o método Create. Após terminar de usar a string list, você deve
liberar a memória ocupada por ela com o método Free:
var
lista: TStringList;
begin
lista := TStringList.Create;
lista.Sorted := True;
lista.Add('Curso');
lista.Add('Delphi');
lista.Delete(0);
lista.Free; {libera memória}
end;
Objetos Predefinidos
A biblioteca do Delphi contém vários objetos predefinidos, que você pode usar no programa sem precisar de declarar
variáveis para eles. Com esses objetos, você pode utilizar funcionalidade adicional no seu programa.

O Objeto Application
O objeto Application representa a sua aplicação como um todo. Ele tem várias propriedades e métodos usadas
internamente pelo Delphi, mas algumas delas são úteis no seu próprio código.
A propriedade Title determina qual o título que aparece no ícone quando o programa é minimizado. A propriedade
ExeName (somente de leitura) permite você consultar qual o nome do arquivo executável do seu programa.
Você pode terminar a execução do programa, em qualquer ponto, usando o método Terminate.
Quando você passa muito tempo em um laço de processamento, o Delphi não tem uma oportunidade para verificar os
eventos do usuário. Isso pode fazer o seu programa aparecer como se estivesse "travado" para o usuário. Para evitar
isso, você pode chamar o método ProcessMessages
do objeto Application, de tempos em tempos dentro do laço, o que dá a oportunidade ao seu programa de processar os
eventos:
while not fim do
begin
...
Application.ProcessMessages;
...
end;

O Objeto Clipboard
O objeto Clipboard representa a área de transferência [clipboard] do Windows, uma área de memória usada para
passar informações entre programas. O clipboard pode ser usado para guardar texto ou imagens. Para ler ou colocar
um texto no Clipboard, use a propriedade AsText, por exemplo:
Clipboard.AsText := edit1.text;
...
s := Clipboard.AsText; {se houver texto no clipboard, pega o valor desse texto}
O método Assign pode ser usado para copiar uma imagem para o Clipboard, a partir de um componente Image, por
exemplo:
Clipboard.Assign(image1.Picture);
Para fazer o contrário, você pode trazer a imagem que está no Clipboard dentro de um componente Image, por
exemplo:
image1.Picture.Assign(Clipboard);
Para saber se um determinado formato está disponível, use o método HasFormat, que retorna um valor do tipo
boolean:
if Clipboard.HasFormat(CF_TEXT) then
...
O parâmetro de HasFormat pode ser: CF_TEXT para verificar se existe
um texto, ou CF_BITMAP, CF_DIB, CF_IMAGE para verificar se existe uma imagem.
Para usar o objeto Clipboard no programa, você precisa acrescentar a unidade Clipbrd à sua cláusula uses.

O Objeto Screen
O objeto Screen representa a tela do computador e tem propriedades que permitem consultar a resolução da tela, e
saber qual é o controle ou formulário que tem o foco atualmente.
As propriedades Width e Height contêm, respectivamente, a largura e altura da tela em pixels. A propriedade
PixelsPerInch é um fator de conversão, que diz quantos pixels existem em uma polegada. Com isso você pode
converter pixels em unidades físicas e vice-versa. A propriedade Fonts é um objeto TStringList que contém todos os
nomes de fontes disponíveis no Windows.
Você pode acessar o formulário ativo (o que tem o foco de teclado) através da propriedade ActiveForm e o controle
que tem o foco com a propriedade ActiveControl (do tipo TControl).
A propriedade Cursor, se alterada, vai mudar o cursor do mouse de forma global para todos os formulários. Você
pode também alterar o cursor para um formulário individual, com a propriedade Cursor do formulário.
Code Explorer
O code explorer aparece quando estamos editando uma Unit. Vamos abrir o projeto CRIACOMPP.DPR,
e vamos no editor de codigo da Unit 'CriaComp', irá aparecer a seguinte janela:

O code Explorer é o que parece do lado esquerdo desta janela, nele conseguimos visualizar quais são as
constantes, variáveis , uses e as classes utilizados na unidade que aparece no editor de código. Vamos
expandir cada item que aparece no code explorer e teremos o seguinte resultado:
Como a única classe que foi definida dentro desta Unit é 'TFormCriaComp', apareceu somento o nome
dela. Nesta classe é mostrado o que foi definido nas partes Public e Private. Na opção
'Variables/Constant' conseguimos obter todas as variáveis globais que foram declaradas e em 'Uses'
identificamos as units utilizadas.
Programação Orientada a Objetos
A programação orientada a objeto (POO) é uma forma de programação que tenta construir os programas
como coleções de classes e objetos, relacionados entre si. Uma classe é uma estrutura de dados, definida
em tempo de projeto, que contém também os procedimentos que manipulam esses dados. Um objeto é
uma instância de uma classe, criada em tempo de execução, de acordo com a estrutura da classe e que
pode ser manipulada com os métodos da classe.
A idéia da POO é criar o mínimo possível de dependências de uma parte do programa em relação à outra.
(As "partes" do programa são as classes e seus objetos). Com isso, uma parte pode ser alterada sem afetar
as outras. Num programa orientado a objeto bem construído, o efeito de uma alteração no código é
menor e mais previsível. Em outros tipos de programação, uma alteração no código pode causar
problemas, bugs, inconsistências etc. em várias partes do programa.
Além disso, a programação orientada a objeto facilita bastante a reutilização de código. Sem POO, é
possível reutilizar código, por exemplo, procedimentos e funções isolados, mas isso tem suas limitações.
Na POO, vários conceitos são importantes: encapsulamento, herança e polimorfismo.
Encapsulamento permite esconder membros de uma classe para evitar que outras classes alterem a
estrutura interna do objeto. Por exemplo, um "cliente" da classe TConta não precisa saber como ela
guarda as movimentações da conta e não tem acesso para modificar essas informações (e falsificar um
extrato, por exemplo).
Com o encapsulamento, você garante que o objeto pode alterar sua representação interna sem afetar
nenhuma outra classe. Ele só não pode alterar a sua interface, isto é, o conjunto de métodos e dados
públicos (por exemplo, Depositar(valor:double) e Retirar(valor:double)). O
encapsulamento então força uma separação entre interface e implementação, onde a interface geralmente
é fixa ou muda pouco enquanto a implementação de uma classe tem total liberdade para mudar.
Herança permite criar uma classe derivada a partir de uma classe base. A classe derivada herda a
implementação da base, reutilizando o código que já existe, que já foi testado e aprovado. A classe
derivada pode modificar ou estender o funcionamento de métodos já existentes.
Uma classe derivada herda também a mesma interface da classe base. Isso quer dizer que em qualquer
lugar que possa ser usado um objeto da classe TBase, ele pode ser substituído por um objeto da classe
TDerivada (porque ele suporta os mesmos métodos). Essa possibilidade, de substituir um objeto por um
de outra classe derivada, é chamado polimorfismo. Com o polimorfismo, um "cliente" de uma classe
(procedimento que usa objetos da classe) pode ser escrito sem se preocupar com a subclasse específica
que ele vai manipular. Futuramente, se novas subclasses são criadas, com capacidades adicionais, o
cliente não precisa nem mesmo saber que elas existem, mas pode usá-las como objetos da classe
TBase.Claro que ainda é possível criar programas mal-estruturados, confusos, e instáveis com POO. Mas
se você entender corretamente os conceitos e aplicar da forma correta os recursos da linguagem, corre
um risco menor de acontecer isso.
Capítulo 12
Aplicações de Banco de Dados

Termos Usados

Desktop x Cliente / Servidor

Formatos de Bancos de Dados

Criando Tabelas

Exemplo: Criando um Formulário "Manualmente"

Exemplo: Usando o Database Form Wizard

Resumo dos Componentes de Dados

Apelidos de Bancos de Dados


Termos Usados
Os sistemas de programação necessários ao funcionamento de uma empresa precisam manter dados de
forma permanente, e fornecer recursos para atualizar e pesquisar nesses dados. Os recursos de linguagem
que permitem isso são chamados de acesso a bancos de dados.
Um registro [record] é um grupo de variáveis com tipos de dados diferentes, que armazenam dados
relacionados. Por exemplo, um registro pode conter os dados relativos a um produto vendido pela
empresa, como descrição, código de identificação, quantidade em estoque. Um campo [field] é um dos
itens de informação dentro do registro, como a descrição do produto.
Uma tabela [table] no Delphi é um conjunto de registros com a mesma estrutura, armazenados de forma
permanente em disco. Uma tabela pode ser um arquivo físico, mas não necessariamente, como veremos.
Algumas documentações se referem aos registros de uma tabela como linhas [rows] e aos campos como
colunas [columns].
Um banco de dados [database] é um conjunto de tabelas que contêm dados relacionados. Por exemplo,
um sistema de contas a pagar poderia ter um banco de dados de contas a pagar, com uma tabela para
duplicatas, uma tabela para bancos etc. Geralmente sistemas pequenos ou médios usam apenas um banco
de dados contendo todos os dados.
Um apelido [alias] no Delphi é um nome que representa um banco de dados, independente de sua
localização. Isso permite mover os dados para outro local, sem alterar o programa.
Existem vários formatos de bancos de dados que podem ser acessados pelo Delphi. Dependendo do
formato, um banco de dados pode ser fisicamente um subdiretório do disco, ou um único arquivo.
Um índice [index, plural 'indexes' ou 'indices'] é um mecanismo que permite pesquisar rapidamente um
registro em uma tabela, dado o valor de um determinado campo (ou alguns campos) da tabela.
Durante a execução, cada tabela possui um registro atual ou corrente [current record], cujos dados estão
disponíveis para o programa. Você pode alterar a posição de registro atual, movimentando-se
seqüencialmente pelos registros, ou então usando um índice para procurar um registro.
Um recurso extremamente poderoso do acesso a bancos de dados do Delphi é a consulta [query], que
permite obter todo um subconjunto da tabela ou de várias tabelas, especificando as condições de seleção.
Nota: Você pode criar um registro no Object Pascal com uma estrutura do tipo record, e manipular um
arquivo de registros. Mas esse tipo de acesso não tem muitos recursos, como a possiblidade de alterar a
estrutura de um arquivo, ou excluir um registro, ou pesquisar um valor.
Desktop x Cliente/Servidor
Uma aplicação que utiliza bancos de dados é composta de três partes:
• Interface com o usuário: responsável por validar as entradas do usuário, e iniciar pesquisas de acordo
com um pedido do usuário.
• Mecanismo de acesso a banco de dados: [database engine]: responsável pela manutenção das estruturas
de dados necessárias em arquivos, pelos detalhes internos do acesso aos dados, e pela manutenção da
integridade dos dados.
• Armazenamento de dados: arquivos que contém os dados em si.
Num computador isolado, todos os três componentes ficam no mesmo computador. Já numa rede, que
envolve no mínimo uma estação e um servidor, a configuração pode ser diferente.
Um banco de dados "desktop" (ou baseado em arquivos) é aquele no qual a interface com o usuário e o
mecanismo de acesso ficam no mesmo computador (a estação) e apenas os arquivos dados ficam num
servidor de rede. Operações de consulta ou pesquisa devem passar através da rede.
Por exemplo, quando um usuário quer ver uma relação de contas a pagar, mas apenas em determinado
período, o sistema deve selecionar alguns registros baseado na data informada. No ambiente desktop, a
estação traz todos os registros através da rede, mesmo os que não são utilizados. O tráfego gerado na rede
é grande, principalmente quando várias estações acessam simultaneamente o servidor.
Já num banco de dados cliente/servidor, a interface com o usuário fica na estação e se comunica
remotamente com o mecanismo de acesso, que é um sistema gerenciador de banco de dados (SGBD)
rodando no servidor. Quando o SGBD recebe um pedido para selecionar alguns dados, ele acessa
localmente os dados no servidor e retorna apenas o resultado
pedido. No caso de uma atualização, não é necessário nem mesmo retornar um resultado, apenas
informar que a atualização foi feita.
O diagrama abaixo resume as diferenças entre os ambientes:
Desktop Cliente/Servidor
Bancos de dados desktop funcionam relativamente bem quando são poucos os dados a serem acessados,
ou poucas consultas feitas simultaneamente, de forma que o tráfego na rede seja pequeno. Em redes
maiores, é recomendável utilizar o mecanismo cliente/servidor.
No Delphi você pode começar a desenvolver sua aplicação usando um banco de dados desktop e depois
migrar para cliente/servidor, com poucas modificações no programa, pois a forma de acesso através do
programa é praticamente a mesma.
Nota: De acordo com a Borland, com menos que 12 usuários simultâneos, um ambiente desktop pode
fornecer um ótimo desempenho. Mas para mais usuários, recomenda-se um sistema cliente/servidor,
como o InterBase ou SQL Server por exemplo. Ambientes c/s também são mais confiáveis.
Formatos de Bancos de Dados
Todo o acesso a dados é feito através do Borland Database Engine (BDE), (anteriormente chamado
IDAPI), que é embutido no próprio Delphi. O BDE permite acesso a alguns formatos de dados "nativos",
mas através de interfaces adicionais, permite acessar praticamente qualquer banco de dados padrão de
mercado. Em resumo, os formatos disponíveis são os seguintes:
· Formatos Desktop: o BDE acessa "nativamente" vários formatos de dados no padrão desktop, como
Paradox, dBase, Microsoft Access e FoxPro:
· Paradox: correspondem ao formato utilizado pelo Paradox, um banco de dados da Borland. Cada tabela
tem um arquivo separado, com a extensão DB.
· dBASE/FoxPro: formatos semelhantes, com pequenas diferenças. Cada tabela é um arquivo separado,
com a extensão DBF.
· Access: usado pelo Access, da Microsoft. Cada banco de dados é um único arquivo .MDB, que contém
várias tabelas.
· InterBase: o InterBase é um banco de dados da Borland que pode ser usado para desenvolver e testar
aplicações cliente/servidor. As edições Professional e Client/Server vêm com uma versão reduzida do
InterBase, chamado "InterBase Local", e com drivers que podem ser usados para acessar essa versão. A
edição Client/Server vem com o InterBase para Windows NT, para ambientes cliente/servidor com até 4
usuários. O InterBase armazena o banco de dados inteiro, com todas suas tabelas, num único arquivo de
extensão GDB.
· SQL Links: na edição Client/Server, o Delphi inclui os drivers "SQL Link", para acesso nativo a vários
formatos de bancos de dados cliente/servidor, como: Oracle, Sybase SQL Server, Microsoft SQL Server,
Informix, IBM DB/2 e InterBase.
· ODBC: permite utilizar drivers ODBC, que é um padrão definido pela Microsoft, mas utilizado por
vários fabricantes de bancos de dados. Através do ODBC você pode utilizar outros bancos de dados que
não são suportados das outras formas. ODBC pode ser usado a partir da edição Professional.
O formato que você vai utilizar depende de se você está acessando um banco de dados já existente, que
exige um determinado formato, ou se você está criando o banco de dados a partir do início. Neste último
caso, você pode optar por um formato desktop ou cliente/servidor, dependendo do custo necessário e do
tamanho e quantidade de acessos previsto para os seus dados.
Criando Tabelas
Após definir qual a estrutura que devem ter suas tabelas, você deve criar fisicamente o seu banco de
dados. Dependendo do formato de dados, você pode utilizar um programa específico do banco de dados
ou usar um utilitário fornecido juntamente com o Delphi — o Database Desktop (DBD).

Chame esse utilitário a partir do Delphi, através de seu ícone em:


|Programas|Borland Delphi 4|Database Desktop.

Definindo um Diretório de Trabalho


Antes de começar a criar tabelas, é melhor definir um diretório de trabalho, que é o diretório padrão
usado pelo DBD para criar os arquivos de dados. Você pode salvar tabelas em qualquer diretório, mas
esta opção facilita o trabalho.

Abra o menu File|Working Directory... e digite o diretório onde serão colocados os arquivos do
curso ("C:\CURSODF\TABELAS"). Clique Ok.

Definindo a Estrutura dos Campos

Para criar uma tabela, use File|New|Table.... Você deve escolher qual o formato de dados a ser
utilizado para a tabela (o padrão é "Paradox 7"). Clique Ok para aceitar o default. Agora deverá aparecer
a lista de campos a ser preenchida. No nosso caso, a tabela terá a seguinte estrutura:
Campo TipoTamanho Chave
CodClienteS (automático)*
Nome A 40
Telefone A 10
DataNasc D (automático)

Digite o nome do campo "CodCliente" na coluna "Field Name" e tecle [Tab] ou a seta [ ] para
mudar para a coluna "Type". Em tabelas Paradox, nomes de campos podem ter até 25 caracteres, e
podem incluir letras, dígitos e espaços. (outros formatos de dados têm regras diferentes).

Os tipos de dados em tabelas Paradox são representados por letras. Clique com o botão direito na
coluna "Type" para escolher um tipo de dados ou digite diretamente a letra correspondente. Para esse
campo, escolha o tipo "S" (short), que corresponde a um número inteiro binário, com a mesma faixa de
valores do tipo smallint do Object Pascal. O tamanho do campo é determinado automaticamente, então
pule a coluna "Type".
Em qualquer tipo de tabela, índices pode ser usados para pesquisa ou ordenação de valores. Mas em
tabelas Paradox, existem dois tipos de índices. O índice primário [primary index] ou chave primária
[primary key] determina um valor que deve ser único para todos os registros da tabela, e também
determina a ordem default em que os registros serão percorridos. Um índice secundário [secondary
index] é usado apenas para pesquisa e ordenação.
A coluna "Key", se estiver marcada, diz que o campo é parte da chave primária da tabela. (Só os
primeiros campos da tabela podem fazer parte da chave primária). No nosso caso, marque a coluna
"Key" apenas para o campo "CodCliente". Faça isso com um duplo clique do mouse ou pressionando
qualquer tecla.

Tecle [ ] (seta para baixo) para definir os outros campos. Digite as características dos campos nas
colunas apropriadas. Os campos "Nome" e "Telefone" são alfanuméricos, isto é, do tipo "Alpha". Note
que eles exigem a definição do tamanho do campo, enquanto outros tipos como "Short" e "Date" não
permitem uma determinação de tamanho, porque utilizam um tamanho automático.

O campo "DataNasc" é do tipo "Date", que armazena datas. Note que o ano sempre é armazenado
com quatro dígitos, embora normalmente ele esteja configurado para mostrar apenas dois dígitos.

Salvando a Tabela

Para salvar a tabela, clique no botão "Save As...". Note que o DBD vai mostrar o diretório de
trabalho (definido anteriormente), mas também permite salvar em qualquer outro diretório. Digite o
nome "CLIENTE" e clique Ok. Como é uma tabela Paradox, a extensão de arquivo ".DB" será
adicionada automaticamente, criando um arquivo CLIENTE.DB.

Entrando com Dados

Após criar a tabela, você pode entrar com alguns dados para teste no próprio Database Desktop.
Para isso abra a tabela que você criou com File|Open|Table..., selecionando CLIENTE.DB no diretório.

Você vai ver a estrutura da tabela, mas ainda sem nenhum registro. Quando você abre uma tabela,
ela está em modo de visualização, e não permite edição. Para editar os dados, tecle [F9] ou clique no

botão , que vai colocar a tabela em modo de edição. O mesmo processo volta ao modo de
visualização.

Digite alguns dados de teste para preencher a tabela. Use as setas, ou [Tab] e [Shift+Tab] para
mudar de campos e a seta [ ] para incluir um novo registro. Como o campo "CodCliente" é a chave
primária, você não pode incluir dois registros que tenham o mesmo valor desse campo. Os registros
sempre serão mantidos na ordem desse campo. Para acrescentar um registro no meio, use a tecla [Ins].

Para excluir um registro, use [Ctrl+Del]. Tecle [F2] ou clique no botão quando quiser editar um
campo senão, quando você começar a digitar, o conteúdo anterior é perdido.
Note como o campo DataNasc é automaticamente validado, e não permite datas inválidas como
"32/04/97", "29/02/97" (1997 não é bissexto). Ele é lido e mostrado no formato "dia/mês/ano", se a
configuração do Windows estiver com esse formato.
Nota: o Windows armazena uma configuração padrão para formato de datas, horas, números etc., que é
usada por todos os programas. Ela pode ser alterada através do Painel de Controle, ícone
"Configurações Regionais".
Exemplo: Criando um Formulário "Manualmente"
Vamos criar um projeto para acessar a tabela de clientes. Crie um novo projeto para começarmos com
um formulário em branco.
Para criar um formulário que acessa dados, você deve selecionar alguns componentes da página Data
Access.

A Página Data Access

Selecione a página Data Access na paleta de componentes. Essa página contém componentes
não-visuais que são responsáveis pelo acesso básico aos dados.

Selecione o ícone do componente Table, . Coloque um componente Table no formulário, em


qualquer posição. Altere sua propriedade Name para "tblCliente". Na propriedade DatabaseName desse
componente contém o nome do banco de dados. Como estamos usando um ambiente desktop, o nome do
banco de dados é o caminho do diretório que contém os arquivos de dados ,altere seu conteúdo para
"C:\CURSODF\TABELAS" (ou o diretório onde estiverem os dados no seu computador). Na
propriedade TableName, clique no botão de seta, você verá a lista das tabelas existentes naquele
diretório selecione o nome da tabela "CLIENTE.DB".
O componente Table representa uma tabela do banco de dados, mas ele não tem capacidade de mostrar
os dados visualmente. Você deve acrescentar também um componente DataSource.

Clique no ícone do componente DataSource, , e coloque-o no formulário. Altere seu nome


para "dsCliente". Na propriedade DataSet, selecione da lista o nome do componente Table, "tblCliente",
que determina de onde vem os dados. Isso faz a ligação entre os dois. Este componente serve como uma
ponte entre um componente Table e os controles de dados.
Esses dois componentes fazem o acesso aos dados, mas como eles não são visuais, é preciso usar
controles de dados, que mostram os valores de campos para o usuário, e lêem os valores digitados para
gravar no banco de dados.

Criando os Controles de Dados


Para mostrar e editar os dados, são necessários controles de dados, que interagem com o usuário,
permitindo editar um campo. Você pode criar controles de dados selecionando um dos ícones da página
Data Controls. Por exemplo, o componente DBEdit funciona como o Edit, mas acessa um campo do
banco de dados.

Mas a forma mais fácil de criar os controles de dados é com um recurso do Delphi bastante
utilizado, o editor de campos [fields editor]. Para abrir o editor de campos, clique duas vezes no
componente 'tblCliente' no formulário.
O editor de campos inicialmente aparece com a lista vazia. Isso significa que o componente Table
cria automaticamente objetos de campo, baseado na estrutura da tabela. Para podermos inserir os
controles de dados no formulário, temos que fazer o seguinte: clique com o botão direito no fundo do
editor de campos e clique em "Add fields...". Vai aparecer uma tela com todos os nomes de campos
selecionados. Clique em Ok para adicionar todos os campos.

Agora o editor de campos vai estar preenchido com a lista de campos. Selecione todos os campos ,
arraste e solte em cima do formulário. Vão aparecer vários controles de dados, do tipo DBEdit e, acima
deles, controles do tipo Label com o nome dos campos. O primeiro controle de dados fica logo abaixo do
label "CodCliente". Este é um controle do tipo "DBEdit" e seu nome é "Edit1". O que faz a associação
são as propriedades DataSource e DataField. Movimente os labels para outra posição, à esquerda do
componente correspondente. Altere a propriedade Caption dos componentes Label "CodCliente" para
"Código" e o Label "DataNasc" para "Data Nasc.".
A propriedade DataSource de cada um dos controles de dados(neste caso temos somente o DBEdit),
contém "dsCliente", que é o nome do componente DataSource . A propriedade DataField contém o
nome do campo, por exemplo "CodCliente" no primeiro controle. (Note que a lista de valores de
"DataField" contém todos os nomes de campos da tabela, em ordem alfabética.) Todos os outros
controles de dados têm o mesmo valor de DataSource, mas cada um tem um valor para DataField, que é
o nome do campo correspondente. Ou seja, todos os controles de dados são ligados ao 'dsCliente', que
por sua vez está ligado a 'tblCliente'.

Para alinhar os componentes selecione todos os componentes edit , em seguida clique em algum
componente selecionado com o botão direito e escolha a opção "Align". Será mostrado uma janela para
fazer as modificações, marque as opções de acordo com a figura abaixo:
Se estas opções estiverem marcadas os componentes selecionados iram ficar alinhados à esquerda e na
vertical teram o mesmo espaço. Repita o processo para os componentes Labels. O resultado final será
como a figura abaixo:

Notas: Cuidado ao escolher as opções de alinhamento, você pode piorar a estética do formulário ,
principalmente se selecionar todos os componentes e na opção "Vertical" marcar "Centers", os
componentes iram ficar na mesma posição, para consertar teria que retirar os componentes um a um , se
você não tem muita prática de utilizar estas opções , salve o formulário antes de fazer as alterações.

Adicionando o DBNavigator

Ainda na página Data Controls, selecione o ícone do DBNavigator, e coloque o controle no


formulário, acima dos controles de dados. Altere a propriedade DataSource desse controle para
"dsCliente". Este controle permite ao usuário navegar na tabela, adicionar , editar e excluir registros. A
tabela que será movimentada é a tabela cliente. O DBNavigator contém os seguintes botões:
Ativando a Tabela

Você pode, mesmo sem executar o programa, ativar o componente Table, de forma que ele já
inicie o acesso aos dados. Se você não fizer isso, terá de acrescentar ao programa um comando para abrir
a tabela. Para ativar o componente table , clique nele e altere sua propriedade Active para True.
Os valores do primeiro registro já aparecem nos controles DBEdit, mesmo sem executar o programa.
Nota: Outra forma de ativar a tabela é chamando o método Open dentro do programa.

Testando o Programa

Execute o programa e veja qual o resultado. Você verá inicialmente os dados do primeiro registro

incluído. Para percorrer os registros existentes, use os botões próximo [next] e anterior [prior].

Você pode posicionar no início ou no fim da tabela com os botões primeiro [first] e último
[last], respectivamente.
Para editar valores do registro, basta digitar no controle de dados correspondente. Quando você edita, os

botões confirmar [post] e cancelar [cancel] ficam habilitados. O primeiro permite você
confirmar as edições, salvando o registro. O outro cancela todas as edições e retorna os registros para os
valores anteriores. Se você mover de registro, a edição é automaticamente confirmada.

Para incluir um novo registro, use o botão incluir [insert], que vai abrir um registro em branco.

Preencha os valores e clique no botão confirmar para salvar o registro, ou cancelar para
cancelar a inclusão.

Os registros vai aparecer na ordem determinada pelo valor do campo chave, "CodCliente".

Para excluir um registro, clique no botão excluir [delete]. O Delphi vai mostrar uma mensagem
predefinida de confirmação e você deve clicar Ok para confirmar a exclusão.

Finalmente, o botão atualizar [refresh] só tem utilidade quando há vários usuários alterando a
mesma tabela, como numa rede. O que ele faz é trazer para a tela os dados de um registro que tenha sido

modificado por outro usuário. E o botão editar [edit] pode ser usado para iniciar a edição de um
registro mas geralmente não é necessário.

Antes de salvar vamos mudar a propridade Name do formulário para 'formCadCliente' e a


propriedade Caption para 'Cadastro de Cliente' . Salve o projeto como CADCLIENTE e
CADCLIENTEP.
Exemplo: Usando o Database Form Wizard
A forma mais rápida de criar um programa que acessa bancos de dados em Delphi é utilizando o
Database Form Wizard [assistente de formulário de banco de dados]. Com essa ferramenta, não é preciso
programação nem mesmo criação de componentes, basta especificar qual a tabela a ser acessada e ele
cria um formulário contendo tudo que é preciso.
O que é feito pelo Database Form Wizard (DFW) é o que fizemos manualmente no "Cadastro de
Cliente", e você pode personalizar o formulário criado da forma que você quiser. Mas na maioria das
vezes, ele facilita o trabalho inicial.

Criando um campo autoincremento


Quando definimos um campo sendo autoincremento, a coluna que foi definida com este tipo tem um
único valor que é gerado automaticamente pelo sistema. O conteúdo desta coluna ,conseguimos obter
somente quando o registro for gravado. Outro detalhe é que não podemos informar seu conteúdo durante
uma alteração ou inclusão de dados.
Como exemplo vamos criar a tabela de produto. Para criar uma nova tabela no Database Desktop , no
menu File|New|Table... e digite os campos como abaixo:
Campo TipoTamanho Chave
CodProduto + *
Nome A 40
QtdEstoque S
PrecoVenda N
CodForncedorS

No Paradox o que identifica um campo sendo autoincremento é o tipo sendo + (AutoIncrement) .


Podemos ter somente um coluna com esse tipo na tabela.
Note que o campo PrecoVenda vai usar o tipo N (Number). Esse tipo equivale ao tipo double do Object
Pascal, isto é, permite armazenar números inteiros bem como números com parte fracionária.

Criando um Formulário para Acessar a Tabela com Form Wizard

Crie um novo projeto no Delphi e depois acione o item de menu Database| Form Wizard.... O
DFW é dividido em várias telas de informações. A primeira tela oferece dois grupos de opções:
Mantenha as opções padrão: "Create a simple form" em "Form Options" indica que vamos criar um
formulário "simples", que acessa uma única tabela e "Create a form using TTable objects" em "DataSet
Options" indica o tipo de componente de acesso a dados utilizado. Veremos as outras alternativas mais
tarde.

Clique em "Next". Agora você deve selecionar a tabela a ser utilizada. Selecione PRODUTO.DB,
que foi criada anteriormente e clique em Next. Na próxima tela, você pode escolher apenas alguns
campos ou todos os campos da tabela para utilizar no formulário. Para selecionar todos os campos, clique
no botão [>>] e depois em Next.

Você pode escolher um lay-out para o formulário. Escolha o padrão "Horizontally" (Horizontal) e
clique em Next. A última tela tem uma opção "Generate a main form" [gerar um formulário principal].
Se ela estiver marcada, o novo formulário será o principal do projeto. Deixe essa opção marcada, e clique
no botão "Finish".

Testando o Programa
Note que o formulário gerado tem vários componentes já posicionados, incluindo controles de dados
para cada campo da tabela. Cada controle de dados é responsável por ler e gravar os dados de um campo.

Você pode alterar a posição e o tamanho do formulário como quiser. Altere o Caption do
formulário para "Cadastro de Produto" e a propriedade Name para formCadProduto. Note que para
alterar uma propriedade do formulário você deve selecionar o formulário. Se um componente estiver
selecionado, tecle [Esc] até não aparecer nenhuma indicação de seleção.
O que o DFW faz quando cria um novo formulário é o mesmo processo que fizemos para criar o
formulário manualmente.
Vamos alterar a propriedade Name do componente table para 'tblProduto', os componentes que tiverem
alguma propriedade associada com o componente Table o Delphi automaticamente muda o nome
também , o nome só não é mudado em linhas de código . No componente DataSource colocar o nome
sendo 'dsProduto', como o nome do componente Table foi mudado precisamos também alterar um trecho
de código no formulário que abre a tabela (no exemplo anterior usamos a propriedade Active). Selecione
o formulário (tecle [Esc] algumas vezes se houver algum componente selecionado) e, no Object
Inspector, clique na página de eventos. Clique duas vezes no valor do evento OnCreate, e irá aparecer
um procedimento de evento contendo:
Table1.Open;

Altere esse comando para:


tblProduto.Open;

Salvando o Projeto

Antes de salvar o projeto, devemos remover o formulário inicial, que não tem função nenhuma.
Isso é preciso porque o Database Form Wizard cria um novo formulário, mas não altera os anteriores.
Clique em Project|Remove from project..., selecione Form1 e clique OK.

Agora salve o projeto, dando os nomes de CADPRODUTO e CADPRODUTOP. Execute o projeto


e cadastre alguns produtos.
Resumo dos Componentes de Dados
Neste capítulo fizemos alguns projetos que utilizam os componentes de acesso a dados e os controles de
dados do Delphi. De forma geral, o fluxo dos dados nesse tipo de programa é:

Os componentes de acesso a dados, como o componente Table, lêem e gravam no banco de dados. O
componente DataSource faz a ligação entre estes componentes e os controles de dados, como o DBEdit.
Apelidos de Bancos de Dados
Quando você desenvolve uma aplicação de banco de dados, você deve especificar o diretório onde
estarão os seus dados (se for um banco de dados local) através da propriedade DatabaseName de um
componente Table. Mas com isso o programa fica dependente do local onde estão armazenados os dados.
Se você mover as tabelas para outro diretório, o programa não vai mais funcionar como antes.
Para resolver esse problema, existem os alias do BDE. Um alias, ou apelido, é um nome que pode ser
usado nos programas em lugar de especificar o diretório. Num banco de dados cliente/servidor, um alias
também é a forma mais usada para acesso aos dados, porque ele guarda também outros parâmetros de
configuração.

Os apelidos são armazenados em um arquivo de configuração do BDE e são globais a todas as


aplicações em Delphi. Você pode criar ou alterar apelidos usando o utilitário Database Desktop (DBD),
através do comando Tools|Alias Manager, mas é mais fácil usar um utilitário que faz parte do próprio
Delphi, o Database Explorer. Para isso clique em Database|Explore no menu do Delphi. Você verá uma
tela como a seguinte:

O Database Explorer mostra do lado esquerdo, na página "Databases", uma árvore contendo os apelidos
que já foram definidos. Ele permite também visualizar e alterar os dados de qualquer tabela, através de
um apelido.
Para criar um novo alias, clique em Object|New... Você deve escolher o tipo de banco de dados que será
usado. Os tipos disponíveis dependem de quais drivers estão instalados no computador. No nosso caso,
deixe selecionado o tipo STANDARD (acesso a Paradox e dBase), que é o padrão. Clique Ok e o novo
alias aparece na lista, com o nome provisório de "STANDARD1". Digite "CursoDelphi" para o alias.
Note que um triângulo aparece à esquerda do nome, que indica que esse nome ainda não foi salvo na
configuração:

Do lado direito, estão os parâmetros de configuração do alias. O único parâmetro que importa no caso é o
PATH. Clique neste e digite "C:\CURSODF\TABELAS" (o diretório onde estão os arquivos do curso).

Agora clique no botão (Apply) para salvar as alterações.


Se você quiser ver as tabelas que estão neste alias, clique no sinal de [+] ao lado do nome para abrir o
alias, depois abra o ícone "Tables" e você verá as tabelas definidas. Ao clicar em uma delas, por
exemplo, CLIENTE.DB, você pode visualizar informações gerais sobre a tabela, ou visualizar e alterar
os dados, clicando na página "Data":
Note que aparecem botões de controle na parte superior, semelhantes ao DBNavigator. Para fechar a

conexão com o alias, clique em CursoDelphi e no botão (Open Close). Agora feche o Database
Explorer (ou SQL Explorer) e retorne ao Delphi.
Abra um dos projetos anteriores, como o projeto CADCLIENTeP. No componente Table, primeiro altere
a propriedade Active para False. Depois selecione a propriedade DatabaseName, abra a lista de valores
e selecione "CursoDelphi" e altere novamente a propriedade Active para True. Faça a mesma alteração
para o projeto CADPRODUTOP.
A lista de valores na propriedade DatabaseName, sempre mostra quais são os aliases cadastrados. O
programa vai mostrar os mesmos dados que antes, mas agora ele não é mais dependente do diretório
"C:\CURSODF\TABELAS". Se você quiser mover os dados para outro lugar, basta alterar o alias
novamente, usando o Database Explorer ou o Database Desktop.
Capítulo 13
Recursos Adicionais

Definindo Tabelas

Criando o Menu Principal

Alterando o Formulário Principal

Usando o Controle DBGrid


Definindo Tabelas
Durante o curso, iremos desenvolver um mini-sistema de controle de vendas, no qual teremos um cadastro de
clientes, um controle das vendas efetuadas para cada cliente e de itens vendidos para cada venda. Isso vai envolver
várias tabelas adicionais no nosso banco de dados.
O modelo abaixo mostra como estão relacionadas as tabelas que serão utilizadas no nosso exemplo:

Volte ao Database Desktop para definir tabelas adicionais e alterar a estrutura da tabela de cliente, criada
anteriormente.

Reestruturando a Tabela "Cliente"


Após criar uma tabela, você sempre pode alterar a estrutura desta, acrescentando novos campos, removendo
campos existentes ou alterando o tipo de um campo. Os dados anteriores são preservados, sempre que possível,
mas obviamente, se você reduzir o tamanho de um campo, ou mudar seu tipo para uma faixa de valores menor,
você pode perder parte dos dados.

Para reestruturar a tabela, use Table|Restructure Table.... A mesma janela com a estrutura dos campos
aparece. Vamos adicionar um campo no meio da estrutura, entre Nome e Telefone. Para isso, clique no campo
Telefone e pressione [Insert]. Digite "CPF" no nome do campo, "A" para o tipo e 11 para o tamanho.

Agora adicione dois campos no final: clique no campo DataNasc, pressione a seta para baixo e digite os
novos campos: "Cidade", "A", 25 e "Estado", "A", 2. A estrutura da tabela agora é a seguinte:
Nome do CampoTipoTamanho Chave
CodCliente S (automático)*
Nome A 40
CPF A 11
Telefone A 10
DataNasc D (automático)
Cidade A 20
Estado A 2

Criando um Índice Secundário

Antes de salvar a tabela modificada, para facilitar as pesquisas e ordenações por nome, vamos criar um
índice secundário para o campo Nome. Clique na lista à direita, abaixo de "Table Properties", e escolha a opção
"Secondary Indexes". Agora o botão "Define..." estará ativo. Clique nesse botão para ver a janela de definição de
índices.

Na lista da esquerda estão todos os campos da tabela. Para definir que o campo "Nome" fará parte do índice,
selecione-o e clique na seta para a direita.
No quadro "Index Options", abaixo da lista de campos, existem opções que você pode alterar. A opção
"Maintained" faz com que o índice seja mantido automaticamente a cada alteração, que é o mais usado. Se estiver
desabilitada, o índice só é atualizado quando você tentar fazer uma pesquisa, o que toma certo tempo.
A opção "Case sensitive" só tem efeito em campos alfanuméricos. Se habilitada, as pesquisas no índice fazem
diferença entre maiúsculas e minúsculas.

Agora clique em OK para salvar o índice. O Database Desktop vai pedir um nome para o índice. O nome
segue as mesmas regras de nomes de campos. Nesse caso, coloque o nome de "IndNome". Mais tarde veremos
como utilizar um índice secundário no programa.

Para salvar a tabela modificada, clique no botão "Save". Se quiser, entre com dados para o campo CPF nos
registros.
Nota: você também pode criar um índice que utiliza vários campos da tabela. Basta adicionar todos os campos
utilizados à lista da direita.

Criando a Tabela "Venda"

Vamos criar agora a tabela de vendas. Para criar uma nova tabela no DBD, faça como antes, usando
File|New|Table... e digite os campos como abaixo:
Campo TipoTamanhoChave
CodVenda S *
CodClienteS
DataVenda

O campo "CodVenda" é a chave primária da tabela, que identifica um registro de venda. O campo "CodCliente"
está relacionado com um registro da tabela "Cliente", indicando que esta venda se refere àquele cliente.

Clique em "Save As..." para salvar a tabela e digite o nome "VENDA".

Criando Integridade Referencial

Vamos alterar a estrutura da tabela "Venda". Como o campo "CodCliente" nesta tabela está relacionado com
um registro da tabela "Cliente" , não podemos deixar que neste campo seja colocado código do cliente que não
exista na tabela "Cliente" e até mesmo ao excluir um cliente não pode ser permitido excluir um cliente que tenha
venda , a não ser que exclua as vendas ,para depois excluir o cliente desejado (chamamos este processo de
exclusão por cascata).
Então na tabela "Venda" vamos alterar sua estrutura criando "Integridade Referencial" na coluna 'CodCliente' com
a tabela "Cliente" , para isto clique na lista "Table Properties", escolha a opção "Referential Integrity" e clique no
botão "Define...". Selecione o campo "CodCliente" na lista da esquerda, e clique na seta para movê-lo para a
direita. Clique em OK para salvar o índice e dê o nome de "IntCliente". Iremos criar integridade referencial
somente nesta tabela, a título de demonstração.
Notas: Utilizar integridade referencial no Paradox, não é muito seguro, pois, ao colocar o programa para funcionar
principalmente em rede ocorre problemas do tipo "Arquivo Corrompido" ou "Índice Fora de Data". Com banco de
dados Client/Server utilizar a integridade referencial é bem mais seguro. No caso do Paradox uma saída seria tratar
a integridade pelo Delphi.

Clique em "Save ..." para salvar a tabela "VENDA".

Criando a Tabela de Itens

No DBD, use File|New|Table... como antes e defina a seguinte estrutura para a tabela:
Campo TipoChave
CodVenda S *
CodProdutoS *
Quantidade S

Defina também um índice secundário no campo CodVenda. Isso é necessário para que o Delphi possa tratar
o relacionamento mais tarde. Para definir um índice secundário, faça como antes: selecione "Secondary Indexes",
clique no botão "Define...", selecione o campo CodVenda e clique Ok. Dê o nome de "IndVenda" para o índice e
clique Ok.

Salve a tabela com o botão "Save as...", dando o nome de ITEM.db (lembre-se que a extensão não precisa
ser digitada). Depois feche o DBD e retorne ao Delphi.
Nota: nomes de tabelas podem ter até 253 caracteres (fora extensão), mas é melhor usar um nome com oito ou
menos caracteres para manter compatiblidade com programas mais antigos.
Criando a tabela Fornecedores

No DBD, use File|New|Table... como antes e defina a seguinte estrutura para a tabela:
Campo Tipo Tamanho Chave
CodFornecedor S *
Nome A 60

Defina também um índice secundário no campo 'Nome' . Para definir um índice secundário, faça como
antes: selecione "Secondary Indexes", clique no botão "Define...", selecione o campo 'Nome' e clique Ok. Dê o
nome de "IndNome" para o índice e clique Ok.

Salve a tabela com o botão "Save as...", dando o nome de "FORNECEDOR.DB".


Criando o Menu Principal
Vamos criar um programa onde utilizaremos os formulários criados anteriormente, além de alguns
novos. Crie um novo projeto no Delphi. O formulário criado durante a criação do projeto será o
formulário principal , este formulário será a janela a partir da qual o usuário acessa as outras janelas do
programa.

O MenuDesigner: criando menus


Para utilizar um menu em um programa, precisamos inserir um componente MainMenu no
formulário. Ele é o primeiro da página Standard: . Após colocá-lo no formulário, clique duas vezes
nele para abrir o Menu Designer, que tem uma aparência como a seguinte:

O Menu Designer mostra uma simulação do menu e permite você criar os itens de menu que você vai
utilizar. À medida que você criar itens de menu, o Object Inspector vai mostrar as propriedades do item
selecionado.
Para criar o primeiro item, simplesmente digite "Cadastro" (sem as aspas) e tecle [Enter]. O Menu
Designer vai mudar para a seguinte aparência:
À direita e abaixo do item, sempre ficam disponíveis retângulos vazios que você pode preencher para
criar novos itens. Se você não os preencher, eles não vão aparecer no menu final.
Agora digite o texto do próximo item: "Cliente" e tecle [Enter]. O marcador vai passar para a
próxima linha. Crie mais um item "Produto". Agora crie o item "Fornecedor". Em seguida crie uma
barra separadora no próximo item. Para criar uma barra separadora, digite "-" (um sinal de menos).
Depois da barra, crie um item "Venda", crie novamente uma barra separadora e finalmente crie mais um
item "Sair". A aparência final vai ficar como na figura:

Quando você digita o texto do menu, você está alterando a propriedade Caption de cada item de menu.
O Object Inspector automaticamente altera a propriedade Name de cada item para ser igual ao valor do
Caption, com um número acrescentado ao final (por exemplo, Cadastro1).

Nós vamos mudar esses nomes para definir um padrão mais descritivo. Clique no item "Cadastro"
e na propriedade Name, troque "Cadastro1" (o nome que o Delphi colocou automaticamente), por
"menuCadastro". Faça de forma semelhante para os outros itens, de acordo com a tabela:
Cliente menuCadClientes
Produto menuCadProdutos
Fornecedor menuCadFornecedores
- N!(separador)
Venda menuCadVendas
- N2(separador)
Sair menuCadSair

Agora clique no item vazio à direita do item Arquivo e digite "Editar". Abaixo desse item, crie
subitens de acordo com a figura:

A propriedade name altere de acordo com a tabela abaixo:


Consultas menuConsultas
Cliente por Estado menuconCliEstado
Vendas por Cliente menuConVendasTotais

Nota: O Menu Designer tem outras facilidades também. Para inserir um item de menu no meio,
pressione a tecla [Ins]. Para excluir um item de menu, use o [Ctrl+Del]. Para definir um nível adicional
de submenu, use [Ctrl+seta para direita].

Tratando Eventos de Menus

Feche a janela do Menu Designer e retorne ao formulário "real". O menu vai estar inserido no
formulário, exatamente com a aparência que foi definida é que ele irá aparecer para o usuário, mas não
permite modificação.
Você pode clicar nos menus "Arquivo" ou "Editar" para ver os seus subitens. Se você executar o
programa, também pode clicar os subitens não vão fazer nada, pois ainda não fizemos nenhum
tratamento de eventos. Para tratar eventos em um sub-item de um menu, basta abrir o menu e clicar no
sub-item (no formulário, mas não no Menu Designer).

Clique no menu "Cadastro" e depois no item "Sair". O Delphi vai abrir um procedimento de evento
para esse item:
procedure TForm1.menuCadSairClick(Sender:TObject);
begin

end;

Esse item deve terminar o programa quando clicado. Para isso, basta fechar o formulário,
utilizando um comando (na verdade, um método) chamado Close:
Close;
O método Close fecha o formulário e, como é o único formulário do programa, ele finaliza a execução.

Execute o programa e clique em Arquivo|Sair. O programa deverá finalizar e retornar ao modo de


projeto.

Acrescente no Menu Designer o item de menu abaixo Crie a seguinte estrutura de menus:
Relatório menuRelatorios
Clientes menuRelClientes
Vendas por ProdutomenuRelVendasProduto

Teclas de Acesso e Teclas de Atalho


Apesar do Windows geralmente ser mais fácil de usar com o mouse, é bom ter em mente que um
programa Windows também deve ser fácil de usar apenas com o teclado, sem utilizar o mouse. Isso entre
outras coisas, envolve uma forma de selecionar menus através do teclado.
O Windows já fornece automaticamente uma interface padronizada de teclado para os menus. Para
verificar isso durante a execução do programa, pressione a tecla [F10]. Agora você pode usar as teclas [
]e[ ] para mudar entre os itens na barra de menu e [Enter] para abrir um menu. Quando o menu
está aberto, as teclas [ ] e [ ] são usadas para movimentação entre os itens do menu, [Enter] para
acionar o item selecionado, ou [Esc] para fechar o menu aberto.
Além dessa facilidade, você pode definir uma tecla de acesso para cada menu e cada item dentro de um
menu. Uma tecla de acesso aparece como uma letra sublinhada no texto do menu. Para um menu da barra
principal, o usuário pode abrir o menu pressionando [Alt+letra], onde letra é a tecla de acesso. Quando o
menu está aberto, um item do menu pode ser acionado pressionando-se sua tecla de acesso (sem usar
[Alt]).

Clique duas vezes no componente (MainMenu) no formulário para chamar o Menu Designer.
Clique no menu "Cadastro" e na propriedade Caption. Para inserir uma tecla de acesso no 'C', digite
&Cadastro. Faça de forma semelhante para todos os outros itens (não altere as barras separadoras):

Item Tecla de Acesso


&Clientes &C
&Vendas &V
&Produtos &P
&Fornecedores &F
&Sair &S
C&onsultas &O
Clientes por &Estado &E
&Vendas por Cliente &V
&Relatório &R
&Clientes &C
&Vendas por Produto&V

Agora, o item Cadastro|Cliente, por exemplo, pode ser acionado com as teclas [Alt+C,C].
Outra forma de acionar itens de menu é através de teclas de atalho [shortcut keys]. A diferença é que
teclas de atalho podem ser usadas mesmo quando o menu não está aberto, esteja onde estiver o foco de
teclado, e não precisam ser letras. Se você abrir um menu do Delphi, verá que teclas de atalho aparecem
à direita do item, como [Ctrl+S], [F5], [Ctrl+F7] etc. Só itens de menu podem ter teclas de atalho (não os
menus da barra principal).
Ainda no Menu Designer, clique no item "Clientes" e na propriedade Shortcut [atalho]. Selecione
da lista a opção "Ctrl+K". Para "Vendas...", coloque "Ctrl+E".Para "Produtos" , coloque "Ctrl+P".
Note também que o item Sair não tem uma tecla de atalho. Isso porque já existe um atalho definido pelo
Windows para sair de um programa qualquer: [Alt+F4]. De forma geral, é bom tentar economizar teclas
de atalho, e só colocar essas teclas nos itens mais utilizados do programa. Se você tem muitas teclas de
atalho, o usuário não conseguirá memorizá-las, e isso desvia do propósito original desse tipo de acesso.
Nota:Você não deve repetir a mesma tecla em um grupo de itens do mesmo nível. O Delphi permite fazer
isso, mas a tecla não vai funcionar da forma esperada. Já itens em menus diferentes podem usar a
mesma tecla sem problemas.
Nota: Teclas de atalho devem ser únicas entre todos os itens de menu.
Execute o programa e teste as teclas. Você não precisa abrir um menu para acionar a tecla
correspondente a um item.
Note que nos itens separadores, o Delphi coloca momes padrão(N1 e N2), que você não precisa alterar. A
propriedade ShortCut fornece um acesso rápido pelo teclado em alguns itens.
Alterando o Menu Principal
Alterando Propriedades

Reduza o tamanho do formulário na vertical, ou a propriedade Height, de forma que ele não ocupe
espaço na tela, deixando apenas a barra de menu visível. Crie um procedimento para o evento OnCreate
, e coloque os códigos abaixo:
left := 0;
top := 0;
A barra de menu irá ficar posicionada no topo do DeskTop.

Altere o Name do formulário para 'FormPrincipal'. Num projeto grande, é importante dar nomes
distintos aos vários formulários. Altere sua propriedade Caption para "Controle de Estoque". Salve o
projeto e chame a unidade do formulário principal de VENDPRIN.PAS e o projeto de VENDAS.DPR.

Acrescentando os formulários anteriores


Neste projeto iremos acrescentar os formulários criados anteriormente. Para cada um dos fomulários,
você deve clicar no ícone (Add File to Project).

Adicione as unidades CADCLIENTE.PAS e CADPRODUTO.PAS.

Associando os formulários aos itens de menu


O formulário principal, que contém os itens de menu, irá chamar cada um dos formulários criados. Para
que ele possa fazer isso, a unidade principal deve ter uma cláusula uses, com os nomes de todas as
unidades.
Fique posicionado na unit "VendPrin" . nessa unidade logo após a palavra implementation, acrescente:
uses CadCliente, CadProduto;

Agora tecle [F12] para voltar ao formulário. Clique no item de menu Cadastro|Clientes e, no
procedimento de evento, coloque o seguinte comando:
formCadCliente.Show;

Iremos fazer a mesma coisa para o Produto. Clique no item de menu Cadastro|Produto e , no
procedimento de evento, coloque :
formCadProduto.Show;

Salve o projeto e depois execute o programa.Note que no início da execução, aparece apenas o
formulário principal. Ao clicar em um item de menu, por exemplo Cadastro|Clientes, aparece o
formulário correspondente. Você pode clicar novamente no formulário principal e abrir outro formulário.
Como os formulários não são modais, vários formulários podem estar na tela ativos simultaneamente.
Usando o Controle DBGrid
Além de usar os controles de dados, para mostrar um formulário da forma "tradicional", usando um
controle para cada campo, você pode usar um controle DBGrid, que permite visualizar vários registros ao
mesmo tempo.

Criando os Componentes de Dados

Vamos criar um novo formulário e utilizar a mesma tabela de clientes, mas com um componente
DBGrid. Para criar um formulário ir no menu File|New Form . Selecione a página Data Access e
coloque no formulário um componente Table e um componente DataSource. Defina suas propriedades
como antes:

Table
Name: tblCliente
DatabaseName: CursoDelphi
TableName: CLIENTE.DB

DataSource
Name: dsCliente
DataSet: tblCliente
Note que em DatabaseName estamos usando o alias definido anteriormente, "CursoDelphi", que pode
ser escolhido na lista de valores. De agora em diante, usaremos esse nome para fazer referência ao
diretório.

Agora abra a página Data Controls e selecione o ícone (DBGrid). Coloque o componente no
formulário e altere sua propriedade DataSource para "dsCliente". Agora volte ao componente Table e
altere sua propriedade Active para True. A grade de dados já vai mostrar os dados da tabela e você pode
usar as barras de rolagem para ver mais registros.

Altere a propriedade Align do DBGrid para "alClient", desta forma o DBGrid irá preencher todo o
formulário. Altere também a propriedade Name do formulário para "formConsClienteEstado".

Manipulando dados no DBGrid

Para verificar o funcionamente do DBGrid , temos que no menu principal chamar o formulário.
Posicione no formulário da Unit VendPrin , no clique no menu Consultas|Clientes por Estado e
coloque o comando a seguir:
procedure TformPrincipal.menuConCliEstadoClick(Sender: TObject);
begin
formConsClienteEstado.show;
end;

Salvar esta unit como CONSCLIENTEESTADO. Acrescente na unit VendPrin na seção


implementation, uma uses para a unit ConsClienteEstado. Ficando desta forma:
uses CadCliente, CadProduto, ConsClienteEstado;
Execute o programa, clique no menu Consultas|Clientes por Estado e verifique o funcionamento do
DBGrid, que é bem semelhante a uma grade de dados do Database Desktop. Você pode usar as setas para
mover entre campos (linhas) e registros (colunas). Quando você tenta sair de um campo, se você digitou
um dado inválido, imediatamente verá uma mensagem de erro (por exemplo, no campo DataNasc).
Quando você sai de uma linha, os dados do registro são imediatamente gravados. Para alterar um campo,
editando o valor existente, pressione [F2]. Para inserir um registro, você pode descer abaixo do último
registro, ou teclar [Insert]. Para excluir um registro, tecle [Ctrl+Del].
Nota: você também pode alterar características das colunas se você clicar duas vezes no próprio
DBGrid. Essas alterações têm prioridade sobre as propriedades dos campos da tabela e você tem outras
opções como, por exemplo, mudar a cor do campo. Mas geralmente não é necessário alterar opções no
próprio DBGrid.

Usando o Editor de Campos

Vamos posicionar na unit "CadCliente.pas", caso esteja na unit pressione a tecla [F12] para
mostrar o fomulário.Até agora utilizamos os objetos de campos para criar os controles de dados, mas ele
tem outras finalidades também colocar máscara para a edição de determinados campos.
Quando você utiliza o componente Table, ele cria internamente, para cada campo da tabela, um objeto
TField correspondente, que tem propriedades para definir a formatação do campo, tamanho com o qual
ele aparece na tela etc. Se você quiser alterar as propriedades dos campos em tempo de projeto, você
precisa criar uma lista persistente de objetos, e isto é feito no editor de campos.

A estrutura da tabela "Cliente" foi alterada , foi acrescentado alguns campos que ainda não foram
adicionados ao editor de campos. Clique duas vezes no componente 'tblCliente' no formulário para abrir
o editor de campos. Quando a lista de campos está vazia, significa que todos os campos terão as
propriedades default. Para adicionar os objetos de campo que estão faltando, clique com o botão direito
no fundo e clique em "Add fields...", será mostrado somente os campos que foram acrescentados a tabela
, como todos os campos estão selecionados clique em Ok para adicioná-los. Agora quando você clicar em
um dos campos na lista, as propriedades do campo (do objeto TField) aparecem no Object Inspector.

No editoe de campos clique em "DataNasc" vamos mudar o formato de data que é usado para
mostrar as datas. Altere a propriedade DisplayFormat, colocando o valor "dd/mm/yyyy".

Alterando a Máscara de Edição


Cada campo pode ter também uma máscara de edição, que define alguns caracteres que aparecem no
campo, mas que o usuário não precisa digitar. No nosso caso, vamos colocar uma máscara no campo
CPF.
No editor de campos, clique no campo CPF. No Object Inspector, clique na propriedade
EditMask. Para alterar a máscara de edição, você pode digitar um valor diretamente para essa
propriedade, mas é mais fácil usar o editor especializado. Clique duas vezes no valor ou uma vez no
botão de reticências.

Na máscara de edição, digite: 999.999.999-99. Cada nove na máscara corresponde a uma posição
digitada pelo usuário, que só permite números, mas os outros caracteres sempre aparecerão na máscara, e
o usuário não precisa digitar.

É importante também desmarcar o quadro "Save literal characters". Se esse quadro estiver
marcado, os pontos e traços da máscara serão incluídos no campo, quando ele for gravado. Se ele ficar
desmarcado, só os caracteres digitados serão incluídos no campo. Note que quando você altera essa
opção, um 0 ou 1 é adicionado ao final da máscara.
A opção "Character for blanks" define qual o caractere que será usado nos espaços vazios durante a
digitação. Geralmente é melhor manter o padrão, '_' (sublinhado). Quando você altera essa opção, o
caractere é adicionado ao final da máscara.
Para testar a máscara, na mesma janela você pode digitar alguma coisa em Test Input. Se você digitar e
não conseguir sair do campo (o Delphi vai mostrar uma mensagem), tecle [Esc] e tente novamente.

Vamos definir uma máscara para edição no campo "DataNasc". Então no editor de campos, clique
no campo "DataNasc". No Object Inspector, clique na propriedade EditMask , em "Sample Masks"
clique na opção "date" , esta opção contém uma máscara para data. O conteúdo de "Input Mask" será
'!99/99/00;1;_', substitua o '0' por '9', a diferença de utilizar 0 ou 9 é que quando colocamos '0' fazendo
parte da máscara, quer dizer que naquele local o usuário é obrigado a informar algum número, se utilizar
'9' aceita somente números, mas não é obirgatório.É importante que a opção "Save literal characters"
esteja marcada , pois, as barras são gravadas no campo.
Capítulo 14
Validação e Pesquisas

Exemplo: Cliente por Estado

Exemplo: Clientes - Alterando

Validação de Campos e Registros

Tratamento de Exceções

Pesquisas na Tabela

Blocos Protegidos

Usando Outros Controles de Dados


Exemplo: Cliente por Estado
Iremos realizar algumas alterações no formulário "formConsClienteEstado", portanto , abra este
formulário.Atualmente este formulário mostra todos os clientes cadastrados, vamos realizar uma procura
onde iremos mostrar os Clientes por um determinado Estado escolhido pelo usuário.

Altere a propriedade Caption para 'Cliente por Estado' e modifique também a propriedade Align
do DBGrid para 'alBottom' e aumente o tamanho do fomulário para acrescentar um Componente

GroupBox( ) . Altere sua propriedade Caption para "Parâmetros", como na figura abaixo:

Altere o nome do controle de edição para 'editEstado'e sua propriedade CharCase para
'ecUpperCase', deixe a propriedade Text vazia. Isso faz com que todas as letras digitadas sejam
convertidas em maiúsculas. Chame o botão de 'btnProcurar' e mude sua propriedade Default para True.

No evento OnClick do botão 'btnProcurar', vamos alterar a propriedade Filter do componente


Table dinamicamente,com esta propriedade iremos definir um filtro que irá procurar os clientes que
pertencem ao estado indicado.Digite o seguinte:
tblCliente.Filter := 'Estado = ''' + editEstado.text + '''';
tblCliente.Filtered := true;
tblCliente.Open;
Note que para modificar a propriedade Filter, usamos vários apóstrofos na sintaxe. Cada dois apóstrofos
na string representam um apóstrofo na string resultante. Por exemplo, se editEstado = 'GO' a string do
Filter vai conter :
Estado = 'GO'
Mas a propriedade Filter funciona somente se a propriedade Filtered for True.Após a alteração da
propriedade é necessário abrir o componente Table novamente para ver o resultado.

Salve o formulário e execute. Para testar informe alguns estados e clique no botão 'Procurar'.
Notas: O paradox é "case sensitive", portanto ele faz diferenciação entre letras maiúsculas e minúsculas.
Exceção somente quando cria um índice secundário para o campo e a opção "case sensitive" deve estar
desmarcada.
Exemplo: Clientes - Alterando
Neste capítulo, faremos várias alterações no formulário "formCadCliente". Portanto, abra esse
projeto.
Note que ainda faltam no formulário controles de dados para os novos campos que foram acrescentados
por último: CPF, Cidade e Estado. Os objetos foram criados , falta criar os controles de dados.

Clique duas vezes em 'tblCliente' (o único componente Table do formulário) para abrir o
editor de campos. Agora arraste, um de cada vez, os campos CPF, Cidade e Estado para o formulário.
Deixe o formulário com a aparência semelhante a figura abaixo:

Mantenha o editor de campos aberto.


Validação de Campos e Registros
Uma máscara de edição proporciona uma forma automática de validação de campos, mas você pode
também adicionar seu próprio código de validação, executado pelo Delphi quando for necessário.
Existem duas formas de validação:
· Validação de Campo: é feita logo quando o usuário alterna para outro campo, no mesmo registro ou em
outro. Se a validação falha, o usuário não consegue mudar de campo.
· Validação de Registro: é feita apenas quando o usuário muda de registro, e se a validação falha, o
usuário não pode mudar de registro. Pode ser usada para fazer uma verificação que envolva mais de um
campo.

Validando Campos
Para fazer a validação de campo, você deve tratar o evento OnValidate. Esse é um evento do
componente TField associado ao campo.

Vamos adicionar uma validação ao campo CPF. Para este projeto foi criado uma unit independente
chamada GeralVenda que contém uma função para fazer a verificação do CPF, a idéia não é explicar
como funciona a verificação de CPF , mas de como fazer esse tipo de verificação no Delphi, segue
também nesta unit uma rotina para verificação de CGC.Então iremos utilizá-la para verificar se um
determinado CPF foi informado corretamente.O nome da função é ValidaCPF ela retorna true se o CPF
estiver correto, temos que passar como parâmetro um valor do tipo 'String'.
Notas:Para verificar se um cpf é válido ou não, primeiro certifique que ele tenha 11 dígitos. Os dois
últimos dígitos são utilizados para verificação.
Declare algumas variáveis:
var soma, somaDig, pos, resto : smallint;
A constante ordZero contém a posição do número 0 na tabela Ascii.
A verificação do penúltimo dígito é feita através da soma dos dígitos até a posição , cada dígito é
necessário multiplicar por 11 e subtrair pela sua posição, é necessário também ter a soma de todos os
dígitos até a posição 9..
for pos := 1 to 9 do
begin
soma := soma + (ord(textoCPF[pos]) - ordZero) * (11 - pos);
somaDig := SomaDig + (ord(textoCPF[pos]) - ordZero);
end;
Verificação do Penúltimo Dígito: Divida o conteúdo da variável soma por 11,faça a subtração do resto
pelo número 11 e atribua a uma variável, se o conteúdo desta variável for maior que 9, altere o conteúdo
da variavel para 0, em seguida verifique se o conteúdo desta variável é diferente do penúltimo digito do
CPF, se for CPF é inválido, caso contrário é necessário verificar o último dígito.
resto := 11 - soma mod 11;
if resto > 9 then resto := 0;
if resto <> ord(textoCPF[10] - ordZero then
exit; {DV errado}
Para verificar o último dígito é nessário somar os resultados encontrados nos calculos anteriores. Então
Soma + SomaDig + 2 * resto , dividir este resultado por 11, o resto subtrair por 11 , se o resultado
obtido for maior que 9 , atribuir 0 a esse resultado, em seguida verificar se o resultado encontrado é igual
ao último dígito verificador , se for igual CPF Válido.
soma := soma + somaDig + 2 * resto;
resto := 11 - soma mod 11;
if resto > 9 then resto := 0;
if resto <> ord(textoCPF[11]) - ordZero then
exit; { segundo DV errado }
Result := True; { tudo certo }

No formulário "formCadCliente" abra o editor de campos para tblCliente e clique no campo CPF
na lista. No Object Inspector, abra a página de eventos e crie um procedimento de evento para
OnValidate.
Neste procedimento de eventos, adicione o seguinte:
begin

if not ValidaCPF(tblClienteCpf.value) then


begin
ShowMessage('CPF Inválido!');
Abort;
end;

end;
A função ValidaCPF verifica se o CPF esta correto.Caso esteja errado, o procedimento mostra uma
mensagem, com ShowMessage e gera uma exceção com o procedimento Abort. Qualquer exceção
gerada faz com que o Delphi cancele a movimentação de campo ou de registro, mas é melhor usar Abort
porque ele gera uma exceção silenciosa, da classe EAbort, que não mostra nenhuma mensagem de erro
além das mensagens do seu programa.

Antes de executar o programa na seção Implementation da Unit CADCLIENTE acrescente uma


clausula uses para a unit independente:
uses GeralVenda;

Para adicionar a Unit ao projeto , no menu Project|Add to Project ( ), escolha a unit


"GERALVENDAS.PAS" em seguida clique no botão "Abrir".

Execute o programa e veja o que acontece. Altere um registro existente, digitando um valor para o
CPF, Inválido e tecle [Tab] para tentar mudar de campo. Você deve digitar um valor válido ou teclar
[Esc] para retornar ao valor anterior do campo.
É importante notar que a validação de campo só acontece quando o usuário edita o campo. Um registro
já existente, que tenha um valor inválido, não passará pela validação a não ser que o usuário altere o
valor do campo CPF.
Nota: Quando vários controles estão associados ao mesmo campo, as alterações de propriedade afetam
todos eles.

Validando Registros
Para fazer uma validação de registro, você pode utilizar o evento BeforePost do componente Table. Esse
evento é executado logo antes do registro ser gravado, seja um novo registro ou um registro já existente.

Essa gravação pode ocorrer quando o usuário clica no botão do DBNavigator ou quando ele muda
de registro, se o registro foi alterado.

Selecione o componente 'tblCliente', e na página de eventos, clique duas vezes no valor de


BeforePost. Neste evento vamos fazer o seguinte:
begin
if tblClienteNome.IsNull then
begin
ShowMessage('O nome é obrigatório');
tblClienteNome.FocusControl;
Abort;
end;
if tblClienteCidade.IsNull then
begin
ShowMessage('A cidade é obrigatório');
tblClienteCidade.FocusControl;
Abort;
end;
end;
A propriedade IsNull de um componente TField retorna True se ele tem um valor nulo, isto é, vazio, não
informado. Se o campo estiver vazio, o procedimento mostra uma mensagem e chama o método
FocusControl do componente. Esse método coloca o foco no controle de dados associado (com isso não
é preciso saber o nome do controle DBEdit ligado ao campo). Depois de fazer isso, o procedimento
chama Abort para cancelar a gravação do registro.

Com a validação de registro, o usuário só tem duas opções: ou digitar valores válidos, ou cancelar

a edição do campo, com o botão do DBNavigator. Execute o programa e note que agora essa
validação também é feita se você incluir um registro e tentar gravá-lo.

Salve e execute o programa. Para testar crie um novo cliente , não informe seu nome , mas coloque
o nome da cidade onde ele mora, ao salvar o cliente , o cliente não será salvo enquanto não informar o
seu nome.
Notas: A verificação se um campo é ou não obrigatório pode ser no banco de dados e no Delphi
podemos fazer a verificação através de erros.Veremos nos próximos capítulos.
Tratamento de Exceções
Desmarque a opção " Stop on Delphi Exceptions" no menu Tools | Debugger Options | Language Exceptions.
Mas tarde iremos entender o motivo.

Observe que ao digitar uma data inválida , ocorre a seguinte mensagem:

Esta mensagem que aparece é um erro de execução, ou seja, uma mensagem do Delphi avisando de uma
condição de erro.O termo usado no Delphi para essas situações é exceção.Uma exceção[exception] é uma
condição anormal que ocorre durante a execução do programa, que interrompe o seu funcionamento.
Quando ocorre uma exceção, o Delphi mostra uma mensagem padrão e termina a execução do
procedimento de evento atual, mas continua a responder a eventos e executar o programa.Para qualquer
exceção, você pode fazer seu próprio tratamento, interceptando o tratamento do Delphi.

Para tratar essa exceção você tem que utilizar um evento que ocorra antes da mensagem de erro.
Como o componente está associado a um objeto de campo , podemos fazer este tratamento nos eventos
do objeto de campo. Clique duas vezes no componente tblCliente ( ) para abrir o editor de campos.
Clique no campo "DataNasc" e crie um procedimento para o evento OnSetText . Esse evento é
executado todas as vezes que é alterado o conteúdo do objeto de campo. Neste procedimento o parâmetro
"Text" contém o valor da data informada. Coloque o código a abaixo:
var Data : TDateTime;

begin

try
data := strtodate(text);
tblClienteDataNasc.asDatetime := data;
except
on EConvertError do
begin
showmessage('Data Inválida!');
abort;
end;
end;
end;
O comando try (tentar) é um comando de tratamento de exceções. Os comandos entre o try e o except
são executados, mas se ocorre alguma exceção nesse bloco, o Delphi sai do bloco e entra na parte
except[exceto]. Então o Delphi procura uma cláusula on correspondente à classe da exceção gerada. Se
ele encontra um on correspondente, ele executa o comando de tratamento associado. Depois de executar
o tratador, o Delphi não termina a execução do procedimento de evento, mas continua a execução com o
próximo comando depois do end do try.
A função strtodate converte uma variável do tipo string para data, caso ocorra algum erro gera uma
exceção da classe 'EConvertError', se ocorrer a exceção mostramos a mensagem 'Data Inválida' , em
seguida o comando abort é executado ,es este comando cancela o procedimento, se ele não for colocado
a mensagem do Delphi irá continuar aparecendo. Quando colocamos algum procedimento para este
evento , ao sair do procedimento a data informada não será colocada no objeto de campo, por isso que
atribuimos o valor da data ao objeto 'tblClienteDataNasc'.

Visualizando Informações sobre Exceções


Se a opção " Stop on Delphi Exceptions" estiver marcada , o Delphi mostra uma mensagem mais detalhada
sobre cada exceção e mostra exatamente qual a linha que causou a exceção. Para verificar esta opção no
menu Tools | Debugger Options | Language Exceptions e marque a opção " Stop on Delphi Exceptions". Agora execute
o programa e provoque uma exceção, cadastrando um data inválida para a data de nascimento'. Nesse caso a mensagem
será:

ou seja , o projeto causou uma exceção da classe 'EConvertError'. O nome da classe de exceção é uma
informação importante quando você fizer o tratamento da exceção no programa.

Para continuar a execução, tecle[F9] ou clique no botão (Run). Depois irá aparecer a
mensagem que colocamos ( caso não tenha tratado a exceção aparece a mensagem do Delphi).

Classes de Exceções
Quando você faz o tratamento de exceções, pode tratá-las a nível específico de cada exceção ou você
pode tratar uma classe de exceção mais geral. As classes fazem parte de uma hierarquia de exceções,
como a seguinte:
Exception (qualquer exceção)
EIntError (erros de números inteiros)
EDivByZero (divisão por zero de inteiro)
EIntOverflow (transbordo inteiro)
ERangeError (valor ou ind. vetor fora de faixa)
EMathError (erros de matemática real)
EOverflow (transbordo de real)
EUnderflow (valor muito peq. de real)
EZeroDivide (divisão por zero de real)
Se você criar uma cláusula de tratamento para uma exceção mais geral, como 'EMathError', ela será
acionada para qualquer uma das classes abaixo na hierarquia, seja divisão por zero (EZeroDivide),
transbordo de valor (EOverflow) ou qualquer outra. A classe Exception é o nível mais geral de
tratamento e intercepta qualquer exceção.
Se num conjunto de tratadores, você colocar um tratador primeiro para uma exceção mais geral e depois
para uma exceção específica, o tratador mais específico nem vai chegar a ser executado.
Notas: O tratamento de exceção pode ser feito em qualquer procedimento que julgar necessário.
Pesquisas na Tabela
Uma característica fundamental de um programa é permitir que o usuário faça uma pesquisa em uma tabela,
fornecendo o valor a ser pesquisado.
Na tabela de clientes, vamos facilitar a pesquisa pelo nome do cliente.

Adicionando um DBGrid

Para isso vamos adicionar ao formulário um controle DBGrid, que vai mostrar toda a lista de clientes,
permitindo selecionar qualquer um deles. Abra um espaço adicional à direita do formulário e acrescente um
componente Label, com o texto "Procura:" e um componente Edit (não use um DBEdit), com o nome de

"editProcura". Abaixo deles acrescente um componente DBGrid (ícone da página Data Controls). O formulário
deve ficar como o seguinte:

Agora associe a grade (DBGrid) com a tabela, alterando sua propriedade DataSource para 'dsCliente'. Com
isso, se a tabela estiver ativa, ele vai automaticamente mostrar todos os campos e todos os registros da tabela. Não
vamos permitir que o usuário altere os dados usando o grid. Para isso basta mudar a sua propriedade ReadOnly para
True.

Vamos restringir os campos mostrados apenas ao campo "Nome". Para isso, clique duas vezes em 'tblCliente'
para abrir o editor de campos, selecione todos os campos menos Nome e altere a propriedade Visible para False. Se
preciso, reduza o DisplayWidth para que o valor do campo apareça todo no Grid.

Algumas características de visualização do DBGrid não são necessárias aqui, como as linhas divisórias de
registros e campos, ou os títulos da parte de cima. Vamos desativar essas características, alterando a propriedade
Options. Expanda essa propriedade no Object Inspector e altere as seguintes opções:
Opção ValorFinalidade
dgEditing False não permite incluir ou excluir
dgTitles False desativa os títulos
dgIndicator False desativa o indicador de registro
dgColLines False desativa as linhas verticais
dgRowLines False desativa as linhas horizontais
dgTabs False não usa [Tab] para mudar de campo
dgAlwaysShowSelectionTrue sempre mostra a linha selecionada
Se a opção 'dgAlwaysShowSelection' estiver desativada, o grid só mostra qual a linha selecionada (a do registro
atual) quando ele tem o foco de teclado. Nós devemos ativá-la para que o usuário saiba qual o nome selecionado na
lista a qualquer momento.

Execute o programa e verifique. Você pode clicar na grade a qualquer momento para selecionar um dos
registros, ou usar o DBNavigator para percorrer os registros.
Nota: existem duas opções parecidas: dgAlwaysShowEditor e dgAlwaysShowSelection. Para visualizar melhor as
opções, aumente a largura do Object Inspector.

Utilizando um Índice Secundário


Mas há um problema: a grade mostra os dados ordenados pelo índice principal da tabela, que no caso é o índice no
campo CodCliente. Para facilitar a pesquisa, eles deveriam estar ordenados por nome.
Para mudar a ordem dos registros, podemos usar um índice diferente. Para ordenar por nome, que é o necessário,
precisamos usar o índice criado para o campo Nome.
Para isso, basta alterar a propriedade IndexName do componente Table. Se ela estiver vazia, a ordem dos registros
será determinada pelo índice primário da tabela. Se você colocar o nome de um índice secundário, a ordem será de
acordo com esse índice. Essa propriedade afeta tanto a ordem virtual dos registros, quanto as pesquisas que você
fizer, como veremos.

Clique na propriedade IndexName e selecione o nome "IndNome" da lista. Esse é o nome do índice
secundário criado. Note como a ordem dos registros é alterada no controle DBGrid. Se você apagar o valor de
IndexName, a ordem volta a ser a do campo CodCliente.
Além da propriedade IndexName, existe outra chamada IndexFieldNames, que tem a mesma função. O valor desta
propriedade é a lista dos campos que compõem o índice. Por exemplo, você poderia colocar "Nome" em
IndexFieldNames e teria o mesmo efeito. Isto é útil se você não souber o nome do índice, mas se souber quais os
campos que fazem parte dele. As duas propriedades são mutuamente exclusivas: se você altera uma, a outra terá seu
valor eliminado.

Pesquisando pelo Nome


Os controles acima do DBGrid serão usados para permitir pesquisa na tabela pelo nome do cliente. A cada digitação
do usuário, vamos posicionar a tabela no nome mais próximo.
Para isso, vamos usar o método FindNearest do componente Table. Esse método procura um valor na tabela
utilizando o índice atual. Se o valor não for encontrado exatamente, ele posiciona no registro mais próximo.

Selecione o controle 'editProcura' e crie um procedimento para o evento OnChange. Esse evento é executado
toda vez que o usuário altera o valor do controle, digitando ou apagando caracteres. No procedimento basta colocar o
seguinte:
begin
tblCliente.FindNearest([editProcura.Text]);
end;
Execute e verifique o resultado. Quando você digita um nome em 'editProcura', o valor mais próximo é selecionado.
Isso é chamado de busca incremental.
Nota: se o índice tiver mais de um campo, coloque em IndexFieldNames os nomes de campos separados por
ponto-e-vírgula ";", sem espaços.
Nota: quando você clica num ícone da paleta de componentes e depois em cima de um componente, aquele que você
criou fica contido no primeiro, isto é, restrito à área do componente e os dois são movimentados em conjunto.

Pesquisando pelo Código


Agora vamos dar ao usuário a opção de procurar um registro pelo valor do campo código, que também tem um
índice definido (o índice primário da tabela).

Para isso, abra um espaço na parte inferior do formulário. Vamos colocar nesse espaço um componente

GroupBox, que é usado para agrupar visualmente outros componentes. Selecione o ícone do GroupBox na
página standard e coloque-o no formulário. Coloque na sua propriedade Caption o texto "Procura por código".

Dentro do componente GroupBox, crie um label, um quadro de edição e um botão, como na figura abaixo:

Mude o Caption do label para "Código:". Mude o Name do componente Edit para 'editProcCodigo' , a
propriedade Text deixe vazia e o nome do botão para 'btnProcCodigo' e altere a propriedade caption para
"Procurar".

Agora clique duas vezes no botão Procurar. Neste procedimento, vamos usar o valor digitado para pesquisar
na tabela, usando o método FindKey. Como vamos pesquisar por um campo diferente, devemos alterar a
propriedade IndexName do componente dinamicamente, e depois retorná-la ao original. Isso gera um pequeno
problema — ao alterar essa propriedade, os controles do formulário vão mudar para refletir o novo índice. Isso pode
ser evitado com o método DisableControls do componente Table. Quando ele é chamado, os controles de dados não
atualizam mais seus valores até que o método EnableControls seja chamado. Com isso, coloque o seguinte no
código do procedimento:
var codigo: integer;
begin
codigo := StrToInt(editProcCodigo.Text);
with tblCliente do
try
DisableControls;
IndexName := ''; {usando índice primário}
if not FindKey([codigo]) then
ShowMessage('Codigo não encontrado');
finally
IndexName := 'IndNome'; {usando índice por Nome}
EnableControls;
end;
end;
O método FindKey é uma função que retorna True se encontrou o registro. Caso ele encontre, ele posiciona no
registro, senão mantém posicionado no registro atual.
Blocos Protegidos
Os tratamentos de erros no Delphi podem ser feitos para uma exceção específica. As vezes , não é
necessário tratar um exceção específica , mas é preciso realizar uma ação de finalização,
O finally é utilizado quando uma determinada exceção ocorre , e necessitamos executar de qualquer
forma um determinado comando ou vários comandos antes de interromper o procedimento. Esses
comandos devem ser colocados no trecho finally..end. Em geral , blocos protegidos podem ser usados
em qualquer situação que envolva uma alocação e liberação de recursos(como um arquivo , por
exemplo).
No procedimento de evento do OnClick observe que a alteração de IndexName e a chamada de
EnableControls são feitas no bloco finally. Isso significa que mesmo que aconteça uma exceção, esses
comandos são executados. Isso é importante, pois se esse tipo de tratamento não for feito, os controles de
dados podem ficar num estado desabilitado, e o usuário não conseguiria mais utilizar o programa.

Execute e verifique o resultado. Se você digitar um código e clicar no botão Procurar, o programa
vai tentar procurar o registro.
Usando Outros Controles de Dados
Além dos controles DBEdit e DBGrid, que já utilizamos, a página Data Controls contém vários outros
que são úteis nos programas.

Usando o DBComboBox

Os controles DBListBox ( ) e DBComboBox ( ) são equivalentes, respectivamente, aos

controles padrão ListBox ( ) e ComboBox ( ), da página Standard. Ambos mostram uma lista de
nomes (definida pela propriedade Items) e permitem que o usuário escolha um dos itens da lista. Os
controles ComboBox e DBComboBox mostram a lista fechada, com um botão de seta que permite o
usuário selecionar um valor, enquanto que ListBox e DBListBox mostram a lista sempre aberta.
Os controles da página Data Controls, além da mesma função que seus equivalentes na página Standard,
ainda têm a capacidade de ler e gravar um valor no banco de dados.

O campo Estado da tabela pode utilizar uma lista de valores, com os nomes de estados possíveis.
Para isso, vamos utilizar um controle DBComboBox. Apague o controle DBEdit relacionado ao Estado e

substitua por um DBComboBox ( ), na mesma posição do anterior.

A propriedade Style de uma combo box determina se ela permite digitação de um valor que não
está na lista. O valor padrão é 'csDropDown', que permite digitar, além de escolher da lista. O valor
'csDropDownList' proíbe digitação — o usuário só pode escolher um valor da lista. Altere para
'csDropDownList.

Associe o controle com o campo da tabela: em DataSource, selecione 'dsCliente' e em DataField


selecione "Estado".

Agora vamos preencher a lista dos valores possíveis da combo box. Para isso, clique na
propriedade Items e abra o seu editor. Agora, em cada linha, digite uma sigla de estado (para simplificar,
digite apenas as que estiverem presentes nos seus dados de teste). Não é preciso se preocupar com a
ordem alfabética:
AM, PA, RR, RO, AC, GO, TO, MT, MS, SE, SP, RJ, MG, ES, BA, AL, MA, CE, PI, RS, SC,DF.

Para colocar todos os itens da lista em ordem alfabética, altere a propriedade Sorted para True.

Execute o programa e veja a diferença. Agora o usuário, ao invés de digitar a sigla do estado, pode
selecionar a partir da lista. Para usar a combo box pelo teclado, sem usar o mouse, use as setas [ ] e [ ]
para se movimentar pelos itens, ou digite [Alt+ ] ou [Alt+ ] para abrir ou fechar a lista. Quando a lista
está aberta, use as setas para escolher um item e tecle [Enter] para selecionar, ou [Esc] para fechar a lista.
Se você teclar uma letra, a combo box seleciona o primeiro item que começa com essa letra e, se você
continuar repetindo a tecla, seleciona os próximos.
Em alguns casos, a lista de valores não é fixa no programa, mas deve ser criada dinamicamente. Nesse
caso, preencha a propriedade Items em tempo de execução, usando o método Add:
cboEstado.Items.Add(sigla);
Nota: quando os dados vêm de outra tabela, existe uma forma mais fácil, que é utilizar o controle

DBLookupComboBox ( ), como veremos mais tarde.


Capítulo 15
Acesso s/ Controle de Dados

Estados de uma Tabela

Métodos para Percorrer Dados

Métodos para Modificar Dados

Eventos dos Componentes de Dados

Manipulação de Datas

Exemplo: Criando Formulário sem Utilizar o dbNavigator

Tratamento do Erro "Key Violation"

Usando um Controle DBLookupComboBox


Estados de uma Tabela
Dependendo da situação, um componente Table pode estar em vários estados diferentes. É preciso
entender estes diferentes estados para poder utilizá-lo. A propriedade State (acessível somente em tempo
de execução) tem valores diferentes dependendo do estado. Os principais são:
· Inativo (dsInactive): a tabela está fechada. A propriedade Active é True.
· Visualização (dsBrowse): a tabela está aberta, e o usuário está visualizando ou percorrendo registros.
· Inserindo (dsInsert): foi iniciada a inclusão de um novo registro, mas esse registro ainda não foi salvo

no banco de dados. O componente entra nesse estado quando o usuário clica no botão do
DBNavigator.
· Editando (dsEdit): foi iniciada a edição de um registro existente, mas o registro ainda não foi salvo. O
componente entra nesse estado quando o usuário começa a alterar um campo. Quando a propriedade
AutoEdit do componente DataSource é False (o padrão é True), o usuário deve iniciar a edição

explicitamente, clicando no botão do DBNavigator.


Quando a tabela está no estado inativo, você não pode acessar os dados. Quando ela está no estado de
visualização (dsBrowse), você pode percorrer os registros da tabela e ler os valores de campos, utilizando
os componentes TField, mas você não pode alterar os valores de campos.
Para alterar os valores dos campos de um registro, você deve colocar a tabela em estado de edição
(dsEdit) e depois alterar o valor dos componentes TField. Depois você deve salvar os dados, retornando
ao estado de visualização.
Quando você inicia a inclusão de um registro, todos os componentes TField ficam com o valor em
branco (nulo), e você pode atribuir valores para eles. Depois que você salva o registro, a tabela retorna ao
estado de visualização.
Métodos para Percorrer Dados
O componente Table tem vários métodos que permitem você acessar a tabela utilizada, independemente
dos controles de dados. Se o componente Table estiver ligado a um componente DataSource, que utiliza
controles de dados, o resultado dos métodos vai aparecer na tela.

Métodos de Abertura e Fechamento


Para abrir a tabela e poder acessar os seus dados, primeiro defina as propriedades DatabaseName e
TableName do componente Table, seja em tempo de projeto ou no código do programa. Depois utilize o
método Open, por exemplo:
tblCliente.Open;
Após terminar de utilizar a tabela, você pode usar o método Close para liberar a tabela e o componente
Table para outros usos.
tblCliente.Close;
O método Open equivale a alterar a propriedade Active para True. O método Close equivale a alterar a
propriedade Active para False.

Métodos para Percorrer Registros


Os seguintes métodos podem ser usados para percorrer registros:
BotãoMétodo Descrição
First Posiciona no primeiro registro.
Prior Volta para o registro anterior.
Next Avança para o próximo registro.
Last Posiciona no último registro.
Avança 'n' registros.
N/A MoveBy ( n )Se 'n' for positivo, move para frente.
Se 'n' for negativo, move para trás.

Repare que cada um deles, exceto MoveBy, é equivalente a um botão correspondente do controle
DBNavigator. Na verdade, o DBNavigator apenas chama os métodos correspondentes do componente
Table, em resposta ao clique do usuário.
Lembre-se que a ordem em que os registros aparecem quando você percorre seqüencialmente é a ordem
determinada pelo índice que estiver sendo utilizado no momento. Quando a tabela é aberta, o índice
utilizado é o índice primário, mas isso pode ser alterado.
Para ler os campos do registro atual, existem duas formas. Se você usou o editor de campos para criar
uma lista de componentes TField, cada um deles tem um nome, que é formado pelo nome do
componente Table, mais o nome do campo. Com isso, basta acessar a propriedade Value do componente
TField:
preco := tblItemPrecoVenda.Value;
quant := tblItemQuantidade.Value;
Mas se você não criou uma lista de campos, os componentes TField são criados internamente e não têm
nome, mas ainda podem ser acessados com o método FieldByName:
preco := tblItem.FieldByName('PrecoVenda').AsFloat;
quant := tblItem.FieldByName('Quantidade').AsInteger;
As propriedades AsFloat e AsInteger são necessárias nesse caso, para especificar qual o tipo de dados
que você está acessando. No caso de um componente criado explicitamente, o Delphi já sabe o tipo de
campo pela declaração, então você pode usar Value, que tem o mesmo tipo de dados do campo. Da
mesma forma, existem outras propriedades "As*", como AsString, AsDate, que retornam o valor do
campo convertido para um determinado tipo de dados.
Você não pode atribuir valores para a propriedade Value ou As* do componente se você estiver em
modo de percorrer registros.
Quando você está percorrendo a tabela, existem duas propriedades úteis, ambas do tipo boolean. A
propriedade EOF é verdadeira quando você estiver posicionado no fim da tabela, depois do último
registro. Já BOF é verdadeira quando você estiver posicionado no início da tabela. Por exemplo, para
percorrer a tabela do início ao fim.
with tblItem do
begin
First; { vai para o primeiro registro }
while not EOF do
begin
preco := tblItemPrecoVenda.Value;
quant := tblItemQuantidade.Value;
...
Next; { vai para o próximo registro }
end;
end;
Se ambas forem verdadeiras, a tabela está vazia, não tem nenhum registro:
if tblItem.BOF and tblItem.EOF then
ShowMessage('A tabela está vazia');

Exemplo: Percorrendo a Tabela de Itens

Crie um novo projeto no Delphi, no qual vamos percorrer a tabela de itens (ITEM.DB) através de
comandos de programa, usando os métodos do componente Table. Esse programa vai percorrer toda a
tabela e totalizar a quantidade de itens vendiddos. Altere a propriedade Caption do formulário para
'Totalizar Itens'.

Coloque no formulário um componente Table ( ) e defina as seguintes propriedades: Name


será 'tblItem', DatabaseName será 'CursoDelphi', o alias que diz onde estão os dados e TableName será
'ITEM.DB'. Crie controles no formulário como na figura:

Coloque um label, com o texto "Total:" e um controle de edição, com o nome 'editTotal', que irá
mostrar o resultado final. Finalmente coloque um botão, com o texto 'Totalizar' e nome 'btnTotalizar'.

Clique duas vezes no 'tblItem' para abrir o editor de campos. Acrescente todos os campos à lista.
Repare que se você clicar em um nome de campo, por exemplo 'Quantidade', você verá que a
propriedade Name do objeto de campo é tblItemQuantidade. Esse é o nome que você deve utilizar no
programa para acessar o valor do campo.

Clique duas vezes no botão Totalizar e digite o seguinte:


var total: double;
begin
total := 0;
with tblItem do begin
Open;
First;
while not EOF do begin
total := total + tblItemQuantidade.Value ;
Next;
end;
Close;
end;
editTotal.Text := FloatToStr(total);
end;
Dentro do with, a primeira linha chama o método Open para abrir a tabela. Isso não seria necessário se a
propriedade Active já tivesse o valor True no início da execução. Depois First posiciona no primeiro
registro. Essa chamada não é realmente necessária, porque logo depois de abrir a tabela, o registro atual
já é o primeiro.
O laço while verifica o valor da propriedade EOF. Enquanto ela for falsa (not EOF), o laço percorre os
registros. Para cada registro, o campo Quantidade é somado com à variável 'total'. Note a forma de
acesso aos campos: com os nomes dos objetos TField, usando sua propriedade Value. Depois de somar o
valor, o método Next muda para o próximo registro.
Depois do laço, o método Close fecha a tabela (faz Active = False), e finalmente o resultado final,
acumulado em 'total', é mostrado no controle 'editTotal'.

Execute o programa e clique no botão Totalizar para ver o resultado. Salve o projeto como
TOTALITEM.PAS E TOTALITEMP.DPR.
Métodos para Modificar Dados
Editando Registros
Para alterar os campos do registro atual, você deve colocar a tabela em modo de edição. Para isso você
deve chamar o método Edit do componente Table. Após chamar o método Edit, altere os valores dos
campos, usando os objetos TField. As alterações não serão gravadas até que você chame o método Post.
Por exemplo:
tblItem.Edit;
tblItem.FieldByName('Quantidade').AsString := quantidade;
... {outras alterações}
tblItem.Post;
Ou, de forma mais abreviada, usando o with:
with tblItem do
begin
Edit;
FieldByName('Quantidade').AsString := quantidade;
... {outras alterações}
Post;
end;
Você pode usar também os componentes TField, se você tiver usado o editor de campos para criá-los
explicitamente. Nesse caso, é melhor atribuir um valor para a propriedade Value, que já tem o tipo de
dados correto.
Quando múltiplos usuários acessam a mesma tabela, Edit tem uma função mais importante: ele trava
[lock] o registro, de forma que nenhum usuário consegue editá-lo (mais ainda pode fazer a leitura).
Quando você tentar travar o registro, e outro usuário já está editando, seja no mesmo computador ou em
outro computador na rede, Edit vai gerar uma exceção EDBEngineError. Essa exceção pode ser tratada
usando um comando try..except, coma já vimos. Depois que você chama o método Post, o registro é
gravado e liberado.
Se depois de chamar Edit, você resolver cancelar as alterações no registro (devido a uma exceção, por
exemplo), você pode chamar o método Cancel em vez de Post. Esse método libera o registro e cancela
todas as alterações feitas nos componentes TField.
Se você mudar de registro após o Edit, usando o método Next por exemplo, o componente vai verificar
se você alterou algum campo. Se você tiver alterado, ele automaticamente salva o registro antes da
movimentação. Se não foi feita alteração, ele cancela a edição para não gravar um registro
desnecessariamente.
Quando você usa o controle DBNavigator, alguns botões dele chamam automaticamente esses métodos:

=Edit, = Post, = Cancel.


O botão Edit geralmente não é necessário, porque geralmente o usuário pode iniciar a edição
simplesmente alterando um controle de dados, o que automaticamente chama o método Edit. Mas isso
depende da propriedade AutoEdit do componente DataSource, que normalmente é True. Quando
AutoEdit é False, o usuário não pode editar os controles de dados e para poder modificar o registro, ele

deve primeiro clicar no botão do DBNavigator.

Acrescentando Registros
Para incluir um novo registro na tabela, você deve chamar o método Insert ou Append. Se a tabela não
tem índice primário, Insert insere um novo registro no meio da tabela e Append acrescenta o novo
registro ao final. Para uma tabela que tem chave primária, os dois métodos fazem exatamente a mesma
coisa: o registro será inserido na posição determinada pela chave. Assim, o que discutiremos sobre Insert
se aplica igualmente a Append.
Quando você chama Insert, ele não grava dados na tabela. Ele apenas cria um novo registro vazio em
memória. Depois você deve preencher os campos desse registro com os seus valores, usando os objetos
TField. Para gravar os dados, chame o método Post. O novo registro será o registro atual da tabela, e vai
se tornar o registro atual. A posição do registro na tabela será determinada pelo valor da sua chave
primária. Por exemplo:
codigo := 20;
with tblItem do
begin
Insert;
tblItemCodItem.Value := codigo;
tblItemQuantidade.Value := quant;
tblItemPrecoVenda.Value := preco;
Post;
end;
Se você quiser cancelar a inserção do novo registro, use o método Cancel. Quando você cancela, a
posição de registro volta ao registro onde você estava posicionado antes de chamar Insert.
Se você mudar de registro após chamar Insert, o componente vai verificar se você alterou algum campo,
e vai gravar o novo registro, caso tenha havido alteração, ou cancelar a inclusão, caso você tenha deixado
o registro inteiro em branco.

Quando você usa o controle DBNavigator, o usuário pode clicar no botão , que chama

automaticamente o método Insert da tabela. Depois ele preenche os campos e clica no botão , que

chama o método Post, ou no botão , que chama o método Cancel. Quando você usa o controle
DBGrid, o usuário pode pressionar a tecla [Insert] para chamar o método Insert, preencher os campos, e
mudar de registro.
Excluindo Registros
Para excluir o registro atual, use o método Delete:
tblItem.Delete;
A operação de exclusão é executada imediatamente e não pode ser desfeita. Registros excluídos não
podem ser recuperados.

Usando o controle DBNavigator, o usuário pode clicar no botão , que chama esse método
automaticamente. Usando o controle DBGrid, o usuário pode pressionar [Ctrl+Delete] para excluir o
registro atual. Esses dois controles têm uma propriedade ConfirmDelete. Se ela estiver verdadeira (valor
True), o controle mostra uma mensagem de confirmação predefinida, senão exclui o registro
imediatamente.

Outros Métodos
Quando você trabalha com os métodos do componente Table, o efeito dos métodos (edição, inclusão, ou
movimentação) aparece imediatamente nos controles de dados (se houver). Essa atualização dos
controles geralmente é desnecessário, e toma um certo tempo, tornando muito demorada uma operação
com vários registros, por exemplo.
Você pode desabilitar temporariamente a atualização dos controles, com o método DisableControls e
depois habilitar novamente a atualização com o método EnableControls. Quando você chama
EnableControls, todos os controles de dados ficam "congelados" com seus valores atuais. Por exemplo:
with tblItem do begin
DisableControls;
while not EOF do ...
EnableControls;
end;
Se você não lembrar de habilitar os controles de dados, eles ficam inutilizáveis pelo usuário. Note
também que se acontecer uma exceção depois de desabilitar os controles, o Delphi vai sair do
procedimento e eles não serão habilitados novamente. Para evitar essa última situação, use um comando
try..finally:
try
DisableControls;
while not EOF do ...
finally
EnableControls;
end;
Um comando try..finally garante que a parte do finally é sempre executada, seja numa execução normal,
ou quando ocorre uma exceção no bloco do try. Nesse caso, isso garante que os controles de dados são
habilitados novamente.
Numa rede, os registros que estão sendo visualizados podem ficar desatualizados à medida que outros
usuários adicionam novos registros. Para obter os registros mais atualizados da tabela, você pode usar o
método Refresh:
tblCliente.Refresh;

Esse método é chamado automaticamente pelo botão do DBNavigator.


Eventos dos Componentes de Dados
Os componentes de dados têm vários eventos que são acionados em momentos específicos.

Eventos do Componente Table


Para cada método do componente Table (Edit, Insert, Post, etc.), existem eventos "Before" e "After",
que acontecem respectivamente antes e depois da execução do método. No evento "Before", você pode
cancelar a execução do método atual, gerando uma exceção. Esses eventos são acionados nas seguintes
situações:
· BeforeScroll, AfterScroll: antes e depois, respectivamente, de qualquer movimentação de registro na
tabela, provocada por um dos métodos de movimentação First, Next, Prior, Last, MoveBy, FindKey,
FindNearest, etc.
· BeforeCancel, AfterCancel: antes e depois do cancelamento de uma edição ou inclusão (método
Cancel).
· BeforeClose, AfterClose: antes e depois da tabela ser fechada (método Close).
· BeforeDelete, AfterDelete: antes e depois de um registro ser excluído. No evento BeforeDelete, você
pode mostrar sua própria mensagem de confirmação de exclusão, evitando uma mensagem do Delphi.
· BeforeEdit, AfterEdit: antes e depois do início da edição de um registro.
· BeforeInsert, AfterInsert: antes e depois do início da inclusão de um registro.
· BeforeOpen, AfterOpen : antes e depois da tabela ser aberta (ativada).
· BeforePost, AfterPost: antes e depois do registro ser gravado. O evento BeforePost, como já vimos, é
o melhor lugar para se fazer uma validação de registro, evitando a gravação se necessário.
· OnNewRecord: acontece depois da inclusão de um novo registro, antes do evento AfterInsert. Nesse
evento você pode, por exemplo, preencher o campo chave da tabela com um valor gerado pelo programa.
Se você fizer alguma alteração de campos em AfterInsert, ou qualquer outro evento, o registro será
considerado modificado, por isso é melhor usar OnNewRecord.
Cada um desses eventos tem um cabeçalho como o seguinte:
procedure TForm1.Table1BeforePost(DataSet: TDataset);
Ou seja, um parâmetro do tipo 'TDataset'. TDataset é uma classe que é ancestral da classe TTable e de
outros objetos de acesso a dados. Usando o parâmetro 'DataSet', você pode manipular a tabela de forma
genérica, sem especificar o nome exato do componente.
Além desses, o evento OnCalcFields, como já vimos, acontece quando você tem campos calculados, no
momento em que é necessário obter o valor desses campos.
Manipulação de Datas
O Delphi permite armazenar datas e horas no banco de dados, em campos do tipo Date (apenas datas), ou
do tipo Time (apenas horas), ou campos Date/Time ou Timestamp (guarda data e hora), em alguns
bancos de dados. É importante também saber nesse caso as funções disponíveis para manipulação de
datas.
O Delphi codifica datas e horas internamente como números, que representam uma contagem de dias
desde 1/1/0001. Ele usa o tipo de dados TDateTime para esse formato codificado. Você pode usar a
função EncodeDate para gerar esse valor codificado, e o procedimento DecodeDate para obter os
componentes da data separados (dia, mês e ano). Por exemplo:
var
data: TDateTime;
...
begin
...
data := EncodeDate( ano, mes, dia );
....
DecodeDate( data, ano, mes, dia );
...
end;
Para tratamento de horas, você tem semelhantemente as rotinas EncodeTime e DecodeTime, que de
forma genérica, são usadas assim:
varHora := EncodeTime( horas, minutos, segundos, milisegs );

DecodeTime( varHora, horas, minutos, segundos, milisegs );


Você pode fazer operações com as datas codificadas, como por exemplo, somar um número a uma data,
ou subtrair duas datas para saber a quantidade de dias entre elas.
Você também pode converter uma data/hora para sua representação textual, para tratá-la como uma
string, usando DateToStr, TimeToStr, ou DateTimeToStr:
editData.Text := DateToStr(tblClienteDataNasc.Value);
labelRelogio.Caption := TimeToStr(Now);
Essas funções convertem a data usando o formato padrão, que depende da configuração do Windows.
Mas você pode também usar outros formatos, usando FormatDateTime, por exemplo:
texto := FormatDateTime(data, 'dd/mm/yyyy');
No segundo argumento de FormatDateTime, você pode usar os marcadores de posição (entre outros):
MarcadorInsere
d, dd O dia do mês. Se o dia é de 1 a 9, "dd" acrescenta um zero à esquerda.
ddd O dia da semana abreviado.
dddd O nome do dia da semana, completo.
m, mm O mês como um número. Se o mês é de 1 a 9, "mm" acrescenta um zero à esquerda.
mmm O nome do mês abreviado.
mmmm O nome do mês completo.
yy O ano com dois dígitos.
yyyy O ano com quatro dígitos.
h, hh As horas. Se é de 0 a 9, "hh" acrescenta um zero à esquerda.
m, mm Os minutos. Se é de 0 a 9, "mm" acrescenta um zero à esquerda.
s, ss Os segundos. Se é de 0 a 9, "mm" acrescenta um zero à esquerda.
/ O separador de datas (pode não ser uma barra).
: O separador de horas (pode não ser o dois-pontos).

Esses mesmos marcadores podem ser usados com a propriedade DisplayFormat de um campo do tipo
data ou hora.
Você pode converter uma string em data/hora, usando as funções de conversão StrToDate, StrToTime e
StrToDateTime.
Para saber o dia da semana, você pode usar a função DayOfWeek:
diaSemana := DayOfWeek(data); {1=domingo,...7=sábado}
Finalmente, você tem funções para consultar a data/hora atuais do computador: Now retorna a data e
hora atuais codificadas, Date retorna apenas a data e Time retorna apenas a hora. Por exemplo, para
mostrar a data de hoje em um controle de edição, faça:
editData.Text := DateToStr(Date);
Exemplo: Criando formulário sem utilizar o
dbNavigator
Neste exemplo iremos criar o cadastro de fornecedores, sem utilizar o componente dbNavigator , iremos utilizar
Botões e tratar o evento de cada um , fazendo uma simulação do dbNavigator.

Crie um novo formulário, altere seu nome para 'formCadFornecedor', na propriedade Caption coloque
'Cadastro de Fornecedores'. Acrescente 9 componentes SpeedButton ( ), e altere as propriedades a seguir, as
figuras são encontradas no direito do Delphi 'BORLAND SHARED\IMAGES\BUTTONS' :
SpeedButton1
Name spbIncluir
Glyph FILENEW.BMP
Hint Inclui
SpeedButton2
Name spbAlterar
Glyph FILEOPEN.BMP
Hint Alterar
SpeedButton3
Name spbSalvar
Glyph FLOPPY.BMP
Hint Salvar
SpeedButton4
Name spbCancelar
Glyph IGNORE.BMP
Hint Cancelar
SpeedButton5
Name spbPrimeiro
Glyph VCRREWND.BMP
Hint Primeiro
SpeedButton6
Name spbAnterior
Glyph ARROW3L.BMP
Hint Anterior
SpeedButton7
Name spbProximo
Glyph ARROW3R.BMP
Hint Próximo
SpeedButton8
Name spbUltimo
Glyph VCRFSFOR.BMP
Hint Último
SpeedButton9
Name spbExcluir
Glyph TRASH.BMP
Hint Excluir
Coloque seu formulário como a figura abaixo:

Coloque um componente Table e um DataSource no formulário, defina suas propriedades como:

Table
Name: tblFornecedor
DatabaseName: CursoDelphi
TableName: Fornecedor

DataSource
Name: dsFornecedor
DataSet: tblFornecedor

Clique duas vezes no componente tblFornecedor, para abrir o editor de campos, em seguida adicione os
objetos de campos e arraste-os para o formulário. Organize seu formulário de acordo com a figura abaixo:

Salve a unit como 'CADFORNCEDOR.PAS'. No evento OnShow do formulario, vamos abrir o


componente tblFornecedor , crie um procedimento para este evento e coloque:
tblFornecedor.Open
Para que o formulário funcione é necessário acrescentar em cada Botão , um procedimento para o evento
OnClick.

No procedimento do evento OnClick do botão spbIncluir colocar:


procedure TformCadFornecedor.spbIncluirClick(Sender: TObject);
begin
tblFornecedor.insert
end;
O método Insert do componente Table, inclui um registro em branco.

No procedimento do evento OnClick do botão spbAlterar colocar:


procedure TformCadFornecedor.spbAlterarClick(Sender: TObject);
begin
tblFornecedor.edit
end;
O método Edit que permite fazer alterações, mas essas alterações podem ser confirmadas ou não.

No procedimento do evento OnClick do botão spbSalvar colocar:


procedure TformCadFornecedor.spbSalvarClick(Sender: TObject);
begin
tblFornecedor.post
end;
O método Post, confirma as alterações feitas nos campos da tabela.

No procedimento do evento OnClick do botão spbCancelar colocar:


procedure TformCadFornecedor.spbCancelarClick(Sender: TObject);
begin
tblFornecedor.cancel
end;
O método Cancel, cancela as alterações feitas nos campos da tabela.

No procedimento do evento OnClick do botão spbPrimeiro colocar:


procedure TformCadFornecedor.spbprimeiroClick(Sender: TObject);
begin
tblFornecedor.first
end;
O método First movimenta para o primeiro registro.

No procedimento do evento OnClick do botão spbAnterior colocar:


procedure TformCadFornecedor.spbAnteriorClick(Sender: TObject);
begin
tblFornecedor.prior
end;
O método Previous movimenta para o registro anterior.

No procedimento do evento OnClick do botão spbIncluir colocar:


procedure TformCadFornecedor.spbProximoClick(Sender: TObject);
begin
tblFornecedor.next
end;
O método Next movimenta para o próximo registro.

No procedimento do evento OnClick do botão spbUltimo colocar:


procedure TformCadFornecedor.spbUltimoClick(Sender: TObject);
begin
tblFornecedor.last
end;
O método Last movimenta para o último registro.

No procedimento do evento OnClick do botão spbExcluir colocar:


procedure TformCadFornecedor.spbExcluirClick(Sender: TObject);
begin
if application.messagebox('Deseja excluir o fornecedor?', 'Confirmação',
MB_ICONQUESTION + MB_YESNO) = idyes then
tblFornecedor.delete
end;
O método Delete exclui o registro atual. Antes de excluir o registro será mostrado uma mensagem perguntando
se deseja excluir o fornecedor.
Notas: Se foi feita alguma alteração no registro atual , em seguida foi aplicado algum méodo de movimentação
na tabela ,as alterações serão confirmadas, antes da movimentação.
Neste exemplo mostramos somente o que cada método faz, não foi realizado nenhum controle de desativar o
botão Salvar , se o componente Table não estiver no modo de Alteração ou Inclusão , esses controles podem ser
feitos com a propriedade Enabled de cada componente SpeedButton.
Tratamento do Erro 'Key violation.'
Este erro ocorre quando estamos incluíndo um registro , e o valor informado para a chave primária
já existe. Para mostrar uma mensagem de erro mais clara para o usuário , podemos mostrar a nossa
própria mensagem de erro. Este erro ocorre quando o usuário confirma as informações que foram
colocadas, ou seja, quando o nosso programa executa o método Post. Quando ocorre algum erro durante
a execução desse método, gera o evento OnPostError, então podemos criar um procedimento para este
evento que irá mostrar a mensagem que desejamos.Neste procedimento coloque os comandos abaixo:
if e.message = 'Key violation.' then
begin
showmessage('Fornecedor Cadastrado!');
abort;
end;
O parâmetro 'E' contém o erro que ocorreu durante a gravação dos dados. Este parâmetro é da classe
EDataBaseError,a propriedade message desta classe , contém a mensagem do erro. É necessário
abortar o procedimento, para que a mensagem em inglês não aparece, por isso utilizamos o método
Abort;.
Notas: Ao verificar uma mensagem com a propriedade message, para que a condição utilizada seja
verdadeira, a mensagem que esta verificando é case sensitive, portanto, tem que ser escrita idêntica ao
conteúdo da propriedade

Vamos associar este formulário ao formulário principal, na Unit 'VENDPRIN.PAS', logo após a
seção implementation acrescente:
Uses CadCliente, ....., CadFornecedor;

Agora tecle [F12] para voltar ao formulário. Clique no item de menu Cadastro|Fornecedor e, no
procedimento de evento, coloque o seguinte comando:
formCadFornecedor.show

Salve o projeto e execute . Para testar cadastre alguns fornecedores , faça alterações, tente
cadastrar fornecedores que tenham o mesmo código.
Usando um Controle DBLookupComboBox
Quando o usuário cadastra um produto, ele precisa digitar o código do fornecedor, que deve corresponder
a um valor válido na outra tabela. Esse é um processo sujeito a erros e torna difícil validar o valor que o
usuário digita (para validar, seria necessário pesquisar na tabela de fornecedores). Você pode facilitar a
entrada desse campo para o usuário permitindo a ele selecionar o fornecedor a partir de um lista de
nomes.

Para isso, no formulário de produtos, apague o controle DBEdit que está sendo usado para o

campo "CodFarnecedor". Vamos utilizar outro controle que é o DBLookupComboBox ( ) da página


Data Controls. Esse controle tem a capacidade de gravar valores em uma tabela e mostrar uma lista de
valores de outra.

Coloque um controle DBLookupComboBox no formulário ao lado do rótulo "CodFornecedor".


Altere o Caption do rótulo para "Fornecedor".

No controle DBLookupComboBox, mude o nome para 'cboFornecedor'. Vamos alterar também as


seguintes propriedades: DataSource para 'dsProduto', e DataField para 'CodFornecedor', ou seja, os
mesmos valores do DBEdit anterior. Essas propriedades, como para qualquer controle de dados, dizem
quais os dados que o controle vai acessar.
Além dessas propriedades, outras propriedades determinam de onde o controle busca a lista de valores a
ser mostrada. Mas antes de alterar essas propriedades, precisamos criar novos componentes de dados no
formulário.

No formulário de fabricantes (FormCadFornecedor), selecione o componente 'tblFornecedor' e o


componente 'dsFornecedor' e tecle [Ctrl+C] para copiá-los. Agora volte para o formulário de produtos
(FormCadProduto) e tecle [Ctrl+V]. Isso vai colocar no formulário duas cópias dos componentes, que
acessam a tabela FORNECEDOR. Isso é necessário para fornecer os valores mostrados por
'cboFornecedor'.

Retornando ao componente 'cboFornecedor', altere as seguintes propriedades:


PropriedadeValor
ListSource dsFornecedor
KeyField CodFornecedor
ListField Nome

A propriedade ListSource determina qual a tabela que será usada para buscar os valores a serem
mostrados. Como 'dsFornecedor' está ligado à tabela FORNECEDOR.DB, os dados serão lidos desta
tabela. O valor de KeyField (CodFornecedor, nesse caso) é o nome do campo, na tabela
FORNECEDOR, que corresponde ao campo DataField na tabela PRODUTO. Finalmente ListField
determina qual o campo que será mostrado na lista, nesse caso, Nome.

Falta um detalhe: você deve ativar a tabela de fornecedores, 'tblFornecedor' nesse formulário.
Acrescente o seguinte no procedimento do evento OnCreate do formulário:
begin
tblProduto.Open;
tblFornecedor.Open;
end;

Agora execute o programa. Repare que você pode selecionar o fornecedor pelo nome agora.

Se você quiser mostrar o código também, além do nome, você pode alterar a propriedade ListField
para incluir mais de um campo. Para isso, coloque os nomes dos campos, separados por ";" (sem espaço
entre os nomes e o ponto-e-vírgula). No nosso caso, coloque "Nome;CodFornecedor" em ListField e
execute o programa. Quando a lista está fechada, aparece apenas o nome do fornecedor, mas quando
você abre a lista, vão aparecer os dois campos: nome e código.
Capítulo 16
Utilizando Mestre/Detalhe

Criando um Formulário Mestre/Detalhe

Usando Três Tabelas no Formulário


Criando um Formulário Mestre/Detalhe
Uma situação muito comum na utilização de bancos de dados é o relacionamento entre duas tabelas,
onde uma tabela tem um registro que se relaciona com vários registros de outra. No Delphi, isto é
chamado de relacionamento mestre/detalhe [master/detail]. A tabela mestre é aquela que tem um único
registro fazendo parte do relacionamento. A tabela detalhe é aquela que tem vários registros
relacionados.
No nosso caso, um cliente possui vários registros de venda relacionados (1-N). A tabela Cliente será a
tabela mestre e a tabela Venda será a tabela detalhe.

Usando o DFW para criar o formulário


O Database Form Wizard pode criar automaticamente um formulário mestre/detalhe, basta você informar
as tabelas e quais os campos relacionados. No nosso caso, usaremos a tabela Cliente como mestre e a
tabela Venda, criada anteriormente, como detalhe.

Esse formulário será criado no projeto VENDA.DPR , portanto ele tem que estar aberto. No menu
Database|Form Wizard.... Na primeira tela, no primeiro grupo de opções, selecione a segunda opção,
"Create a master/detail form" e clique em Next.

Na próxima tela, você deve escolher a tabela mestre. Primeiro clique na lista "Drive or Alias
name" e selecione "CursoDelphi", o nome de alias que for criado anteriormente. Agora, na lista de
tabelas, escolha CLIENTE.DB e clique em Next. Depois selecione apenas os campos CodCliente e
Nome (clique no campo e no botão ">"). Na próxima tela, escolha um layout horizontal para o
formulário.

Agora você deve selecionar a tabela detalhe. Selecione a tabela VENDA.DB, e na outra tela
adicione todos os campos (clique no campo e no botão ">>"). Na próxima tela, escolha o layout "Grid".
Isso vai colocar um controle DBGrid na parte "detalhe" do formulário, em vez de controles de dados
separados, este componente permite visualizar vários registros ao mesmo tempo..

Por último, o Database Form Wizard vai perguntar quais os campos que compõem o
relacionamento entre as tabelas. A tabela detalhe deve ter um índice definido para o campo que faz o
relacionamento. A lista dos índices disponíveis aparece abaixo de "Index Name". Selecione o índice
"CodCliente", que foi criado quando colocamos a restrição de integridade . Agora, na lista da esquerda,
aparece o campo "CodCliente" (se o índice tivesse mais de um campo, todos estariam na lista). Selecione
o campo. Na lista da direita, aparecem os campos da tabela mestre. Selecione o campo que tem o mesmo
nome, "CodCliente". A tela do DFW ficará como a seguinte:
Isso define o seguinte relacionamento entre as tabelas:

Agora clique em Next e depois no botão "Finish". O formulário será adicionado com as opções
definidas.

Testando o Formulário Gerado

Execute o programa, e veja como funciona o relacionamento mestre/detalhe. Na parte "detalhe" do


formulário (o controle DBGrid da parte inferior), você pode adicionar registros que estarão
automaticamente relacionados com o registro de cliente que aparece na parte superior.
Se você usar o DBNavigator do topo do formulário para mudar de registro na tabela mestre, os registros
de detalhe que aparecem embaixo serão diferentes. Como a tabela Venda não continha dados,
inicialmente nenhum cliente tem registros associados.
Note que o campo "CodCliente", que define o relacionamento com o cliente, aparece no Grid. Quando
você começa a incluir um registro, ele é preenchido automaticamente de acordo com o valor do código
do cliente atual. Se você alterar esse campo manualmente, o registro vai desaparecer da lista, pois vai
estar relacionado com outro cliente.
Após entrar com alguns dados, finalize a execução.

Analisando o Formulário
Vejamos exatamente como é definido o relacionamento mestre/detalhe pelo DFW. Como antes, são
utilizados os componentes Table e DataSource, só que desta vez existem dois componentes Table e dois
DataSource, todos no topo do formulário, ao lado do DBNavigator, como na figura:

O primeiro 'Table1', está associado com a tabela CLIENTE.DB, através da propriedade TableName. O
'DataSource1' está ligado com 'Table1', através da propriedade DataSet. De forma análoga, os outros
dois acessam a tabela de vendas: Table2.TableName tem o valor "VENDA.DB" e DataSource2.DataSet é
Table2.
Os dois controles DBEdit da parte superior do formulário estão ligados ao DataSource1 e o controle
DBGrid da parte inferior está ligado ao DataSource2.

Vamos alterar os nomes desses componentes de dados, para facilitar o entendimento. Mude os
nomes para os seguintes:

Além das ligações que já vimos, num formulário mestre/detalhe, é preciso relacionar as duas tabelas. Isso
é feito com propriedades que ligam 'tblVenda' (antes Table2) a 'dsCliente' (antes DataSource1). Selecione
o componente 'tblVenda' e veja que a propriedade MasterSource está ligada com 'dsCliente'. Isso define
qual tabela será a mestre para essa tabela.
A propriedade MasterFields diz qual o campo (ou campos, se mais de um) na tabela mestre, que faz o
relacionamento, e nesse caso é "CodCliente". A propriedade IndexFieldNames, nesse caso, define qual o
campo (ou campos) na tabela detalhe, que faz o relacionamento com o outro, e nesse caso tem o mesmo
nome, "CodCliente" (mas não é obrigatório que tenha o mesmo nome).

Nota: O valor que aparece na propriedade DataSet é o nome do outro componente, mas para
propriedades de ligação, o Delphi guarda internamente um ponteiro de objeto, independente do nome.

Tente executar o programa — o Delphi vai mostrar um erro de compilação dentro do procedimento
de evento 'FormCreate' , que foi criado pelo Database Form Wizard. Os dois comandos, "Table1.Open" e
"Table2.Open", são para abrir as duas tabelas em tempo de execução. Eles não funcionarão mais, porque
você alterou os nomes dos componentes Table. Substitua pelo seguinte:
begin
tblCliente.Open;
tblVenda.Open;
end;
Notas: Os comandos existente no procedimento de evento OnCreate serão executados no momente que o
formulário for criado.

Alterando Propriedades do Formulário

Vamos alterar algumas características desse formulário. Ao executar observe que ele permite
alterar registros da tabela mestre, no caso a tabela de clientes. Mas você pode proibir esse tipo de
alteração. A forma mais simples de fazer isso é alterar a propriedade ReadOnly de 'tblCliente' para o
valor True. Isso proíbe qualquer forma de alteração na tabela, seja edição, inclusão ou exclusão de
registros.

Com isso, alguns botões do DBNavigator, como , , , e , que fazem


alterações na tabela, estarão sempre desabilitados e não têm mais função. Por isso vamos removê-los do
DBNavigator. Selecione o controle e a propriedade VisibleButtons. Essa é uma propriedade de conjunto
que determina quais botões são visíveis. Clique duas vezes no nome da propriedade para expandir os
elementos do conjunto. Cada um deles corresponde a um botão do DBNavigator. Altere para False os

elementos nbInsert (afeta o botão ), nbDelete ( ), nbEdit ( ), nbPost ( ) e nbCancel ( ).


No DBGrid da parte inferior, o campo 'CodCliente' está disponível para visualização e edição, mas ele
sempre terá o mesmo valor que o campo CodCliente da parte superior, portanto vamos torná-lo invisível.

Para tornar o campo invisível, clique duas vezes no componente 'tblVenda' para abrir o editor de
campos. Selecione o campo CodCliente. Não remova da lista, senão o Delphi não vai conseguir manter o
relacionamento. Apenas altere sua propriedade Visible para False.

Altere o Caption do formulário para "Clientes e Vendas" e a propriedade Name para


formCadCliVend . Altere também o label "CodCliente", para "Código". Se quiser, reduza o tamanho do
painel que contém os controles de edição para a tabela de clientes. Você deve clicar na borda mais
espessa e não no fundo do painel.

Execute o programa e verifique que agora não é possível editar os campos da tabela de clientes,
apenas adicionar e remover registros de vendas relacionados.
Salvando o Projeto

Salve esse projeto como CADCLIVEND.


Usando Três Tabelas no Formulário
Com o Database Form Wizard só é possível criar um formulário mestre/detalhe para no máximo duas
tabelas. Algumas situações exigem tratar um relacionamento mestre à detalhe à detalhe, com três tabelas,
ou dois relacionamentos mestre/detalhe no mesmo formulário. Para isso é preciso criar manualmente os
relacionamentos, alterando as propriedades MasterSource e MasterFields do componente Table.
Agora vamos relacionar a tabela de vendas com a tabela de itens vendidos (ITEM), que contém
informações sobre cada item individual vendido.

Ligando os Componentes de Dados

Abra o projeto anterior, VENDA.DPR, se ele já não estiver aberto no Delphi. Nós precisamos
alterar o formulário de clientes e vendas. Primeiro adicione ao formulário mais um componente Table e
mais um DataSource.

Para o componente Table, altere as seguintes propriedades: Name para 'tblItem', DatabaseName
com o nome do alias, "CursoDelphi" e, TableName com o nome da tabela "ITEM.DB" (note que a lista
de valores de TableName mostra as três tabelas).

Para cada registro de venda, haverá vários registros de item de venda, relacionados através do
campo "CodVenda". Para fazer esse relacionamento, altere a propriedade MasterSource, selecionando
da lista o 'dsVenda'. Isso define qual a tabela mestre para esta. Agora clique na propriedade
MasterFields e no botão de reticências. Deve aparecer uma caixa de diálogo, onde você pode facilmente
definir o relacionamento, bem semelhante ao que aparece no Database Form Wizard. Em "Available
indexes", selecione o índice "IndVenda". Nas listas abaixo, selecione o campo "CodVenda" em cada uma
e clique no botão Add. Depois clique em Ok.

Isso vai alterar automaticamente a propriedade IndexFieldNames para conter "CodVenda", e


MasterFields contém "CodVenda" também. Ambas definem os nomes dos campos que participam do
relacionamento.

Agora selecione o componente DataSource que você colocou por último. Mude o seu nome para
'dsItem' e na propriedade DataSet, selecione 'tblItem' para ligá-lo com o último componente Table.

No evento Oncreate do formulário coloque tblitem.open;

Criando Controles de Dados


Vamos ampliar o formulário para incluir um grid para a tabela de itens. Mas antes de fazer isso,
precisamos alterar propriedades do painel que contém o grid da parte inferior.

Clique no grid de vendas e tecle [Esc] para selecionar seu painel. Altere sua propriedade Align
para 'alTop'. O valor anterior era 'alClient', que alinhava o painel de acordo com o espaço disponível no
formulário. Clique no quadrado de seleção inferior e arraste-o para reduzir o tamanho vertical do painel.
Isso vai deixar um espaço vazio na parte inferior do formulário. Se preciso, aumente o formulário na
vertical para aumentar essa área.

Para manter a aparência do formulário, selecione a página Standard e o componente Panel, ,


que é o último ícone. Coloque um componente na parte inferior. Esse é um controle cuja finalidade
geralmente é só a de conter outros controles. Altere suas propriedades da seguinte forma:
Propriedade Valor Efeito
Caption (vazio) tira o texto "Panel2"
BevelInner bvLowered efeito3-d interno "afundado"
BorderWidth4 aumenta a borda
Align alClient preenche o espaço disponível

Agora, selecione a página Data Controls na paleta de componentes e selecione o DBGrid ( ).


Coloque-o em cima do painel. Altere sua propriedade Align para 'alclient', para que ele ocupe o espaço
do painel e BorderStyle para 'bsNone', para remover a borda preta em volta do controle. Altere também
DataSource para 'dsItem'. Com isso, a grade vai mostrar os dados da tabela de itens e permitir alteração.

Para que o campo 'CodVenda' não apareça na grade, selecione o componente 'tblItem' e clique
duas vezes para abrir o editor de campos. Adicione todos os campos, selecione 'CodVenda' na lista e
altere a propriedade Visible para False.

Criando Campos Calculados


Usando um componente Table, você pode mostrar, além dos campos da tabela, campos calculados, que
aparecem da mesma forma que campos do registro, mas sem permitir alteração. O valor de um campo
calculado é definido pelo código do seu programa. Quando o Delphi precisa mostrar um campo
calculado, ele aciona o evento OnCalcFields do componente Table.
No nosso caso, a tabela de itens terá um campo calculado. O preço de venda do item é um preço unitário,
que deve ser multiplicado pela quantidade de itens vendidos. Para isso, vamos criar um novo campo
PrecoTotal contendo esse resultado.

No formulário, clique duas vezes no componente 'tblItem', para abrir o editor de campos. Para
definir um novo campo calculado, clique com o botão direito e em "New Field...". Você deve definir um
nome para o campo. Digite "PrecoTotal". O nome do componente TField é criado automaticamente
como "tblItemPrecoTotal", mas você pode mudar se precisar. Você deve também escolher um tipo de
dados para o campo. Escolha "FloatField" na lista (tipo real) e clique Ok.
Todos os componentes TField têm nomes baseados no nome do componente Table ao qual eles
pertencem, como "tblItemCodVenda" etc. Esses nomes podem ser alterados, se necessário, selecionando
o componente na lista e alterando sua propriedade Name. Para definir o valor de um campo calculado no
programa, precisamos saber o nome dos componentes utilizados.

Para realizar o calculo do Preço Total é necessário ter o preço de venda do produto, portanto vamos
acrescentar um componente Table e um DataSource, que estarão associados com a tabela "Produto.db".

Para o componente Table, altere as seguintes propriedades: Name para 'tblProduto',


DatabaseName com o nome do alias, "CursoDelphi" e, TableName com o nome da tabela
"PRODUTO.DB".

Clique duas vezes no componente 'tblProduto', para abrir o editor de campos.Para adicionar os
objetos de campos clique em "Add..." em seguida clique no botão 'Ok'.

Agora selecione o componente 'tblItem' e crie um procedimento para o seu evento OnCalcFields.
Esse procedimento é executado a cada vez que um registro da tabela é mostrado. Durante a sua execução,
você pode atribuir valores para campos calculados, como 'tblItemPrecoTotal'. Digite o seguinte:
begin
if tblproduto.FindKey([tblitemCodProduto.value]) then
tblItemPrecoTotal.Value := tblItemQuantidade.Value *
tblProdutoPrecoVenda.Value;
end;
Para ler ou alterar o valor de um componente TField, leia ou altere sua propriedade Value. O tipo dessa
propriedade depende do tipo de dados do campo. Como o Preco de Venda do produto está na tabela
produto é necessário obter o valor desta tabela, portanto, utilizamos o método FindKey para
posicionarmos no produto que estamos querendo o preço. E o objeto de campo "PrecoTotal" será o valor
da quantidade vendida multiplicada pelo preço de venda do produto.
Ao invés de ficar mostrando o código do produto , iremos também mostrar o nome do produto, para isto
temos que criar um outro campo LooKup, mas este será mostrado o campo nome da tabela produto.

Usando um campo Lookup


Para mostrar o nome do produto , vamos criar um campo Lookup.Um campo Lookup traz dados de uma
outra tabela relacionada. Neste caso, o campo irá buscar um registro na tabela de produto, através do
campo CodProduto, e mostrará o nome do produto.

Para inserir um campo Lookup, clique duas vezes em 'tblItem' para abrir o editor de campos.
Clique com o botão direito e selecione "New Field...". Em "Name", especifique "NomeProduto", em
"Type" selecione "string", em "Size" informe 50. Marque a opção "Lookup".

Agora em "Key Fields", selecione "CodProduto" .Esse é o campo que será usado para buscar o
produto na outra tabela. Em "DataSet", selecione "tblProduto", que é a tabela de onde será buscado os
dados. Finalmente , em "LooKup Keys", selecione "codproduto" este é o campo que será utilizado para
fazer o relacionamente da tabela produto com a tabela item e em "Result Field" coloque nome (o campo
de resultado que será mostrado). As propriedades estarão como abaixo:
Arraste o campo LookUp 'NomeProduto' , para a primeira posição e o campo 'PrecoTotal'
coloque-o na última posição, como na figura abaixo:

Testando e Salvando o Projeto


Para que o usuário possa acessar o formulário de vendas , é preciso mostrar esse formulário a partir do

menu principal. Para isso , retorne ao formPrincipal. Você pode usar o botão .

Agora, acrescente o seguinte código ao evento Onclick do item de menu Cadastro|Vendas:


procedure TformPrincipal.menuCadVendasClick(Sender: TObject);
begin
formCadCliVend.show
end;

Para funcionar, você precisa adicionar uma cláusula uses, no início da seção de implementação
(implementation) do formulário. Adicione o seguinte:
uses CadCliVend
Agora execute o programa para ver o efeito das modificações. Note que no grid da tabela Item, o campo
calculado PrecoTotal , os campos LooKup PrecoVenda e Nome vão aparecer para cada registro. Se
você alterar os campos Quantidade , PrecoVenda ou o Produto, o evento OnCalcFields será executado, e
o valor do campo será alterado. Na verdade, esse evento é executado para qualquer alteração de campo,
mesmo os que não afetam seu valor.

Finalize a execução e salve o projeto novamente.

Atualizando Quantidade em Estoque


Ao realizar uma venda de um determinado produto, é necessário atualizar a quantidade disponível em
estoque.

Então antes de salvar os dados na tabela de item, iremos atualizar a tabela de produto. O evento
que ocorre antes de gravar um determinado registro é o evento OnBeforePost, crie um procedimento
para este evento na tabela 'tblItem', e acrescente o código a seguir:
procedure TformCadCliVend.tblItemBeforePost(DataSet: TDataSet);
begin
if not tblproduto.findkey([tblItemCodProduto.value]) then
begin
showmessage('Produto não encontrado!');
abort;
end;
tblproduto.edit;
if tblItem.State in [dsEdit] then
tblProdutoqtdEstoque.value := tblprodutoqtdEstoque.value +
QuantidadeAnterior;
if tblitemquantidade.value > tblprodutoqtdEstoque.value then
begin
showMessage('Quantidade Insuficiente no Estoque!');
tblProduto.cancel;
if tblitem.State in [dsEdit] then
tblItem.Cancel.
tblItemQuantidade.FocusControl;
abort;
end
else
tblProdutoqtdEstoque.value := tblprodutoqtdEstoque.value -
tblItemQuantidade.value;
tblproduto.post;
end;
O método FindKey foi utilizado para verificar se o produto incluído existe, caso não exista irá aparecer
uma mensagem e o procedimento será abortado. Quando o procedimento OnBeforePost é abortado o
evento OnAfterPost não ocorre, portanto, a venda não será efetuada.
O método Edit foi utilizado para colocar a tabela 'Produto' em modo de Alteração , com isso pode ser
feito alterações em qualquer campo desta tabela . Antes de atualizar o campo 'QtdEstoque' , é necessário
verificar se a tabela 'Item' esta sendo alterada, caso esteja , o campo alterado é 'Quantidade' ( pois é o
único campo que iremos permitir alteração) , portanto, temos que adicionar o valor antigo ao estoque ,
para depois retirar a quantidade desejada, o valor antigo foi armazendo na variável QuantidadeAnterior.
Em seguida é feito uma verificação se o valor que esta sendo vendido é maior que o estoque existente, se
isso ocorrer não podemos permitir a venda do item, então cancelamos a alteração feito na tabela
produto(está alteração ocorre somente se o campo 'quantidade' da tabela item esta sendo alterado) ,
cancelamos também as alterações da tabela item , caso ela esteja em modo de edição e em seguida
abortamos o procedimento. Caso não tenha ocorrido nenhum dos problemas citados anteriormente,
finalmente podemos subtrair a quantidade vendida pela quantidade existente no estoque.

Para atualizar o estoque estamos considerando que o único campo que possa ser alterado da tabela
'Item' é o campo 'Quantidade', para fazer a alteração do produto é necessário excluir o item de venda e
incluir novamente. Não iremos aceitar modificação no campo 'codproduto' da tabela 'item', vamos criar
um procedimento no evento OnChange do objeto 'tblItemQuantidade', para criar este procedimento
clique duas vezes na tabela 'Item', com isso irá abrir o editor de código, escolha o campo 'Quantidade', no
'Object Inspector ', clique na página 'Events', em OnChange, acrescente o código a seguir:
procedure TformCadCliVend.tblItemCodProdutoChange(Sender: TField);
begin
if tblItem.state in [dsEdit] then
begin
showmessage('Não é permitido a alteração do produto!');
tblitem.cancel;
abort;
end
end;

Falta criar a variável 'QuantidadeAnterior' e atribuir um valor, abaixo da seção implementation


acrescente:
Var QuantidadeAnterior : integer;

Essa variável irá conter o valor do campo 'Quantidade' antes de ser modificado, crie um
procedimento para o evento OnBeforeEdit, este evento ocorre antes de fazer modificações nas tabelas,
neste procedimento coloque:
procedure TformCadCliVend.tblItemBeforeEdit(DataSet: TDataSet);
begin
Quantidadeanterior := tblItemQuantidade.value;
end;

Quando o item for excluído , temos que acrescentar a quantidade que foi retirada ao estoque,
portanto, no evento OnBeforeDelete (antes de excluir) , coloque o procedimento abaixo:
procedure TformCadCliVend.tblItemBeforeDelete(DataSet: TDataSet);
begin
if tblproduto.findkey([tblItemCodProduto.value]) then
begin
tblproduto.edit;
tblProdutoqtdEstoque.value := tblprodutoqtdEstoque.value +
tblItemQuantidade.value;
tblproduto.post;
end;
end;
Neste procedimento é realizado uma procura do produto pertencente ao item , caso o produto seja
encontrado , será adicionado ao campo 'qtdEstoque' a quantidade do item excluído.
Capítulo 17
Consultas e SQL

Exemplo: Usando Consultas

Exemplo: Consultando Várias Tabelas

Outros Comandos SQL

Consultas num Ambiente Cliente/Servidor

Métodos e Eventos do Componente Query


Exemplo: Usando Consultas

O componente - Query é usado para obter dados baseados em um comando SQL. Ele pode ser
usado em situações onde uma tabela não seria suficiente, por exemplo:
· Acessar um subconjunto da tabela, em vez de todos os registros
(isso é fundamental para ter bom desempenho num ambiente cliente/servidor)
· Reunir dados de múltiplas tabelas (junções)
· Totalizações de dados de vários registros
A forma mais fácil de usar um componente Query é usar o Database Form Expert para criar um
formulário.

Criando o Formulário

Abra o projeto VENDAS.DRP e acione o menu Database |Form Wizard.... Na primeira tela do
DFW, mantenha a opção "Create a simple form" na parte superior, mas na parte de baixo selecione
"Create a form using TQuery objects". Clique em Next.

Na próxima tela, na lista "Drive or alias", selecione CursoDelphi. Na lista de tabelas, selecione a
tabela VENDA.DB, depois selecione todos os campos e escolha o layout "In a grid". Na última tela,
desmarque a opção "Generate a main form" e clique em Finish. O formulário gerado é semelhante aos
que já criamos com o DFW, mas ele utiliza um componente Query em vez de um Table. Os componentes
de dados estão no topo do formulário:

Query1 DataSource1
Selecione o componente 'Query1' e veja suas propriedades. Note que o 'DataSource1' tem na propriedade
Dataset o valor 'Query1'. Isso faz a ligação entre os dois.

Vamos alterar os nomes dos componentes para 'qryVenda' e 'dsVenda', respectivamente. Depois
altere também o código do evento OnCreate do formulário, para abrir 'qryVenda' em vez de 'Query1'.
A propriedade mais importante do componente Query é a propriedade SQL. Essa propriedade é uma lista
de strings (TStrings), que contém um comando da linguagem SQL. Clique em SQL e no botão com
reticências.
Repare que o conteúdo da propriedade é o seguinte:
Select
venda."CodVenda",
venda."CodCliente",
venda."DataVenda"
From "c:\cursodelphi\venda.DB"
As venda
O comando select é um comando SQL que consulta registros. Primeiro são especificados os campos da
tabela que serão retornados pelo select. Depois do from, vem o nome da tabela. Nesse caso, é um nome
completo, incluindo subdiretório. A expressão "as Venda" cria um apelido para a tabela dentro do
comando, que é utilizado anteriormente, na lista do select (Venda."CodCliente", por exemplo). Clique
OK para fechar o editor de strings.

Altere a propriedade Name do formulário para 'formConsVendasporCliente' e a propriedade


Caption para 'Vendas por Cliente".

Para o usuário poder visualizar esse fomulário temos que chamá-lo no evento Onclick do menu
Consultas|Vendas por Cliente da Unit VendPrin. Neste evento coloque o código abaixo:
formConsVendasporCliente.show;

Para isso funcionar, você precisa adicionar uma cláusula uses , no início da seção implementation
do formulário VendPrin. adicione o seguinte:
uses ConsVendasCliente;

Salve o formulário criado como "CONSVENDASCLIENTE.PAS". Execute o programa e veja o


resultado: você pode percorrer os registros da tabela, mas não pode alterá-los.

Obtendo um Resultado Modificável


Uma consulta geralmente retorna um resultado que não é modificável, ou seja, registros apenas para
leitura. Mas você pode pedir ao Delphi para retornar um conjunto de registros modificável ["live" result
set], que permite acessar a tabela como através de um componente Table.

Para isso, altere a propriedade RequestLive do componente Query para True. Quando esta
propriedade é verdadeira, o componente vai tentar retornar um conjunto de registros modificável, mas
nem sempre isso é possível. Em tempo de execução, você pode verificar se a consulta é modificável,
consultando sua propriedade CanModify (do tipo boolean).

Execute o programa e agora você deve ser capaz de fazer alterações nos registros.

Simplificando o Select

O comando select, que retorna os dados, pode ser simplificado, e podemos evitar que o diretório da
tabela seja codificado de forma fixa. Abra novamente a propriedade SQL e substitua todas as linhas do
comando pelo seguinte:
select *
from Venda
Você pode colocar os comandos na mesma linha, ou em linhas separadas. O "*" (asterisco) no select em
vez de uma lista de campos seleciona todos os campos da tabela, independentemente da estrutura dessa
tabela. Note que não é preciso colocar a extensão de arquivo (.DB) e é melhor não colocar porque isso
torna mais fácil alterar o programa para trabalhar com outros formatos de dados (onde tabelas não são
arquivos).

Execute o programa e repare que ele continua funcionando como antes.

Alterando a Consulta

Uma consulta que retorna todos os registros não é realmente muito utilizada. A principal função de
consultas é restringir os registros retornados a um subconjunto dos dados. Para fazer isso, vamos alterar a
propriedade SQL novamente, acrescentando uma cláusula where ao final:
select *
from Venda
where DataVenda = '08/02/98'
Isso vai retornar todos os registros onde o campo DataVenda tenha o valor '02/08/98', e apenas esses
registros. Observe que a data deve ser informada como 'mm/dd/yy' . Execute o programa e veja o
resultado. Note que se você alterar o valor do campo DataVenda, e salvar o registro, o registro vai
desaparecer do resultado. Quando o tipo do campo for data ou Caracter a condição deve ser passada
entre apóstrofos, caso seja um valor numérico especifique somente o número.
Nota: na sintaxe do SQL, para inserir um texto, você pode usar " (aspas) ou ' (apóstrofos).

Alterando a Consulta Dinamicamente


A consulta como está é uma consulta estática, ou seja, a condição de seleção de registros nunca muda,
porque está usando um valor constante, '08/02/98'. Você pode fazer também uma consulta dinâmica, e
alterar a condição em tempo de execução.

Para fazer isso, vamos alterar a propriedade SQL para incluir um parâmetro no lugar da constante
'08/02/98'. Um parâmetro é uma variável dentro do comando SQL, que você pode alterar dinamicamente.
Altere o SQL para o seguinte:
select *
from Venda
where DataVenda = :ValorData
Isso cria um parâmetro chamado 'valorData'. O que define o parâmetro são os dois-pontos antes do nome.
Depois de fazer essa alteração, clique Ok.
Nota: Não deve haver nenhum espaço entre o dois-pontos e o nome do parâmetro.

Agora selecione a propriedade Params e abra o seu editor. O editor de parâmetros, que vai
aparecer, é onde você define qual o tipo de dados a ser usado pelo parâmetro, primeiro deve-se escolher o
parâmetro e ir no object inspector:
Expande a propriedade Value em type escolha "Date", que será o tipo de dados do campo , ao
confirmar o Object Inspector ira ficar como a figura abaixo:

Notas: Nos parâmetros colocamos as datas sendo 'dd/mm/yy', pois, nos parâmetros as datas são do tipo
date e ao passar para a linguagem SQL o Delphi coloca no formato que o SQL utiliza.

Em "Value" você pode, opcionalmente, fornecer um valor inicial para o parâmetro, ou marcar a
opção "Null Value", que significa que ele começa com o valor nulo, ou vazio. Nesse caso digite
"02/08/98" e depois clique Ok.

Precisamos de uma interface para alterar o parâmetro dinamicamente. Na página Standard,

selecione o ícone do GroupBox ( ) e coloque um controle no formulário, em qualquer espaço vazio.


Altere sua propriedade Caption para "Parâmetros".

Agora crie controles dentro do GroupBox "Parâmetros", dentro dele acrescente o componente

MaskEdit ( ) , seu funcionamento é idêntico ao Edit a diferença é que ele permite utilizar máscaras.
Altere a propriedade Name para mskDataVenda, clique na reticências da propriedade EditMask e
escolha a opção "Date", substitua os '0' por '9', em seguida clique em "Ok", com uso definimos uma
máscara para o componente de edição. Acrescente um componente Button e um Label , conforme a
figura abaixo:
Chame o botão de 'btnAlterar' e mude a sua propriedade Default para True.

No código associado ao 'btnAlterar', alterar os parâmetros, e abrir a consulta novamente com os


parâmetros modificados. Com isso o usuário pode realizar a consulta para qualquer data. Digite o
seguinte:
begin
try
with qryvenda do
begin
Close;
Params[0].AsDate := strtodate(mskDataVenda.Text);
Open;
end;
except
on EConvertError do
begin
showmessage('Data Inválida!');
mskDataVenda.setfocus;
end;
end;
end;
Como é feito uma conversão de String para Date, foi feito o tratamento de exceção , pois, se a pessoa
informar uma data inválida iremos mostrar a mensagem de erro 'Data Inválida'.
A propriedade Params do componente Query é um vetor de objetos do tipo TParam, com índices
começando de zero. Cada um desses objetos tem propriedades como AsString, AsInteger, ... que
permitem alterar o valor do parâmetro.

Execute o programa e veja o que acontece quando você altera os parâmetros.


Nota: Outra forma de alterar o parâmetro é usando a função-método ParamByName, fornecendo o
nome do parâmetro em vez de sua posição na lista. Por exemplo, o código abaixo também acessa o
parâmetro valorData: ParamByName('valorDAta').AsDate := strtodate(mskDataVenda.Text);

Alterando Outras Partes da Consulta


Os parâmetros permitem que você substitua constantes dentro da consulta por variáveis do programa,
mas se você precisar alterar a estrutura da consulta, precisa alterar a propriedade SQL em tempo de
execução. Essa propriedade é uma string list, que pode ser utilizada com os métodos de string lists já
vistos. O exemplo abaixo mostra como mudar a propriedade SQL dinamicamente :
with qryCliente.SQL do
begin
Clear;
Add('select * from Cliente');
Add('where Estado = "' + estado1 + '"');
Add('or Estado = "' + estado2 + '"');
end;
Ao abrir o componente QryCliente , a consulta irá mostrar os clientes que pertencem ao intervalo de
estados pedidos.
Não faremos isso neste projeto, e na maioria das vezes isso não é necessário.
Exemplo: Consultando Várias Tabelas
Tendo os nossos dados sobre clientes, vendas e itens, imagine que precisamos fazer o seguinte: consultar todos os itens
vendidos para cada cliente para uma data específica. Para isso, podemos usar um comando select do SQL envolvendo
várias tabelas, chamado de junção de tabelas.

Consulta com Duas Tabelas


Como no exemplo anterior realizamos uma consulta que mostra as vendas e os códigos dos clientes, vamos mostrar o
nome do cliente, para isto temos que alterar a propridade SQL do componente QryVenda . Como queremos mostrar o
nome do cliente temos que utilizar a tabela 'cliente' na clausula from da consulta.Altere a propriedade SQL de acordo o
comando abaixo:
select cli.Nome, vend.CodVenda
from CLIENTE cli, VENDA vend
where cli.CodCliente = vend.CodCliente
order by cli.Nome
Notas: Quando o comando Select utiliza mais de uma tabela, o componente Query não permite alterações dos dados.
Note que na cláusula from, os nomes das tabelas estão seguidos de nomes mais curtos, que são os apelidos [alias]
utilizados para as tabelas dentro do SQL. Esses apelidos afetam apenas o comando atual. A lista de campos do select
seleciona o campo Nome da tabela 'cli' (apelido de "CLIENTE.DB") e o campo CodVenda da tabela 'vend' (apelido de
"VENDA.DB").
A condição cli.CodCliente = vend.CodCliente na cláusula where é importante numa junção de tabelas. Ela diz qual a
ligação entre elas. Se não for especificada, a consulta vai funcionar, mas vai retornar o produto cartesiano das duas
tabelas, ou seja, todas as combinações possíveis com o registro de uma e o registro da outra tabela.
A cláusula order by determina que o resultado será ordenado pelo campo Nome da tabela CLIENTE.DB.

Coloque a propriedade Active do componente qryVenda para true. Com isso irá aparecer a seguinte mensagem:

Isso ocorre porque antes de fazer a modificação na propriedade SQL , foi criado um objeto de campo para o código do
cliente , com a alteração foi retirado o campo, mas como o objeto ainda existe. Ele continua procurando o campo
'codcliente', para resolver o problema clique duas vezes no componente 'qryVenda' para abrir o objeto de campo, remove
todos os objetos de campos existentes.

Altere a propridade Active para true e observe que cada registro do resultado tem o valor do campo Nome e do
campo CodVenda. Existem vários registros de resultado para cada cliente, ou seja, com o nome do cliente repetido, mas
com códigos de venda diferentes.

Consultando Três Tabelas

Para adicionar mais uma tabela na consulta, a tabela de itens de venda, vamos alterar o select, acrescentando o nome
da tabela e a condição de ligação. Desta vez vamos obter no resultado apenas o campo Nome da tabela de clientes e o
campo Quantidade, da tabela de itens. Na propriedade SQL, digite o seguinte:
select cli.Nome, item.Quantidade
from CLIENTE cli,
VENDA vend,
ITEM
where cli.CodCliente = vend.CodCliente
and vend.CodVenda = item.CodVenda
order by cli.Nome
Repare que como são três tabelas, temos duas condições de junção, uma ligando CLIENTE a VENDA e outra ligando
VENDA a ITEM

Quando você altera a propriedade SQL, o componente automaticamente muda a propriedade Active para False,
altere para True.

Usando Campos Calculados no SQL


Podemos ainda gerar um campo calculado para cada registro, que será mostrado no resultado. Uma forma de fazer isso é
definindo campos calculados e usando o evento OnCalcFields. Mas quando se usa componentes Query, outra forma mais
fácil é usar expressões no SQL.

Por exemplo, vamos calcular para cada item, o preço de venda multiplicado pela quantidade, gerando um campo
calculado 'PrecoTotal', mas o preço de venda existe na tabela produto, portanto iremos acrescentar também a tabela
produto. Altere a lista de campos do select para o seguinte:
select cli.Nome, item.Quantidade , prod.precoVenda * item.Quantidade as PrecoTotal
from CLIENTE cli,
VENDA vend,
ITEM, produto prod
where cli.CodCliente = vend.CodCliente
and vend.CodVenda = item.CodVenda and prod.codproduto = item.codproduto
and datavenda =:ValorData
order by cli.Nome
Depois da expressão é colocado um "as PrecoTotal", que define um nome de campo para a expressão (senão no cabeçalho
do campo vai aparecer a própria expressão).

Altere Active para True novamente e veja o resultado.

Usando Funções de Resumo


Em vez de retornar registros com o nome do cliente repetido, como está sendo feito, você pode, usando SQL, agrupar
vários registros, retornando um registro de resumo, contendo, por exemplo, o total de algum campo.
Para isso, você deve usar a cláusula group by do SQL, além de funções de resumo do SQL.

Por exemplo, para totalizar o produto de 'Quantidade' e 'PrecoVenda' para todos os itens relacionados a cada cliente,
basta usar o seguinte comando:
select cli.Nome,
sum(item.Quantidade * prod.PrecoVenda) as TotalVenda
from CLIENTE cli,
VENDA vend,
ITEM item,
PRODUTO prod
where cli.CodCliente = vend.CodCliente
and vend.CodVenda = item.CodVenda
and prod.codproduto = item.codproduto
and datavenda =:ValorData
group by cli.Nome
A função sum é uma função de resumo (ou função agregada) do SQL, que faz o somatório de um campo ou expressão. A
cláusula group by define como são formados os grupos de registros para fazer a totalização.
Outras funções de resumo que podem ser usadas em vez de sum são:
avg(campo) calcula a média de um campo
max(campo) calcula o valor máximo do campo
min(campo) calcula o valor mínimo do campo
count(campo) conta os valores do campo, mas só considera registros onde o campo está preenchido
count(distinct campo)conta quantos valores distintos do campo existem (em [4 2 2 4 2 3], a contagem seria 3).
count(*) conta os registros
Outros Comandos SQL
O comando select é um comando SQL de consulta, mas existem comandos SQL para atualização de tabelas, que permitem
atualizar diversos registros. Esses comandos são utilizados também através de um componente Query, mas de uma forma
ligeiramente diferente, como veremos.

Testando Comandos SQL


Para executar um comando SQL de atualização, como os que serão usados, você pode usar o Database Explorer. Clique em
Database|Explore, no menu do Delphi. Na árvore "Databases", abra o alias "CursoDelphi". Ao fazer isso, do lado direito
aparece um marcador de página "Enter SQL":

Clique em (Execute Query) para executar o comando.

Alterando Registros
Para alterar valores de registros, use o comando update. Com ele, basta fornecer a tabela, a condição de atualização e as
alterações de campos a serem feitas. Por exemplo, o comando a seguir atualiza a tabela Cliente, preenchendo o campo
Estado, com o valor 'GO', mas apenas nos registros onde Estado era vazio (is null verifica se o campo está vazio):
update Cliente
set Estado = 'GO'
where Estado is null

Excluindo Registros
Para excluir um conjunto de registros baseado numa condição, use o comando delete. Por exemplo, o seguinte comando
exclui todos os registros da tabela Venda, que estejam entre 01/01/94 e 01/01/95:
delete from Venda
where DataVenda >= '01/01/94' and DataVenda <= '01/01/95'
ou, de forma equivalente:
delete from Venda
where DataVenda between '01/01/94' and '01/01/95'
Inserindo e Copiando Registros
Para inserir um registro em uma tabela, você pode usar o comando insert:
insert into Produto (CodProduto, Nome, CodFabricante)
values (1234, 'Novo Produto', 34)
Mas geralmente essa forma não tem muita utilidade. Outra forma, que é de mais ajuda na programação, permite copiar dados
de uma tabela para outra. Para isso, use um comando select dentro do insert. Por exemplo, para copiar todos os registros da
tabela Cliente que tem Estado = 'GO' ou 'TO', para uma outra tabela 'ClienteCentroOeste', pode-se fazer:
insert into ClienteCentroOeste
select * from Cliente
where Estado in ('GO', 'TO')
Esse comando assume que ambas as tabelas têm a mesma estrutura. Se não tiverem, você
pode alterar a lista de campos do select para incluir apenas os que estão presentes
na tabela de destino, por exemplo:
insert into ClienteCentroOeste
select Codigo, Nome from Cliente
where Estado in ('GO', 'TO')

Executando um Comando de Atualização


Para usar um comando SQL de atualização, coloque o comando na propriedade SQL de um componente Query. Mas existe
uma diferença em relação ao select: um comando de atualização não retorna resultado nenhum. Por isso, você não deve abrir
a consulta (alterando Active), e nem ligar o componente com um DataSource, por exemplo.
Para executar o comando, você deve chamar o método ExecSQL do componente. Esse método não retorna resultado e só
pode ser acionado em tempo de execução. Você pode também preencher parâmetros do componente antes de executar o
comando.
Por exemplo, suponha que você quer apagar todos os registros de VENDA.DB entre uma data inicial e uma data final. Você
pode definir a propriedade SQL com o seguinte:
delete from Venda
where DataVenda between :dataIni and :dataFin
e, no programa, executar o código a seguir quando for excluir os itens:
with qryApagaItem do begin
ParamByName('dataIni').AsString := editDataIni.Text;
ParamByName('dataFin').AsString := editDataFin.Text;
ExecSQL;
end;
Consultas num Ambiente Cliente/Servidor
Num ambiente cliente/servidor, consultas são a forma mais eficiente de acesso aos dados. Nesse tipo de
ambiente, uma consulta envia o comando SQL ao servidor, para ser processado localmente. O servidor
então retorna apenas os registros pedidos. Se você utilizar um componente Table, você sempre estará
acessando todos os registros, e isso traz as desvantagens que já vimos, de gerar tráfego de rede.
Você pode também usar expressões em comandos SQL, como vimos nos exemplos. A vantagem é que o
cálculo da expressão é feito no servidor, aliviando o processamento na estação, que recebe apenas os
resultados.
Funções de resumo também repassam parte do processamento para o servidor, que vai fazer toda a
operação de totalização e retornar os resultados.
Quando você usa comandos de atualização, como update, o comando é enviado ao servidor, que o
processa localmente. Nesse caso, como ele não retorna resultados, o tráfego entre a estação e o servidor é
o mínimo possível.
Métodos e Eventos do Componente Query

As classes de componentes TTable ( ) e TQuery ( ) são ambas derivadas de uma classe ancestral
comum, chamada TDataSet. Quase todos os métodos e eventos que vimos para o componente Table se
aplicam também a um componente Query:
· Métodos de abertura e fechamento: Open, Close.
· Métodos e propriedades de movimentação: First, Next, Prior, Last, MoveBy, propriedades BOF e
EOF.
· Métodos de modificação: Edit, Insert, Post, Cancel, Delete. Note que se a consulta não retorna um
resultado modificável, não é possível utilizar esses métodos, e a propriedade CanModify terá o valor
False.
· Métodos e propriedades de acesso a campos: FieldByName, propriedade Fields.
· Métodos para os controles de dados: EnableControls, DisableControls.
· Eventos: Before..., After..., OnCalcFields, OnNewRecord.
Capítulo 18
Instalador e Alias Local

Usando um Alias Local

Criando um Programa Instalador


Usando um Alias Local
Se você quisesse alterar o diretório dos dados em tempo de execução, teria que alterar a propriedade
DatabaseName de todos os componentes de dados. Mas, como já vimos, um alias resolve esse
problema: ele armazena a localização do diretório, evitando que sejam necessárias alterações no
programa. Mas um alias não pode ser criado dinamicamente pelo programa, e não resolve algumas
situações, como por exemplo, um programa que usa um diretório diferente a cada vez que é rodado.
Para esse tipo de situação, você pode usar um alias local. Um alias local funciona apenas dentro do seu
programa, enquanto ele está rodando, e pode ser modificado durante a execução.

Usando o Componente Database

O componente Database da página Data Access ( ) permite criar um alias local. No


'FormPrincipal', coloque um componente Database. Mude o seu nome para 'dbVendas'. Esse componente
tem várias propriedades, mas a forma mais fácil de alterá-las é usando o Database Editor. Clique duas
vezes no componente para chamar esse editor. A janela do Database Editor é semelhante à seguinte:

Em "Name", você digita o nome do alias local que você está criando. Depois, se você escolher um valor
da lista "Alias name", seu alias local será sinônimo para um alias já existente. Senão, você deve escolher
um driver de banco de dados em "Driver name" e alterar seus parâmetros de configuração em "Parameter
overrides". Repare que "Alias name" e "Driver name" são mutuamente exclusivas.

No nosso caso, em "Name", digite "VENDAS". Esse será o nome do nosso alias local. Na lista
"Driver name", selecione "STANDARD" e clique no botão "Defaults". Esse botão preenche a lista de
parâmetros, de acordo com os parâmetros de configuração do driver. No caso do driver "STANDARD",
usado para bancos de dados locais, só importa o parâmetro PATH. A lista vai mostrar uma linha com o
parâmetro PATH, sem valor definido:
PATH=

Na lista de parâmetros, coloque o diretório atual dos dados depois do parâmetro:


PATH=C:\CURSODF\TABELAS
Isso basta para definir o alias local. As outras opções só tem utilidade em bancos de dados
cliente/servidor, onde é necessário fornecer uma senha para conectar com o gerenciador de bancos de
dados: "Login prompt", se ativo, mostra uma janela que requisita o nome de usuário e senha para
conexão. E "Keep inactive connection", se ativado, diz para manter a conexão com o banco de dados
mesmo quando ele não estiver sendo usado.

Clique Ok e repare que o Database Editor altera várias propriedades do componente Database, de
acordo com as opções escolhidas: DatabaseName fica com o valor "VENDAS", DriverName com o
valor "STANDARD", LoginPrompt e KeepConnection com o valor True e Params (do tipo TStrings)
com os parâmetros. Note que o nome do alias (DatabaseName = "VENDAS") não é necessariamente
igual ao nome do componente (Name = "dbVendas").

Alterando os Componentes de Dados


Agora, vamos alterar a propriedade DatabaseName de cada um dos componentes Table dos formulários
do projeto.

Para começar, no formulário de fabricantes, selecione o componente 'tblFabricante' e clique na


propriedade DatabaseName. O nome do alias local que foi criado, VENDAS, vai aparecer na lista de
valores, juntamente com os aliases globais, como CursoDelphi. Selecione VENDAS na lista.

Agora no formulário de produtos, selecione ambos os componentes 'tblProduto' e 'tblFabricante' e


altere DatabaseName para VENDAS. Faça o mesmo para todos os formulários e seus componentes
Table ou Query.

Execute o programa e verifique que ele ainda continua funcionando.


De agora em diante, se você quiser alterar o diretório onde estão os dados, basta alterar a propriedade
Params do componente 'dbVendas' no formulário principal, em tempo de projeto. Mas a verdadeira
utilidade deste componente é permitir alterar a localização dos dados em tempo de execução.

Alterando o Caminho em Tempo de Execução


A propriedade Params do componente 'dbVendas', no formulário principal, contém uma lista de valores
de parâmetros de configuração. Essa é uma propriedade do tipo string list, que pode ser manipulada com
os métodos já vistos para string lists.
Vamos alterar o caminho do diretório dinamicamente, de acordo com o diretório onde está o programa
executável. Com isso, o programa vai sempre encontrar os dados, desde que os arquivos de dados
estejam no mesmo diretório.

No formulário principal, acrescente no procedimento do evento OnCreate, os códigos abaixo:


var
diretorio: string;
begin
diretorio := ExtractFilePath(Application.EXEName);
dbVendas.Connected := False;
dbVendas.Params[0] := 'PATH=' + diretorio + 'tabelas\';
end;
O objeto Application tem a propriedade EXEName, que contém o caminho completo do arquivo
executável. Usamos essa propriedade com a função ExtractFilePath, para extrair apenas a parte do
diretório. Depois alteramos a primeira linha da propriedade Params, de 'dbVenda', para utilizar esse
diretório.
A propriedade Connected do componente Database "ativa" o banco de dados, assim como Active faz
para uma tabela. Não é normalmente necessário alterar essa propriedade manualmente, porque o
componente Table já faz isso automaticamente. Mas num ambiente cliente/servidor, geralmente é melhor
alterá-la no início da execução. Para alterar os parâmetros, ela deve estar com o valor False, por isso é
alterada logo no início.
É importante notar que o código acima é executado antes do código do OnCreate para os outros
formulários. Quando o programa tenta abrir uma tabela que depende de um componente Database, é
preciso que ele esteja configurado com parâmetros válidos.

Execute o programa e veja que ele continua funcionando, desde que os arquivos de dados estejam
no mesmo diretório do executável. Salve o projeto novamente.
Lembre-se: A propriedade Params é uma lista de strings (v. cap. 12), por isso pode ser acessada como
um vetor onde o primeiro elemento tem o índice zero.
Criando um Programa Instalador
Como já foi dito, ao executar um projeto no Delphi, ele cria um programa executável, um arquivo que
pode ser executado independentemente do Delphi. Por exemplo, no nosso projeto anterior
(VENDAS.DPR), o programa se chama VENDAS.EXE. Você pode executá-lo diretamente no Windows.

Finalize o Delphi. No Windows, clique em [ ]|Executar... , digite


"C:\CURSODF\VENDAS.EXE" e clique Ok. Isso irá executar o programa fora do Delphi.
No entanto, é preciso uma forma mais amigável de executar o programa, para que o usuário não precise
digitar o nome do executável. Geralmente, os programas no Windows são executados através de atalhos,
que muitas vezes ficam no menu . Se você for copiar esse programa para outro usuário, não precisa do
Delphi para executá-lo, mas precisa instalar alguns arquivos do BDE (quando se utiliza bancos de
dados).
O Delphi (edições Professional e Client/Server) vem com um programa chamado InstallShield Express,
para criar programas instaladores. Um programa instalador copia para o computador do usuário o seu
arquivo executável e mais os arquivos que ele precisa utilizar.

Iniciando o InstallShield
O InstallShield trabalha com projetos de instalação [setup projects]. Um projeto de instalação é um
arquivo contendo um conjunto de opções que definirão o conteúdo e funcionamento do seu instalador.
Para o projeto Vendas, iremos criar um projeto de instalação, que criará disquetes de instalação para o
programa. Esses disquetes contêm um programa de instalação (SETUP.EXE) e todos os arquivos
necessários ao funcionamento do programa VENDAS.EXE, compactados para ocupar menos espaço.
Quando você fornece os disquetes de instalação a um usuário, ele pode executar o programa de
instalação, que copia VENDAS.EXE e os outros arquivos necessários para o computador do usuário e
cria um atalho no menu do Windows, para poder executar VENDAS.EXE.

Execute o InstallShield através do menu [ ]|Programas, no ícone "IS Express Delphi 4.0 ". Ao
executá-lo, aparece uma tela com três opções: "Open an existing Setup Project" é usado para abrir um
projeto de instalação feito anteriormente. "Open your last Setup Project" abre o último projeto de
instalação usado (se houver). No nosso caso, use "Create a new Setup Project" para criar um novo projeto
de instalação.
Agora você verá a janela New Project, que permite escolher o nome do projeto de instalação e o local
onde ele será salvo. Em "Project Name", digite "VENDAS". Na lista de diretórios, selecione CURSODF,
sob o drive C:. Em "New Subdirectory", digite "INSTVEND". Isso irá criar um novo subdiretório sob
CURSODF, para conter os arquivos do projeto de instalação. Finalmente clique no botão Create. O
InstallShield criará um novo arquivo, chamado VENDAS.iwz, no diretório C:\CURSODF\INSTVEND.
Agora você verá uma janela semelhante a um caderno de anotações, que contém a chamada checklist,
com as tarefas que você deve fazer para criar um programa instalador.
Nota: o InstallShield não é instalado em conjunto com o Delphi. Ele deve ser instalado separadamente a
partir do diretório ISXPRESS do CD do Delphi.

Criando um Instalador
A criação de um instalador envolve diversas fases:
• Definir o Projeto Visual [set the visual design]: nesta fase, você fornece informação básica ao
InstallShield sobre sua aplicação, como nome descritivo, nome do arquivo executável etc. e informações
sobre as mensagens que serão mostradas durante a instalação.
• Selecionar Objetos para o Delphi[Select InstallShield Objects for Delphi]: define se o programa usa ou
não o BDE para acesso a banco de dados e, caso use, quais são os drivers necessários que devem ser
incluídos com o programa.
• Especificar Componentes e Arquivos [specify components and files]: O InstallShield permite criar uma
instalação com várias opções para o usuário. Você pode definir tipos de instalação [setup types], como
"típica", "compacta", "personalizada" que serão apresentados como escolha para o usuário. Você pode
definir componentes [components], que são conjuntos de grupos [groups] a serem incluídos e cada grupo
é um conjunto de arquivos que pode ser incluido ou não na instalação. O mais importante é que nesta
fase você pode acrescentar arquivos adicionais além do seu executável.
• Escolher Componentes da Interface [select user interface components]: permite configurar as várias
janelas de diálogo que o programa de instalação mostra.
• Fazer Mudanças no Registro [make registry changes]: O Registro [registry] é o banco de dados central
de configuração do Windows. Nesta fase, é possível definir modificações que serão feitas no registro.
Não entraremos em detalhes sobre este tópico.
• Especificar pastas e ícones [specify folders and icons]: define qual o nome da pasta no menu iniciar
que será criada para o programa e quais os ícones que o programa criará no menu iniciar.
Após definir as opções, você pode gerar os discos com o Disk Builder, um processo que irá compactar
todos os arquivos necessários e dividir em disquetes. Depois você pode testar a instalação, executando o
programa de instalação no seu próprio computador para ver o funcionamento. Quando tudo estiver
pronto, você pode gerar os discos de distribuição.

Definindo as Opções de Instalação


Vamos definir as opções para o instalador do Controle de Vendas. Primeiro clique em "Application
Information" para abrir a caixa de diálogo "App Info". Em "Application Name", informe "Controle de
Vendas". Esse é o nome que aparece para o usuário. Clique no botão Browse para buscar o arquivo
executável da aplicação. Procure o diretório C:\CURSODF e selecione VENDAS.EXE nesse diretório.
Em "Version", deixe 1.0 e em "Company", digite o nome da sua empresa.
Note que o diretório de destino, onde o programa será instalado, aparece embaixo como:
<ProgramFilesDir>\nome-empresa\Controle de Vendas
O nome <ProgramFilesDir> é uma variável do InstallShield que será substituída no momento da
instalação, com o nome do diretório de programas do usuário (geralmente "C:\Arquivos de Programas").
Você pode alterar esse diretório se quiser.
Você pode clicar na página "Main Window" para alterar o texto que aparece durante a instalação, em
"Main Title". Coloque também "Controle de Vendas". Você poderia escolher um logotipo também (uma
imagem desenhada no MS Paint do Windows, por exemplo), mas não vamos alterar essa opção.
Clique Ok. Agora vamos alterar as opções do BDE. Clique em "General Options", abaixo de
"InstallShield Objects for Delphi". Aparece uma lista com a opção BDE (Borland Database Engine).
Marque a opção.
Aparecerá uma outra janela para escolher o tipo de instalação. Como nosso programa utiliza apenas o
formato de bancos de dados Paradox, vamos selecionar instalação parcial ("Partial BDE Installation").
Em "BDE Driver Types", marque "Paradox driver". Em "Query Engine Types", marque "SQL Engine".
Depois clique [Avançar].
O título muda para "BDE Alias Step 1 of 4". Opcionalmente, você pode definir para o InstallShield os
apelidos de bancos de dados que serão criados no computador do usuário. No nosso caso, não estamos
usando apelidos externos ao programa, mas sim locais, por isso você pode pular todos os passos. Clique
em Avançar três vezes, depois clique em Fim. Agora clique em Ok para retornar à tela principal.

Definindo os Arquivos Utilizados


Quando você definiu as informações iniciais, o InstallShield já acrescentou o arquivo do programa,
VENDAS.EXE, à lista de arquivos a serem instalados. Você pode também acrescentar outros arquivos
como, no nosso caso, as tabelas usadas pelo programa, que se compõe dos seguintes arquivos:
CLIENTE.DB, VENDA.DB, ITEMVEND.DB, PRODUTO.DB, FABRIC.DB e todos os arquivos que
têm o mesmo nome, com extensões .PX, .X?? e .Y??.
No "caderno" do InstallShield, clique em "Groups and Files". Aparece uma árvore com os grupos de
arquivos. Clique no sinal [+] ao lado de "Program Files" para expandir esse grupo. Note que já contém o
arquivo executável, VENDAS.EXE. Para acrescentar outros arquivos, clique no botão "Insert Files...".
Isso vai abrir uma janela, mostrando os diretórios do drive C:. Selecione o diretório "CURSODF" para
ver os arquivos que ele contém. Selecione s arquivos com extensão '.DB', '.PX', '.VAL', '.X','.Y' (como
CLIENTE.XG0, FABRIC.YG0 etc.).
Após colocar todos os arquivos na lista e conferir, clique Ok.

Escolhendo as caixas de diálogo


O InstallShield tem várias caixas de diálogo predefinidas, que são apresentadas durante a instalação do
programa. Algumas delas têm opções de configuração diversas que podem ser alteradas. Para esse
exemplo, não alteraremos nenhuma opção, mas vamos apenas ver quais estão disponíveis.
Clique em Dialog Boxes na janela do InstallShield. Se uma caixa de diálogo estiver marcada, ela será
usada no instalador. Se estiver desmarcada, ela não será usada. Você pode clicar no botão "Preview" para
ver como a janela aparece em tela cheia. Se ela tiver opções a ser configuradas, você pode clicar na
página "Settings", do lado direito, para configurar. Vejamos cada uma:
Welcome Bitmap [bitmap de boas-vindas]: mostra uma imagem inicial ao usuário. Na página Settings,
você pode escolher qualquer arquivo de imagem .BMP, de 16 cores (desenhado com o Paint, por
exemplo).
Welcome Message [mensagem de boas-vindas]: mensagem genérica do InstallShield, em inglês.
Software License Agreement [contrato de licença do software]: você pode escolher um arquivo texto (na
página Settings) que será mostrado ao usuário.
Readme Information [informação tipo leia-me]: você pode escolher um arquivo texto, que mostra
informação de última hora sobre o software.
User Information [informação de usuário]: uma janela que pede nome de usuário, senha e,
opcionalmente, um número de série.
Choose Destination Location [escolha local de destino]: janela que permite ao usuário selecionar um
diretório qualquer para instalar o programa. Você pode definir, em Settings, o local default. (Se você
desativar essa janela, o usuário não pode alterar esse local).
Setup Type [tipo de instalação]: se você usa tipos de instalação (típica, personalizada, compacta), permite
ao usuário selecionar uma delas.

Custom Setup [instalação personalizada]: quando o usuário usa a instalação personalizada, permite
escolher o que será instalado ou não.
Select Program Folder [escolher pasta do programa]: permite ao usuário escolher qual a pasta onde o
programa será instalado. Em Settings, você pode mudar a opção padrão. Recomenda-se usar o mesmo
nome descritivo do programa.
Start Copying Files [iniciar cópia de arquivos]: o instalador mostra uma janela avisando que vai começar
a copiar os arquivos.
Progress Indicator [indicador de progresso]: mostra a porcentagem de arquivos copiados.
Billboards [anúncios]: mostra vários bitmaps enquanto o programa está sendo instalado. Em Settings,
você pode escolher um diretório contendo seus arquivos de imagem. Desative a menos que você tenha
criado seus próprios arquivos
Setup Complete: mostra uma janela avisando que a instalação foi completada e se oferece para executar o
programa.

Definindo os Ícones do Programa


Agora clique em "General Settings", abaixo de "Specify Folders and Icons". Você verá que o
InstallShield já definiu um ícone para o seu programa, com o nome "Vendas". Clique no ícone, altere
"Description" para "Controle de Vendas" e clique em "Modify Icon".
Se houvessem outros arquivos executáveis incluídos, você poderia criar ícones para eles também. Para
isso, basta digitar seus parâmetros e clicar em "Add Icon".
Quando existe apenas um ícone a ser instalado, é recomendável não criar um sub-menu na pasta
Programas, mas sim colocar o ícone diretamente na pasta Programas (veremos o funcionamento disso
mais tarde). Para fazer isso, clique na página Advanced, marque a opção "Programs Menu Folder" e
clique no botão "Modify Info".
Clique Ok para sair dessa janela.
Nota: o ícone que aparece para o programa é o que foi definido no formulário principal. Para mudar
esse ícone, você pode escolher outro na propriedade Icon, dentro do seu projeto.
Gerando os arquivos e testando a instalação
Para gerar os discos de instalação, clique em "Disk Builder". Você pode escolher qual o tamanho do
disquete de destino em "Disk Size" ou "CD-ROM" para gerar arquivos sem limite de tamanho. Depois
clique no botão Build. O InstallShield irá compactar os arquivos e informar quantos disquetes foram
gerados. Depois clique em Close.
Agora, para testar a instalação, no seu próprio computador, clique em "Test Run". O programa de
instalação será executado. Informe as opções indicadas até finalizar a instalação.

Após a instalação, você pode executar o programa através do menu [ ]|Programas, ícone
Controle de Vendas.
Para desinstalar o programa, abra o Painel de Controle do Windows, clique no ícone "Adicionar ou
remover programas", selecione "Controle de Vendas" na lista e clique no botão "Adicionar/remover".
Após confirmação, o programa será removido do Windows.

Gerando os disquetes de instalação


Para instalar o programa em outro computador, você pode gerar disquetes de instalação a partir do
InstallShield. Clique em "Copy to floppy". Para copiar todos os discos, clique em "Copy all disk
images". Ou selecione apenas um disco e clique em "Copy selected disk image".
Para terminar, clique em File|Save para salvar o projeto de instalação e feche o InstallShield.
Capítulo 19
Relatórios

Conceitos do QuickReport

Usando o QuickReport Wizard

Criando um Relatório com Várias Tabelas


Conceitos do QuickReport
O QuickReport é um gerador de relatórios composto de vários tipos de componentes que trabalham em
conjunto. Com ele, você pode criar visualmente o relatório, sem se preocupar com detalhes como quebras
de página, agrupamentos, subtotais etc. Os componentes do QuickReport estão na página QReport.

A página QReport

QuickRep O componente básico sobre o qual você monta os relatórios. Tem a aparência de uma folha
de papel. Como veremos, ele se conecta a um dataset, de onde traz os dados, e permite colocar componentes
imprimíveis sobre ele, que mostram os dados.

QRSubDetail Faz a ligação do relatório com outras tabelas, ligadas da forma mestre/detalhe.

QRBand Uma banda é uma faixa do relatório que é impressa em determinados momentos. Esse
componente permite criar vários tipos de bandas.

QRChildBand Cria uma banda filha, que é sempre impressa após uma determinada banda mestra. Útil
se uma banda é muito grande e se expande, ocupando várias páginas.

QRGroup Cria um agrupamento de itens dentro do relatório. Quando um determinado campo muda de
valor, uma banda de cabeçalho de grupo é impressa.

QRLabel Funciona como um componente Label, mas para impressão. Imprime o conteúdo da
propriedade Caption.

QRDBText Imprime o conteúdo de um campo do banco de dados, escolhido através das propriedades
DataSet e DataField. Pode se expandir verticalmente ou se dividir em várias páginas se necessário.

QRExpr Imprime campos calculados e outros itens.

QRSysData Imprime informações diversas como número de página, data/hora etc. A propriedade
Data define o que será impresso.
QRMemo Imprime um texto de várias linhas, que não vem do banco de dados. Pode se expandir
verticalmente ou se dividir em várias páginas se necessário.

QRRichText Imprime texto formatado, com diferentes fontes, tamanhos e estilos. O texto deve ser
definido via programação.

QRDBRichText: Imprime texto formatado, com diferentes fontes, tamanhos e estilos. O texto vem de
um campo do banco de dados, determinado pelas propriedades DataSet e DataField.

QRShape Usado para colocar desenhos no relatório, como linhas, retângulos, círculos etc.

QRImage Imprime uma imagem no relatório (ex.: o logotipo da empresa).

QRDBImage Imprime uma imagem no relatório, a partir de um campo do banco de dados,


determinado pelas propriedades DataSet e DataField.

QRCompositeReport: Permite agrupar vários relatórios para imprimir como se fossem um só

QRPreview Usado para ver uma prévia de impressão do relatório em outra janela.

Construindo o Relatório
Um relatório do QuickReport tira seus dados de um dataset principal (tabela ou consulta SQL) e de alguns
datasets secundários, que estejam de alguma forma ligados a ele. A propriedade DataSet do componente

QuickRep determina o dataset principal. Funciona como a propriedade DataSet de um componente


DataSource do Delphi.
O relatório é construído visualmente a partir de bandas, que são as diversas faixas horizontais e
componentes imprimíveis, que são colocados nessas bandas. Os relatórios mais simples têm uma banda de
cabeçalho da página [page header], uma banda de detalhes [details] e uma banda de rodapé [page footer].
Outros mais complexos podem usar outros tipos. Os tipos de bandas são:
• Título [title]: impressa uma única vez, na primeira página do relatório, após o cabeçalho de página.
• Resumo [summary]: impressa uma única vez, no final da última página do relatório. Usada geralmente
para os totais gerais.
• Cabeçalho de página [page header]: impressa no topo de cada página.
• Rodapé de página [page footer]: impressa na parte inferior de cada página.
• Cabeçalho de coluna [column header]: impressa no topo de cada coluna. Só tem utilidade com relatórios
de múltiplas colunas. (Vide propriedade Page.Columns no componente QuickRep).
• Detalhe [detail]: impressa uma vez para cada registro do dataset
• Cabeçalho de grupo [group header]: quando existe um agrupamento de itens, é impressa uma vez para
cada grupo. Ex.: se um relatório imprime vendas agrupadas por cliente, um cabeçalho de grupo é impresso
para cada cliente, contendo o nome do cliente.
• Rodapé de grupo [group footer]: impressa no final de um grupo de itens.
• Sub-detalhe [subdetail]: usada em relatórios mestre/detalhe, como veremos mais tarde.
• Filha [child]: banda que serve como continuação de outra. Usado apenas pelo componente QRChildBand.

Você pode construir um relatório posicionando componentes QRBand, QRChildBand ou

QRGroup manualmente ou usar a propriedade HasBand do componente QuickRep para criar alguns
tipos de bandas automaticamente.
Os componentes imprimíveis são colocados sobre as bandas em tempo de projeto e são impressos quando o

relatório é impresso. Eles podem conter texto ou imagens fixos (como QRLabel, QRMemo,

QRRichText, QRImage, QRShape), ou conteúdo ligado aos campos do banco de dados (como

QRDBText, QRDBRichText, QRDBImage) ou ainda conteúdo variável, calculado ou

determinado pelo relatório (como QRExpr e QRSysData).


O componente QuickRep é visual e aparece num formulário em tempo de projeto. No entanto, esse
formulário não é mostrado ao usuário, mas apenas usado internamente pelo programa. Para imprimir o
relatório, usa-se o método Print do componente. Para ver uma prévia de impressão numa janela, usa-se o
método Preview.
Usando o QuickReport Wizard
Existem duas formas de criar um relatório no QuickReport. A forma mais rápida é usar o QuickReport
Wizard, que cria um relatório bem simples, com todos os campos de uma tabela. A outra forma é
desenhar o relatório, colocando os componentes imprimíveis e definindo suas propriedades, o que é mais
trabalhoso, mas é necessário com relatórios mais complicados.

Para o primeiro exemplo, vamos criar um relatório usando o QuickReport Wizard, com a relação
de clientes cadastrados.

Agora clique em File|New... O Delphi irá mostrar o repositório de itens para você escolher o que
quer criar. Clique na página "Business", selecione o ícone "QuickReport Wizard" e clique Ok. O
assistente irá perguntar inicialmente se você deseja criar um relatório com os campos de uma tabela,
cliqque no botão "Start Wizard" , em seguida qual o diretório ou alias da tabela e qual o nome da tabela e
os campos que deseja mostrar. Selecione "CursoDelphi" na lista superior e depois "CLIENTE.DB" na
lista inferior, e selecione os campos "Nome" e "Telefone". Em seguida clique no botão "Finish".

Note que existe um componente Table no formulário. Altere o seu nome para 'tblCliente' e a sua
propriedade Active para True. Mude o Name do formulário para "FormRelCliente".
Imprimindo o Relatório

Salve o formulário com RELCLIENTE.PAS. Retorne ao formulário principal (formPrincipal),


clique duas vezes no menu Relatório|Clientes e coloque o seguinte comando;
FormRelCliente.QuickRep1.Preview;
Note que 'FormRelCliente' é o nome do formulário que contém o componente QuickReport e
'QuickRep1' é o nome desse componente (não foi alterado). Preview é um método do componente que
mostra uma janela de prévia de impressão. Caso queira imprimeir utilize o método Print.

Para que os comandos acima funcionem, acrescente uma cláusula uses no início da seção de
implementação da unidade:
uses RelCliente;
Lembre-se de que 'RelCliente' é o nome da outra unidade, que contém o formulário do QuickReport.

Agora execute o programa e teste o funcionamento do relatório que será mostrado no vídeo. Note
que a janela de visualização é modal _ você deve fechá-la para voltar ao formulário.
Nota: a qualquer momento, se você quiser ver uma prévia de impressão, basta clicar com o botão direito
no componente QuickRep (a folha em branco) e no item "Preview".

Analisando o Relatório Gerado

Termine a execução do programa e abra o formulário 'FormRelCliente'. Vamos ver como funciona
esse formulário básico.
Note que ele possui um componente Table no canto. Esse componente está ligado à tabela
CLIENTE.DB, do banco de dados CursoDelphi, como foi informado no QuickReport Wizard, na
propriedade DataBaseName altere seu conteúdo para 'Vendas', pois, em todos os formulários estamos
usando o Alias Local . Em vez de uma tabela, poderia ser um componente Query que faz uma consulta
SQL.
A "folha branca" com linhas horizontais e verticais é um componente QuickRep, que é o local de
desenho do relatório. Nesse componente, a propriedade DataSet faz referência a "tblCliente", a tabela
usada para os dados do relatório.
Sobre esse componente, são colocadas várias bandas. A banda "Column Header", logo abaixo, tem um
componente QRLabel para cada nome de campo, definindo os cabeçalhos das colunas. Se você quiser
alterar o texto de um deles, clique no componente e altere a propriedade Caption.
A banda "Detail", mais abaixo, é impressa repetidas vezes, uma para cada registro. Os componentes que
estão nesta banda são da classe QRExpr e cada um está associado com um campo da tabela. Em tempo
de projeto, o componente mostra o nome do campo entre colchetes. Esta escolha da campo que será
mostrado é feito na propriedade Expression , no próximo exemplo iremos ver essa propriedade com
mais detalhes.
Finalmente, na parte inferior, existe uma banda "Page Footer", impressa no rodapé da página. Clique na
barra de rolagem horizontal, para ver o conteúdo do formulário à direita. Note que mais à direita na
banda, existe um componente que mostra "Page". Esse é um componente QRSysData, cuja propriedade
Data contém o valor 'qrsPageNumber', que o faz mostrar o número de página automaticamente.

Salve o projeto novamente.


Criando um Relatório com Várias Tabelas
Agora que já usamos o QuickReport Wizard, veremos como criar um relatório posicionando os
componentes manualmente.Iremos criar um relatório de vendas de produtos, por cliente, que envolve
dados de várias tabelas: CLIENTE, VENDA, ITEMVEND, PRODUTO. Inicialmente iremos colocar
somente a tabela de CLIENTE,depois que estiver mostrando os Clientes, iremos acrescentar as vendas e
os itens vendidos.

Abra o projeto VENDAS.DPR. Nesse projeto, vamos utilizar o QuickReport para criar um relatório

de clientes. Crie um novo formulário, com File|New Form ou o botão . Altere o nome do formulário
para 'FormRelVenda'. Agora coloque um componente Table no formulário e altere suas propriedades para
acessar a tabela de clientes:
Name: tblCliente
DatabaseName: CursoDelphi
TableName: Cliente

Agora clique na página 'QReport' e selecione o componente QuickRep para colocar no


formulário. Mova-o para o canto superior esquerdo e mude as seguintes propriedades:
Name: qrpRelatorio
DataSet: tblCliente

Agora devemos escolher quais as bandas que serão incluídas no relatório. Clique duas vezes no
sinal de + da propriedade Bands para ver seus sub-itens. Altere HasPageHeader, HasPageFooter e
HasDetail para True. À medida que você altera os valores, novas bandas irão aparecer no relatório. O
formulário deverá ter a seguinte aparência:
Nota: ou, se preferir, copie o componente Table do formulário de clientes [Ctrl+C] e cole [Ctrl+V] neste
formulário.
Nota: basta clicar duas vezes em DataSet para aparecer o valor.

Aumente a largura do formulário para que ele ocupe a tela inteira, para melhor visualização. Note
que você pode selecionar uma banda clicando nela com o o mouse. Você pode aumentar a altura da banda,
mas a largura é determinada automaticamente baseado no tamanho de página. As medidas que aparecem
no relatório são dadas em centímetros, como padrão.

Vamos colocar um título do relatório, coloque um componente QRLabel na banda "Page


Header". Altere o Caption para "Vendas por Cliente" e também as seguintes propriedades:
Font.Size: 20, para aumentar o texto
Alignment: taCenter, para centralizar o texto
AlignToBand: True, para centralizar o componente em relação à banda.
Quando você altera AlignToBand, a posição do componente se altera. Se você mudar apenas Alignment,
apenas a posição do texto em relação ao componente se altera.

Agora na banda "Details", iremos mostrar somente o nome do cliente(essa seção será impressa uma

vez para cada cliente). Coloque um componente QRLabel e altere o Caption para "Nome" e, logo

à frente dele, um componenteQRDBText( ), com as propriedades DataSet ,selecione "tblCliente" e


DataField, selecione o nome do campo, "Nome".
Nota: para chamar o Object Inspector, se ele não estiver visível, use a tecla [F11] ou [Enter].
Inserindo um Número de Página
Você pode inserir, em qualquer posição do relatório, um número de página que será incrementado
automaticamente. Vamos colocar um na banda "Page Footer".

Primeiro coloque um componente QRLabel e altere o seu Caption com "Página:". Depois selecione

o componente QRSysData e coloque à direita do rótulo. Altere a propriedade Data, selecionando


'qrsPageNumber'.

Vamos colocar também outro componente QRSysData para exibir a data e hora de impressão do
relatório. Coloque-o na banda "Page Footer" e altere as seguintes propriedades:
Data: qrsDateTime, para mostrar data e hora
AutoSize: True, para que o componente aumente de acordo com o conteúdo dele.
Alignment: taRightJustify, para alinhar o texto à direita do componente
AlignToBand: True, para alinhar o componente à direita da banda

Visualizando o Relatório

Para visualizar o relatório como ele vai estar quando impresso, altere a propriedade Active do
'tblCliente' para True, clique no componente QuickRep (fora das bandas) com o botão direito e clique na
opção Preview do menu. Isso vai mostrar, como antes, a janela de prévia de impressão. Essa janela contém
vários botões de controle. Os três primeiros permitem

escolher a quantidade de zoom: 100 % ou largura da página. Quando o relatório tem várias

páginas, podem ser usados botões para navegar entre as páginas: primeira, anterior,

próxima e última. Você pode imprimir o relatório clicando no botão ou configurar as opções de

impressão com o botão . O botão permite salvar o relatório num arquivo e permite abrir esse
arquivo novamente para visualizar. E o botão Close fecha a janela de visualização.

Desative a tabela (altere Active para False) porque ela será ativada apenas durante a execução do
programa.

Salve o projeto. Quando o Delphi pedir o nome da unidade do novo formulário, informe "RVenda".

Acrescentando Várias Tabelas ao Relatório

Coloque três componentes Table no formulário. Para todos eles, altere DatabaseName,
selecionando "Vendas". Defina as suas propriedades da seguinte forma:
Name TableName
VENDA.DB
tblVenda
ITEM.DB
tblItem
PRODUTO.DB
tblProduto

Agora coloque três componentes DataSource no formulário e conecte-os aos componentes Table
correspondentes:
Name DataSet
tblCliente
dsCliente
tblVenda
dsVenda
tblItem
dsItem

Conectando as Tabelas

Faremos ligações mestre-detalhe para relacionar as tabelas. Primeiro vamos conectar 'Venda' a
'Cliente' (um cliente tem N vendas). Clique em 'tblVenda' e altere as propriedades: MasterSource:
dsCliente, MasterFields: CodCliente, IndexName: CodCliente.

Agora vamos conectar 'ITEM' e 'VENDA'. Clique em 'tblItem' e altere: MasterSource: dsVenda,
MasterFields: CodVenda, IndexName: IndVenda.

Finalmente, para buscar o nome do produto a partir da tabela ITEM, devemos conectar as duas.
Clique em 'tblProduto' e altere: MasterSource: dsItem, MasterFields: CodProduto, IndexFieldNames:
CodProduto.
A aparência do relatório , nesse momento, será semelhante à figura:
Criando Sub-detalhes para Vendas
Quando se tem um relatório com tabelas mestre-detalhe, a banda "detalhe" se relaciona apenas com a
tabela mestra. Para colocar dados das outras tabelas, devemos colocar bandas "sub-detalhes", utilizando o

componente QRSubDetail.

Coloque no formulário um componente QRSubDetail. Ele aparece como uma nova banda, abaixo de
"Detail", mas acima de "Page Footer". Vamos mostrar nesse caso os dados da tabela de vendas. Na
propriedade DataSet, selecione 'tblVenda'. Mude o nome desse componente para 'qrsubVenda'. Isso vai
ajudar a identificá-lo mais tarde, como veremos.
Nesse componente, a propriedade Master define quem é o 'relatório mestre' para ele, que determina
quando ele será impresso. Note que a propriedade Master nesse caso foi definida como 'qrpRelatorio', que
é o nome do componente QuickRep. Deixe-a como está.

Coloque na banda sub-detail um componente QRLabel, com o texto "Código Venda:". Na frente
coloque um componente QRDBText, com DataSet: tblVenda e DataField: CodVenda. Após os dois,
coloque mais um QRLabel, com o texto "Data Venda:" e um QRDBText, com DataSet: tblVenda e
DataField: DataVenda. Destaque os rótulos em negrito. O resultado ficará como o seguinte:

Se quiser, use o Preview para visualizar o relatório (tblVenda deve estar ativo). Note que primeiro é
impressa a seção 'Detail', com os dados do cliente, depois é impressa várias vezes a seção Sub-detail,
contendo as vendas efetuadas. Por exemplo:
Cliente: Primeiro
Código Venda:101 Data Venda: 04/07/97
Código Venda:102 Data Venda: 05/07/97
Código Venda:105 Data Venda: 13/07/97
Cliente: Segundo
Código Venda:107 Data Venda: 20/07/97
Código Venda:108 Data Venda: 30/07/97
Código Venda:110 Data Venda: 30/08/97
Cliente: Terceiro
Código Venda:107 Data Venda: 20/07/97
Código Venda:108 Data Venda: 30/07/97
........

Criando Sub-detalhes para Itens de Venda

Agora, para cada venda, devemos imprimir os itens de venda correspondentes a eles. Para isso,

coloque mais um componente QRSubDetail no relatório, que vai aparecer abaixo da banda de
vendas. Altere as seguintes propriedades:
Name: qrsubItem
DataSet: tblItem
Master: qrsubVenda
A propriedade Master deve ser alterada, para dizer que essa banda sub-detalhe estará subordinada à banda
'qrsubVenda', criada anteriormente ,ou seja, para cada registro de venda impresso, serão impressos vários
registros de itens de venda.
Agora, para essa banda, vamos criar um cabeçalho de grupo e um rodapé de grupo. Um cabeçalho de
grupo é uma banda impressa antes de todos os registros do grupo (no caso, todos os itens de uma
determinada venda) e um rodapé de grupo é impresso após todos os registros do grupo. O cabeçalho será
usado para mostrar os nomes de campos e o rodapé será usado para mostrar um total geral.

Clique na propriedade Bands desse componente e expanda seus sub-itens. Altere os dois,
HasHeader e HasFooter, para True. Isso vai criar as bandas de cabeçalho e rodapé de grupo, acima e
abaixo do 'sub-detail'.

Clique na banda "Group Header". Na propriedade Font, clique no botão de reticências, selecione
"Negrito" e clique Ok. Com isso, todos os componentes colocados nessa banda ficarão em negrito.
Coloque quatro rótulos de texto (QRLabel), alterando o Caption em cada um com os textos "Produto",
"Quantidade", "Preço Unitário" e "Preço Total". Deixe um espaço maior entre "Produto" e "Quantidade".

Agora clique na banda "sub-detail", abaixo do "Group Header" novamente. Vamos mostrar aqui o
nome do produto (campo Nome de tblProduto), a quantidade vendida (campo Quantidade de tblItem) e o
preço de venda (campo PrecoVenda de tblItem). O preço total é um campo calculado, que será colocado
mais tarde. Coloque três componentes QRDBText, debaixo dos rótulos correspondentes e defina suas
propriedades da seguinte forma:
DataSet DataField
tblProdutoNome
tblItem Quantidade
tblItem PrecoVenda
O resultado, visualmente, será o seguinte:

Se desejar, clique com o botão direito no relatório e depois em Preview(Para funcionar todas as
tabelas tem que estar com a propriedade Active = True). Alguns detalhes ainda precisam ser acertados,
como a formatação dos campos.

Formatando os Campos
Para melhorar a aparência do relatório, vamos mostrar os campos "Quantidade" e "PrecoVenda" alinhados
à direita e "PrecoVenda" formatado como um valor monetário.

Clique no componente QRDBText que mostra "[Quantidade]". Altere a propriedade AutoSize para
False. Quando AutoSize é True, ele muda de tamanho de acordo com o conteúdo. Posicione-o alinhado à
direita de acordo com o rótulo e mude a propriedade Alignment para 'taRightJustify'.

Selecione o componente "[PrecoVenda]" e faça o mesmo: AutoSize=False e


Alignment=taRightJustify. Além disso, altere a propriedade Mask, colocando o texto "#,0.00". Essa
propriedade tem o mesmo efeito de DisplayFormat para campos da tabela, como já foi visto.

Agora visualize novamente a prévia de impressão. Note que os campos aparecem formatados
corretamente.

Criando um Campo Calculado

Você pode criar campos calculados no QuickReport, usando o componente QRExpr. Com ele, você
pode digitar uma expressão qualquer, envolvendo campos da tabela. Você não precisa escrever código de
programa para fazer o cálculo. Vamos usar esse componente para calcular o "Preço Total" do item, que é
PrecoVenda * Quantidade.

Coloque no formulário um componente QRExpr logo abaixo do rótulo "Preço Total". Clique na
propriedade Expression, que define a fórmula de cálculo do campo e clique duas vezes no botão de
reticências. Você verá uma janela onde poderá montar uma expressão de cálculo.

Clique no botão "Database Field", na lista "Select dataset", a tabela 'tblItem', que contém os
valores a serem calculados. Os campos da tabela aparecem na lista abaixo do nome. Clique em
'Quantidade', em seguida pressione o botão 'OK'. No quadro de texto na parte superior da janela, aparece
'tblItem.Quantidade'. Agora, clique no botão com o sinal de multiplicação, [*]. Um sinal * será inserido no
texto da fórmula. Para terminar, clique novamente em "Database Field", escolha a tabela 'tblProduto' e o
campo 'PrecoVenda'.
A fórmula final aparecerá como na figura:
A fórmula de cálculo do campo, portanto, é 'tblItem.Quantidade * tblProduto.PrecoVenda'. Agora clique
Ok para retornar ao formulário.

Para visualizar esse componente melhor, altere a propriedade AutoSize para False e reduza a
largura do componente de acordo com o rótulo na parte superior. Altere também as propriedades:
Alignment: taRightJustify e Mask: "#,0.00", como foi feito antes para o preço de venda.
Se você usar o comando Preview novamente, verá que o campo calculado é mostrado corretamente para
cada registro.

Criando um Sub-total

Agora vamos totalizar o campo calculado criado, para mostrar o total de vendas para cada cliente.
Selecione o componente QRExpr, que foi criado para calcular o preço total. Tecle [Ctrl+C] para fazer uma
cópia dele. Clique na banda "Group Footer" e tecle [Ctrl+V] para trazer essa cópia.

Na propriedade Expression, faça uma alteração manual (não clique no botão de reticências, pois
seria mais complicado). Onde está o valor atual:
tblItem.PrecoVenda * tblItem.Quantidade
substitua por:
SUM(tblItem.PrecoVenda * tblItem.Quantidade)
A função SUM calcula o somatório de uma expressão. Mas é preciso também informar qual é o conjunto
de registros sobre os quais essa soma será efetuada. No nosso caso, será para os itens de cada venda.
Para definir o escopo da soma, é usada a propriedade Master do componente. Essa propriedade faz
referência a uma banda que define quando o somatório será atualizado. Quando essa banda for impressa, o
componente atualiza internamente o total, antes de imprimir.

No nosso caso, em Master, selecione 'qrsubItem', que é a banda que contém os itens de venda. Com
isso, a cada item de venda impresso, o componente acumulará internamente o seu total. Altere também a
propriedade ResetAfterPrint para True. Se ela for False, o componente continua acumulando o total até o
fim da impressão. Quando ela é True, o componente zera o seu acumulador depois de impresso. Para dar
mais destaque ao sub-total, coloque-o em negrito.

Agora coloque à esquerda desse componente um QRLabel, com o texto "Total da venda" e em
negrito.

Use o comando Preview para visualizar o relatório. Note agora que o sub-total será impresso após
os itens de venda.

Chamando o Relatório a Partir do Menu


Agora que o relatório está pronto, faremos com que ele seja chamado a partir do menu principal, no item
de menu Relatórios|Vendas. Mas antes vamos criar um procedimento no formulário do relatório para
facilitar a sua chamada.

Na unidade "RVendas", encontre a classe de formulário (TFormRelVendas). Dentro da seção


public, acrescente a seguinte declaração de procedimento:
procedure VisualizarRelatorio;
Agora, no começo da seção implementation, acrescente o corpo do procedimento:
procedure TFormRelVenda.VisualizarRelatorio;
begin
{abrir todas as tabelas usadas}
tblCliente.Open;
tblVenda.Open;
tblItem.Open;
tblProduto.Open;
{visualizar o relatório}
qrpRelatorio.Preview;
{fechar todas as tabelas usadas}
tblCliente.Close;
tblVenda.Close;
tblItem.Close;
tblProduto.Close;
end;

Abra a unidade do formulário principal, VENDPRIN.PAS. Para poder chamar o outro formulário,
clique em File|Use unit... e selecione "RVenda" para acrescentar a unidade na cláusula uses.

No formulário, clique no item de menu Relatórios|Vendas. Coloque os seguinte comando:


FormRelVenda.VisualizarRelatorio;

Execute o programa e clique em Relatórios|Vendas. O programa mostrará a janela de prévia de


impressão, como já foi visto. Salve o projeto novamente.

Você também pode gostar