Escolar Documentos
Profissional Documentos
Cultura Documentos
0 Total
Esta apostila é melhor visualizada com resolução de 800x600 e no mínimo 256 cores.
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:
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.
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
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.
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.
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:
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;
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
Eventos Comuns
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.
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
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.
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.
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 }
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.
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
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
"Button1"
Name btnCalendario
Caption Calendário
"Button2"
Name btnCalculadora
Caption Calculadora
"Button3"
Name btnFechar
Caption Fechar
"Edit"
Name editData
Text
"Label"
Caption Data
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.
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.
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.
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:
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
Compilação de Unidades
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.
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;
...
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
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
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.
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;
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;
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
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';
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 }
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.
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.
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.
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.
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.
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).
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.
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.
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.
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
end.
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.
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.
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.
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);
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
Classes e Objetos
Herança
Variáveis de Objetos
Listas de strings
Objetos Predefinidos
Code Explorer
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.
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.
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:
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.
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
Criando Tabelas
Abra o menu File|Working Directory... e digite o diretório onde serão colocados os arquivos do
curso ("C:\CURSODF\TABELAS"). Clique Ok.
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.
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.
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.
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
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.
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;
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.
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.
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).
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
Volte ao Database Desktop para definir tabelas adicionais e alterar a estrutura da tabela de cliente, criada
anteriormente.
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
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.
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.
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.
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.
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:
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].
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.
Acrescente no Menu Designer o item de menu abaixo Crie a seguinte estrutura de menus:
Relatório menuRelatorios
Clientes menuRelClientes
Vendas por ProdutomenuRelVendasProduto
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):
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.
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.
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".
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;
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".
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
Tratamento de Exceções
Pesquisas na Tabela
Blocos Protegidos
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.
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:
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
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.
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.
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.
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'.
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.
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.
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.
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
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
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.
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
Manipulação de Datas
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
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');
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 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.
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:
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;
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:
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:
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
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
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.
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.
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.
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.
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
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.
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.
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 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.
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".
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.
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:
menu principal. Para isso , retorne ao formPrincipal. Você pode usar o botão .
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.
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;
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
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.
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;
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).
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).
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.
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.
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.
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.
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).
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.
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')
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
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=
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").
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.
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.
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.
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.
Conceitos do QuickReport
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.
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.
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
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
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
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".
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.
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 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.
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
Primeiro coloque um componente QRLabel e altere o seu Caption com "Página:". Depois selecione
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".
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
........
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'.
Agora visualize novamente a prévia de impressão. Note que os campos aparecem formatados
corretamente.
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.
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.