Docsity
Docsity

Prepare-se para as provas
Prepare-se para as provas

Estude fácil! Tem muito documento disponível na Docsity


Ganhe pontos para baixar
Ganhe pontos para baixar

Ganhe pontos ajudando outros esrudantes ou compre um plano Premium


Guias e Dicas
Guias e Dicas

programação em delph, Notas de estudo de Informática

programação en delph

Tipologia: Notas de estudo

Antes de 2010

Compartilhado em 03/06/2009

jailson-silva-12
jailson-silva-12 🇧🇷

5

(1)

3 documentos

Pré-visualização parcial do texto

Baixe programação em delph e outras Notas de estudo em PDF para Informática, somente na Docsity! MAMMA À postilandoecom O maior Site de apostilas da Internet Delphi Enviado por: Aryanne da Costa 2 BORLAND DELPHI® ESCLARECIMENTOS Parte desta apostila foi criada utilizando-se partes de apostilas encontradas na Internet. Qualquer problema, por favor, contatar o professor para correção. Esta apostila foi elaborada com o propósito de servir de apoio ao curso e não pretende ser uma referência completa sobre o assunto. Para aprofundar conhecimentos, sugerimos consultar as seguintes obras: 1. Cantu, Marco, "Dominando o Delphi 7 – A Bíblia", São Paulo, Makron Books, 2003 2. Cortes, Pedro Luiz, “Trabalhando com Banco de Dados Utilizando o Delphi”, Érica, 2005 O curso parte do pressuposto que a pessoa é iniciante em Delphi, mas que conhece Lógica de Programação e possui conceitos básicos de POO. Eduardo Rosalém Marcelino – ermarc@itelefonica.com.br 5 EXCEÇÕES SILENCIOSAS ..................................................................................................................................... 41 MANIPULANDO EXCEÇÕES GLOBAIS..................................................................................................................... 41 ENTENDENDO UM POUCO SOBRE O QUE É UM GPF .............................................................................................. 42 CONFIGURANDO O DELPHI PARA NÃO “PARAR” EM EXCEÇÕES ............................................................................. 42 CONCLUSÃO: ...................................................................................................................................................... 42 ARQUIVOS INI......................................................................................................................................................... 43 CRIANDO UMA BIBLIOTECA DE PROCEDURES, FUNÇÕES, ETC. ................................................................................. 45 TRABALHANDO COM FORMULÁRIOS ........................................................................................................................ 47 PRINCIPAIS TECNOLOGIAS DE ACESSO A DADOS ..................................................................................................... 49 BDE (BORLAND DATABASE ENGINE): ................................................................................................................... 49 DBEXPRESS (DBX): .............................................................................................................................................. 49 ADO (ACTIVEX DATA OBJECTS): .......................................................................................................................... 49 "INTERBASE EXPRESS">INTERBASE EXPRESS (IBX):............................................................................................. 50 CONFIGURANDO UMA CONEXÃO COM BD USANDO ADO NO BORLAND DELPHI 2005 ................................................. 51 CRIANDO UM CADASTRO SIMPLES (APENAS 1 TABELA) ............................................................................................ 54 VALIDANDO OS DADOS DO CADASTRO .................................................................................................................... 56 CRIANDO UM CADASTRO COM MAIS DE UMA TABELA (FK) ....................................................................................... 57 SUGERINDO UM CÓDIGO AUTOMATICAMENTE ......................................................................................................... 58 SOLICITANDO UM DADO AO USUÁRIO (INPUTBOX).................................................................................................. 59 CADASTROS SEM O TDBNAVIGATOR ........................................................................................................................ 60 UMA TELA CRIADA SEM O TDBNAVIGATOR .............................................................................................................. 61 CONSTRUINDO UMA TELA DE CONSULTA................................................................................................................. 63 PASSANDO PARÂMETROS PARA O OBJETO ADOQUERY ......................................................................................... 64 DATAMODULE ......................................................................................................................................................... 64 CONTINUANDO COM A CONSTRUÇÃO DA TELA DE CONSULTA .................................................................................. 65 O MÉTODO LOCATE DA CLASSE TDATASET .......................................................................................................... 65 VERIFICANDO SE UM REGISTRO EXISTE EM UMA TABELA......................................................................................... 67 EXIBINDO OS ERROS EM UMA TELA SEPARADA ........................................................................................................ 68 TRABALHANDO COM VETORES DINÂMICOS.............................................................................................................. 70 TRANSAÇÕES .......................................................................................................................................................... 73 FILTRANDO DADOS DE UMA TABELA........................................................................................................................ 76 FRAMES .................................................................................................................................................................. 77 OBJETO TFIELD....................................................................................................................................................... 81 ACESSANDO OS DADOS DE UM TFIELD: ............................................................................................................... 83 FIELDS EDITOR....................................................................................................................................................... 84 VALIDANDO ATRAVÉS DO EVENTO ONVALIDADE DOS OBJETOS TFIELD.................................................................... 85 CAMPOS CALCULADOS............................................................................................................................................. 86 CAMPOS LOOKUP .................................................................................................................................................... 88 O EVENTO ONGETTEXT ........................................................................................................................................... 90 O EVENTO ONSETTEXT............................................................................................................................................ 91 TABELAS TEMPORÁRIAS COM O TCLIENTDATASET ................................................................................................... 91 FORMULÁRIOS PADRÕES......................................................................................................................................... 93 CRIANDO UM CADASTRO MASTER - DETAIL ............................................................................................................. 95 APÊNDICE - FUNÇÕES PARA TRABALHAR COM STRINGS (RETIRADAS DO HELP DO DELPHI 7) ..................................106 APÊNDICE - OBJECT PASCAL: ESTRUTURAS BÁSICAS...............................................................................................109 APÊNDICE - RELAÇÕES ENTRE CLASSES..................................................................................................................117 APÊNDICE - DESCRIÇÃO DOS PRINCIPAIS PROCEDIMENTOS E FUNÇÕES PRÉ DEFINIDAS.........................................118 APÊNDICE - TECNOLOGIAS APLICÁVEIS AO DELPHI 7..............................................................................................139 6 UM POUCO SOBRE DELPHI E PROGRAMAÇÃO PARA WINDOWS No início, programar em Windows era algo extremamente complicado e acessível apenas a programadores dispostos a investir muito tempo e fosfatos na leitura de pilhas de livros, intermináveis testes e análise de programas exemplos que mais confundem do que explicam. Mas porque era tão difícil fazer programas para Windows? Para começar, o Windows usa o conceito de GUI (Graphic User Interface – Interface Gráfica com o Usuário), que embora fosse muito familiar para usuários do Unix e do Mac OS, era novidade para usuários do DOS. O uso de um sistema GUI implicava em aprender vários conceitos que eram estranhos ao usuário de um sistema baseado em texto como o DOS. Para complicar um pouco mais, o Windows é um sistema multi-tarefa, e as aplicações são orientadas a eventos, o que implica em aprender um novo estilo de programação. Finalmente, o programador tinha que ter alguma familiaridade com as centenas de funções oferecidas pela API do Windows. Por tudo isso, programação em Windows era um assunto que costuma provocar arrepios nos programadores. Felizmente as linguagens visuais chegaram para mudar esta situação. Foi só com elas que o Windows conseguiu cumprir sua promessa de ser um sistema amigável e fácil de usar também para os programadores, que sempre tiveram que pagar a conta da facilidade de uso para o usuário. Entre as linguagens visuais que surgiram, nenhuma veio tão completa e bem acabada quanto o Delphi. Desde o início ele possuía um compilador capaz de gerar código diretamente executável pelo Windows, proporcionando uma velocidade de execução de 5 a 20 vezes maior que as linguagens interpretadas como o Visual Basic e Visual FoxPro que geravam executáveis Pcode que precisam de arquivos auxiliares de run-time. Além disso, o Delphi também possuía uma engine para acesso a diversos bancos de dados e um gerador de relatórios. O tempo de desenvolvimento de qualquer sistema foi reduzido a uma fração do tempo que seria necessário usando outras linguagens e o resultado é sempre muito melhor. É por isso que o Delphi fez e faz tanto sucesso no mundo inteiro, sempre ganhando prêmios como melhor ferramenta de desenvolvimento para Windows. O objetivo principal de qualquer ferramenta de desenvolvimento ou linguagem de programação é a criação de aplicações. Determinadas linguagens ou ferramentas devido aos recursos que possuem são mais indicadas para a criação de aplicações comerciais, outras se destinam mais a aplicações científicas ou ainda para a criação de sistemas operacionais. O Delphi é uma ferramenta RAD (Rapid Application Development – Desenvolvimento Rápido de Aplicações) criada pela Borland. É uma ferramenta de propósito geral, permitindo o desenvolvimento de aplicações tanto científicas como comerciais com a mesma facilidade e alto desempenho. Integra-se facilmente com a API (Application Program Interface) do Windows, permitindo a criação de programas que explorem ao máximo os seus recursos, assim como os programas escritos em linguagem C/C++. 7 Possui um compilador extremamente rápido, que gera executáveis nativos (em código de máquina, não interpretado), obtendo assim melhor performance e total proteção do código fonte. O Delphi é extensível, sua IDE (Integrated Development Environment – Ambiente de Desenvolvimento Integrado) pode ser ampliada e personalizada com a adição de componentes e ferramentas criadas utilizando-se o Object Pascal, a linguagem de programação do Delphi. Neste ambiente constroem-se as janelas das aplicações de maneira visual, ou seja, arrastando e soltando componentes que irão compor a interface com o usuário. O Object Pascal é uma poderosa linguagem Orientada a Objeto, que além de possuir as características tradicionais das mesmas como classes e objetos, também possui interfaces (semelhantes às encontradas em COM e Java), tratamento de exceção, programação multithreaded e algumas características não encontradas nem mesmo em C++, como RTTI (Runtime Type Information). Assim como o C++, o Object Pascal é uma linguagem híbrida, pois além da orientação a objeto possui também uma parte da antiga linguagem estruturada (Pascal) Devido ao projeto inicial da arquitetura interna do Delphi e da orientação a objeto, suas características básicas mantêm-se as mesmas desde o seu lançamento em 1995 (ainda para o Windows 3.1, pois o Windows 95 ainda não havia sido lançado), o que demonstra um profundo respeito com o desenvolvedor. Isto permite que uma aplicação seja facilmente portada de uma versão anterior para uma nova, simplesmente recompilando-se o código fonte. Obs: Embora as características, teorias e exemplos abordados aqui sejam sobre o Delphi 2005 (última versão disponível), tudo pode ser aplicado em versões anteriores do Delphi, excetuando-se o caso da utilização de componentes e ferramentas introduzidos apenas nesta versão. PRINCÍPIOS DA PROGRAMAÇÃO PARA WINDOWS Antes de começar a trabalhar com o Delphi, é importante ter algumas noções do que está envolvido na programação Windows e no Delphi em particular. Algumas coisas tornam a tarefa de programação no Windows (e ambientes baseados em eventos e interface gráfica) bem diferente de outros ambientes e das técnicas de programação estruturada normalmente ensinadas nos cursos de lógica de programação: Independência do Hardware: No Windows, o acesso aos dispositivos de hardware é feito com intermédio de drivers fornecidos pelo fabricante do hardware, o que evita que o programador tenha que se preocupar com detalhes específicos do hardware. Como acontecia com a programação em DOS. Configuração Padrão: O Windows armazena centralmente as configurações de formato de números, moeda, datas e horas, além da configuração de cores, livrando o programador de se preocupar com esses detalhes específicos. 10 assim, não permite acesso a dBase, Paradox ou Access para estes você deverá continuar a utilizar o BDE. BDE: componentes de acesso a dados utilizando a BDE (até o Delphi 5 faziam parte da guia Data Access). A BDE é a engine que acompanha o Delphi desde sua primeira versão. Muito completa, permite acessar desde bases de dados desktop, como Paradox, dBase ou Access, até bases de dados SGDB, como Interbase, DB/2, Oracle, Informix, SyBase ou MS-SQL Server, todos em modo nativo. Permite ainda o acesso a outros bancos de dados através de ODBC. DBGO: componentes de acesso a dados da interface dbGo (introduzida no Delphi 5 como o nome de ADO Express) através da tecnologia ADO (ActiveX Data Objects), da Microsoft. Tanto a ADO como o OLE DB (drivers de acesso) estão incluídos no MDAC (Microsoft Data Access Components) e permitem acesso a uma série de bancos de dados e à ODBC. Sua principal vantagem é estar incorporada as versões mais recentes do Windows (2000/XP e ME) não sendo necessário nenhuma instalação de engine de acesso. Também é escalável, permitindo acesso desde bases de dados desktop até aplicações multicamadas. A desvantagem é que não é portável para outras plataformas, caso queira portar seu sistema para Linux, terá que trocar todos os componentes de acesso a dados. Interbase: componentes para acesso nativo ao Interbase, através de sua API, constituindo o método de acesso mais rápido e eficiente para este banco de dados. Por não ser uma interface genérica permite utilizar todos os recursos que o Interbase disponibiliza. A desvantagem, no entanto é que ao utiliza-los perde-se a possibilidade de alterar o banco de dados sem mudar o programa, visto que os mesmos se destinam apenas ao Interbase. WebServices: componentes que formam a BizSnap, uma plataforma RAD para desenvolvimento de Web services, que simplifica a integração B2B criando conexões e Web services baseados em XML/SOAP InternetExpress: componentes para manipulação de XML e produção de páginas para internet. 11 Internet: componentes para manipulação de Sockets, páginas e web browser. DataSnap: componentes que permitem a criação de middleware de alto desempenho capazes de trabalhar com Web services, possibilitando fácil conexão de qualquer serviço ou aplicação de cliente com os principais bancos de dados, como Oracle, MS-SQL Server, Informix, IBM, DB2, Sybase e InterBase, através de Serviços Web padrão da indústria e XML, DCOM ou CORBA. (No Delphi 5 esta guia era chamada de Midas). WebSnap: componentes para o desenvolvimento de aplicações Web que suporta os principais Servidores de Aplicações Web, inclusive Apache, Netscape e Microsoft Internet Information Services (IIS);. FastNet: componentes para manipulação de protocolos e serviços da internet como http, nntp, ftp, pop3 e smtp entre outros. (São mantidos por compatibilidade com o Delphi 5, nesta versão foram inseridos os componentes Indy com maior funcionalidade). Decision Cube: componentes para tomada de decisão através da análise multidimensional de dados, com capacidades de tabulação cruzada, criação de tabelas e gráficos. Estes componentes permitem a obtenção de resultados como os obtidos por ferramentas OLAP (On-Line Analytical Processing – Processamento Analítico On-Line) utilizados para análise de Data Warehouses. Qreport: QuickReport é um gerador de relatórios que acompanha o Delphi e se integra totalmente ao mesmo, sem a necessidade de run-time ou ferramentas externas como o Cristal Report, etc. Estes componentes permitem a criação de diversos tipos de relatórios de forma visual e também a criação de preview personalizado. Dialogs: O Windows tem caixas de diálogo comuns, como veremos, que facilitam mostrar uma interface padrão dentro do seu programa para as tarefas comuns, como abrir e salvar arquivos, impressão, configuração de cores e fontes etc. Esta guia tem componentes que permitem utilizar essas caixas de diálogo comuns. 12 Win 3.1: esta guia contém controles considerados obsoletos, que estão disponíveis apenas para compatibilidade com programas antigos. Não crie programas novos que utilizem esses controles. Samples: contém exemplos de componentes para que você possa estudá-los e aprender a criar seus próprios componentes. O código fonte desses exemplos está no subdiretório SOURCE\SAMPLES do diretório de instalação do Delphi. ActiveX: um componente ActiveX é um tipo de componente que pode ser criado em outra linguagem (como C++) e utilizado no Delphi. Esta página contém alguns exemplos de componentes ActiveX prontos para utilizar, que têm funções de gráficos, planilha, etc. O Delphi também pode criar componentes ActiveX, que podem ser utilizado em ambientes como Visual Basic e Visual FoxPro. COM+: catálogo de objetos COM (Component Object Model), tecnologia desenvolvida pela Microsoft que possibilita a comunicação entre clientes e aplicações servidores. Uma interface COM é a maneira como um objeto expõe sua funcionalidade ao meio externo. Indy Clients: componentes para criação de aplicativos clientes para protocolos HTTP, TCP, FTP, DNS Resolver, POP3, SMTP, TELNET, entre outros. Indy Servers: componentes para criação de aplicativos servidores de HTTP, TCP, FTP, TELNET, GOPHER, IRC, entre outros. Indy Misc: componentes complementares aos das guias Indy Clients e Indy Servers, para criação de aplicativos clientes e servidores com acesso a internet, como clientes de ftp, irc e browsers. 15 EVOLUÇÃO DAS LINGUAGENS PARA OO A Orientação a Objetos em computação tem 20 anos apesar de ter surgido fortemente apenas nos últimos 7 anos. Surgiu mesmo na área acadêmica.  1967 - Simula (Noruega)  1980 - Small Talk (Xerox) com objetivos comerciais e acadêmicos, Adele Goldberg (autora)  1980's Objective C(Cox), C++ (Stroustrup), Eifell (Meyer)  Anos 70 - Época da não estruturada, dados e códigos emaranhados.  Anos 80 - Época da estruturada, dados separados de códigos (modulares)  Anos 90 - Época da OO, dados e códigos organizados em objetos. A partir dos anos 80, as principais linguagens incluíram conceitos de OO. Ex. Pascal, C, Lisp, Cobol, depois evoluiram com a inclusão de classes. C++ foi um marco para aplicações em todos os níveis. Surge o Visual Object Oriented Cobol ( c/ win95) e o Visual Basic. As linguagens mais utilizadas no mundo: 1 - Cobol, 2 – Visual Basic (informação do Richard Soley e do Jon Siegel da OMG, em dezembro 96). BENEFÍCIOS DA OOP Listaremos a seguir os principais ganhos, para o desenvolvedor, caso este troque a Metodologia Tradicional pela Orientada ao Objeto. Exatidão- Devido à característica do desenvolvimento estruturado, onde se elabora um projeto e DEPOIS se faz os programas, podemos ter no final um sistema que não atenda perfeitamente seus objetivos depois de implementado. No desenvolvimento OOP, devido ao fato deste ser feito de maneira quase que interativa com o usuário, este risco é significativamente diminuído. A pouca quantidade de código programável também reduz os problemas inerentes as mudanças das especificações durante o desenvolvimento do projeto. Potencialidade- Definimos potencialidade a forma como o programa reage aos erros imprevistos como uma falha na impressora, ou a um disco cheio. Tanto maior for a potencialidade, maior a capacidade do programa em causar o menor estrago possível aos dados e evitar uma saída drástica do sistema. Extensibilidade - Dizemos que quanto maior for a extensibilidade do software, maior será sua capacidade em adequar-se as especificações definidas pelos analistas. Reutilização - A capacidade de se otimizar a produtividade do programador depende diretamente da maneira como o software disponibiliza a reutilização do código gerado. De fato, a maioria dos programadores profissionais, já reutiliza código anteriormente gerado, porém a perfeita reutilização consiste na utilização COMPLETA de um código gerado para algum sistema SEM qualquer outra adaptação prévia. LINGUAGENS PROCEDURAIS X ORIENTADA A OBJETOS : Mesmo as linguagens procedurais oferecem alguma característica funcional OO. Seu conjunto de primitivas porém permite que você digite comandos para que o computador execute. A organização 16 e manipulação dos dados vêm depois. A linguagem OO é projetada para permitir a definição dos objetos que compõe os programas e as propriedades que estes objetos contem. O código é secundário. Pode-se programar na forma procedural com estilo OO quando o programa tem um nível tal de sofisticação que você começa a criar seus próprios tipos e estruturas de dados. A linguagem OO pura tem como um parâmetro de referência que todos os dados sejam representados na forma de objetos. A única pura é a Small Talk (tudo via classe, não tem var. global). Eiffel também. ALGUNS EXEMPLOS DE LINGUAGENS PROCEDURAIS COM EXTENSÕES OO: C++, PASCAL do ambiente DELPHI, PERL, OBJECTIVE C O JAVA implementa tipos simples de dados (integer, real, char) do C, não sendo objetos mas tudo o mais lida com objetos. O código e os dados residem em classes e objetos. LINGUAGENS COMPILADAS X INTERPRETADAS : Linguagem Compilada - código gerado tem alta performance pois é ajustado para um tipo específico de processador. Código Objeto em binário para aquele processador. Ex. Pascal (entenda- se também no Delphi). Linguagem Interpretada - só existe em código fonte. Durante a execução o interpretador pega o fonte e vai executando as ações. Facilita o uso em múltiplas plataformas. Ex. Clipper Um caso de linguagem Compilada/Interpretada é o Java. O Java edita o fonte e salva como código fonte, depois compila o fonte produzindo arquivo binário chamado arquivo de classe que não é executável direto. Este código intermediário chama-se bytecode que são instruções de máquina mas para um processador virtual (o JVM Java Virtual Machine). Por fim o interpretador Java implementa em software executando os arquivos de classe. A OO tem a filosofia de implementar uma abstração do mundo real facilitando a reutilização de código, denominado de caixas pretas de softwares ou ainda softwares IC's. (Integrated Circuits) que estã sendo utilizado aqui como uma analogia de chip com objeto. EVENTOS, MÉTODOS E PROPRIEDADES EVENTOS Os programas feitos em Delphi são orientados a eventos. Eventos são ações normalmente geradas pelo usuário e que podem ser reconhecidas e tradadas pelo programa. Ex.: Clicar o mouse pressionar uma tecla, mover o mouse etc. Os eventos podem ser também gerados pelo windows. Existem eventos associados ao formulário e cada componente inserido neste. Exemplos: - Ao formulário está ligado on show, que ocorre quando mostramos o formulário na tela. - Ao componente botão está ligado o evento on click, que ocorre quando damos um click com o mouse sobre o botão. 17 Eventos comuns ao formulário e aos componentes. Alguns eventos ligados tanto ao formulário quanto aos componentes estão listados a seguir. • OnClick: ocorre quando o usuário clica o objeto. • OndblClick: ocorre quando o usuário da um duplo clique. • OnKeyDown: ocorre quando o usuário pressiona uma tecla enquanto o objeto tem foco. • OnKeyUp: ocorre quando o usuário solta uma tecla enquanto o objeto tem o foco. • OnKeyPress: ocorre quando usuário da um clique numa tecla ANSI. • OnMouseDown: ocorre quando o usuário pressiona o botão do mouse. • OnMouseUp: ocorre quando o usuário solta o botão do mouse. • OnMouseMove: ocorre quando o usuário move o ponteiro do mouse. Rotinas que Respondem a Eventos Cada evento gera uma procedure, aonde você deve inserir as linhas de código que envolvem este evento. Por exemplo, o evento OnClick, que é gerado ao clicarmos em um botão chamado BTNSair, cria a procedure: Procedure TForm1.BTNSairClick(Sender: Tobject); onde Tform1 é o objeto Tform que contém o botão BTNSair, e Sender é um objeto Tobject que representa o componente que deu origem ao evento. Se você quiser inserir uma rotina que trate um determinado evento de um componente, faça o seguinte: • clique sobre o componente; • no Object Inspector, seleciona a página Events; • dê um duplo clique sobre o evento para o qual quer inserir o código; • entre no editor de código e escreva as linhas de código. Exemplo: Procedure TForm1.BTNSairClick(Sender: Tobject); begin Form1.Close; end; Obs.: Escreva seu código entre o begin e o end, se por acaso você quiser retirar o evento e o componente, retire primeiro os eventos do componente removendo somente o código que você colocou e depois o componente; os resto dos procedimentos o DELPHI tira para você. PROPRIEDADES Uma propriedade representa um atributo de um objeto. No Dellphi todas as coisas que aparecem no Object Inspector são propriedades, inclusive os eventos, porém sua referência é a um método. Como vimos, eventos podem estar associados a modificações em propriedade de componente e formulário, ou seja, você pode modificar propriedades de formulários e componentes durante a execução do sistema. Para isto você deverá usar a sintaxe: <componente>.<propriedade>; 20 Div Initialization Program Uses Do Inline Property Var Downto Interface Raise While Else Is Record With End Label Repeat Xor Except VARIÁVEIS Nossos dados são armazenados na memória do computador. Para que nós não tenhamos que nos referir a estes dados de forma direta, através de um endereço numérico difícil de memorizar, o compilador nos permite utilizar variáveis com esta finalidade. Escolhendo nomes sugestivos (mnemônicos) para nossas variáveis (tais como nome, funcao, idade, salario) facilitamos bastante a compreensão de nosso código. Para que o Delphi possa usar nossas variáveis, devemos primeiro declará-las, isto é, informar o nome e o tipo desejados. Por exemplo : o comando a seguir declara idade como sendo uma variável do tipo inteiro (integer) : idade : integer; As variáveis inteiras podem assumir valores entre -32768 e +32767. Elas ocupam 2 bytes na memória. Asim sendo, a declaração acima faz com que o Delphi reserve 2 bytes para a nossa variável idade. Note que a declaração do tipo de uma variável, em princípio não lhe atribui valores. Um erro comum em programação é tentarmos ler valores de variáveis não inicializadas, ou às quais ainda não se atribuiu valores... Damos a seguir uma lista dos tipos de variáveis mais comuns do Object Pascal com suas faixas de valores e o espaço ocupado em memória: BOOLEAN - Tipo lógico que pode assumir somente os valores TRUE ou FALSE e ocupa 1 byte de memória. BYTE - Tipo numérico inteiro, pode assumir valores numa faixa de 0 a 255, ocupa 1 byte. CHAR - Tipo alfa-numérico, pode armazenar um caractere ASCII, ocupa 1 byte. COMP - Tipo numérico real, pode assumir valores na faixa de -9.2.10-18 a 9.2.10+18 , ocupa 8 bytes, pode ter entre 19 e 20 algarismos significativos. EXTENDED - Tipo numérico real, pode assumir valores na faixa de -3,4.10-4932 a +1,1.10+4932, ocupa 10 bytes de memória e tem entre 19 e 20 algarismos significativos. INTEGER - Tipo numérico inteiro, pode assumir valores numa faixa de -32768 a +32767, ocupa 2 byte de memória. LONGINT - Tipo numérico inteiro, pode assumir valores numa faixa de -2147483648 a +2147483647, ocupa 4 bytes de memória. REAL - Tipo numérico real, pode assumir valores na faixa de -2,9.10-39 a +1,7.10+38, ocupa 6 bytes de memória e tem entre 11 e 12 algarismos significativos. CURRENCY - Tipo numérico Real utilizado para armazenar valores monetários. SHORTINT - Tipo numérico inteiro, pode assumir valores numa faixa de -128 a +127, ocupa 1byte de memória. SINGLE - Tipo numérico real, pode assumir valores numa faixa de -1,5.10-45 a +3,4.10+38, ocupa 4 bytes de memória, e tem de 7 a 8 algarismos significativos. WORD -Tipo numérico inteiro, pode assumir valores numa faixa de 0 a 65535, ocupa 2bytes de memória. STRING - Tipo alfanumérico, possuindo como conteúdo uma cadeia de caracteres. O número de bytes ocupados na memória varia de 2 a 256, dependendo da quantidade máxima de caracteres definidos para a string. O primeiro byte contém a quantidade rela de caracteres da cadeia. Os nomes de variáveis devem começar com uma letra ou o caractere sublinhado (_) seguido por uma sequência de letras, dígitos ou caractere sublinhado (_) e não podem conter espaço em branco nem quaisquer tipos de acentos. Os nomes de variáveis podem ter qualquer tamanho mas somente os 63 primeiros caracteres serão considerados. Exemplos : Para definir uma variável Nome do tipo string e uma variável Salario do tipo double, podemos inserir as seguintes linhas de código na cláusula var da unidade de código correspondente. 21 Nome : string; Salario : double; Pode-se declarar mais de uma variável do mesmo tipo na mesma linha, separando-as por vírgula. nome, funcao, endereco : string; ARRAYS (VETORES) Arrays são conjuntos de variáveis com o mesmo nome e diferenciadas entre si por um índice. Eles são úteis para manipularmos grandes quantidades de dados de um mesmo tipo pois evitam a declaração de diversas variáveis. Considere o caso de um programa de Folha de Pagamento que precise armazenar os seguintes dados referentes a 100 funcionários : nome, funcao, salário, etc... Seríamos obrigados a declarar 100 varíáveis nome, 100 variáveis funcao, etc... O array nos permite declarar uma única variável com um índice para apontar para as diferentes ocorrências. Declara-se um array da seguinte forma : nome_da_variável : array[i1..i2] of tipo_de_variável; onde i1 e i2 repreentam os valores mínimo e máximo, respectivamente, do índice. O Object Pascal permite que i1 e i2 possuam qualquer valor desde que i1 seja menor ou igual a i2. Assim, poderiamos declarar um array de 100 variáveis inteira idade de várias formas diferentes : idade : array [1..100] of integer; ou idade : array [-100..-1] of integer; ou idade : array [0..99] of integer, etc... Pode-se definir arrays multidimensionais (com vários índices) como, por exemplo : espaco3d:array[1..10,-5..20,0..30] of double; que pode armazenar 10x26x31=8060 variáveis do tipo double. Um dos casos mais comuns é a matriz com m linhas e n colunas : matriz : array[1..m,1..n] of qqer_tipo. Os elementos dos arrays podem ser quaisquer tipos de variáveis ou objetos. RECORDS (REGISTROS) O Object Pascal permite definir tipos compostos de variáveis denominados registros. Define-se da seguinte forma : nome_do_tipo : Record variavel1 : primeiro_tipo; variavel2 : segundo_tipo; ....... variaveln : n-ézimo-tipo; end; variavel1,variavel2.. variaveln são chamadas de campos do registro. 22 Declaramos uma variável deste tipo da mesma forma que procedemos para declarar variáveis de qualquer tipo pré-definido variavel : nome_do_tipo; Usamos a notação de ponto para acessar um campo de uma variável composta : nome_da_variável.nome_do_campo; Exemplo : funcionario = Record nome : string; funcao : string; salario : double: end; Assim, no exemplo citado anteriormente, ao invés de declararmos um array nome de 100 elementos, um array funcao de 100 elementos, um array salario de 100 elementos, podemos declarar uma única variável chamada empregado, por exemplo, como sendo um array de 100 elementos do tipo funcionário. empregado : array[1..100] of funcionario; Para obter os dados do décimo funcionário, basta fazer : empregado[10].nome; empregado[10].funcao; empregado[10].salario; PRINCIPAIS TECLAS DE ATALHO DA IDE DO DELPHI F9  Executa F8  Executa passo a passo sem entrar em sub-rotinas F7  Executa passo a passo entrando em sub-rotinas CTRL+F5  Adiciona uma variável aos Watch List para ser monitorada durante o Debug CTRL+F9  Compila CTRL + SHIFT + (um número de 0 a 9 )  marca uma posição no texto CTRL + (um número de 0 a 9 )  vai para uma posição previamente marcada/ F11  Painel de propriedades F12  Comuta entre o formulário e a tela de codificação CTRL+F2  pára a execução de um programa. CTRL+F1  Executa o HELP a respeito do texto onde o cursor estava posicionado. COMO É FORMADA UMA APLICAÇÃO EM DELPHI Quando você abre um projeto no Delphi, ele já mostra uma UNIT com várias linhas de código. Este texto tem como objetivo explicar um pouco desta estrutura que o ele usa. Um projeto Delphi tem, inicialmente, duas divisórias: uma UNIT, que é associada a um Form, e outra Project, que engloba todos os FORM e UNITs da aplicação. Em Delphi temos: o Project, os Forms e as Units. Para todo Form temos pelo menos uma Unit (Código do Form), mas temos Units sem form (códigos de procedures, funções, etc). 25 Application.Run; end. ESTRUTURA DA UNIT DE UM FORM (.PAS) As Units do Delphi possuem uma estrutura que deve ser obedecida. Quando um Form é criado também é criada uma Unit associada ao mesmo. A estrutura básica de uma Unit pode ser visualizada observando-se o código fonte da mesma no editor de código. Será semelhante ao exibido a seguir: unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs; type TForm1 = class(TForm) private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} end. Vamos analisar o código acima: • Na primeira linha, o nome em frente à palavra unit, no caso Unit1, indica o nome dado ao arquivo com a programação do formulário. Se o formulário fosse salvo com este nome ele geraria um arquivo externo com o nome de Unit1.pas e outro com o nome de Unit1.dfm. (Quando for salvar seus formulários você deve dar nomes mais significativos). • Na linha seguinte, a palavra interface delimita a seção de interface na qual serão colocadas as definições de funções, procedimentos, tipos e variáveis que poderão ser vistos por outras units da aplicação. • A cláusula Uses dentro da seção interface indica quais units deverão ser ligadas para poder complementar a nossa . Ao criar um Form as Units definidas no código acima são inseridas automaticamente, pois fornecem o suporte para criação do mesmo. Ao inserir componentes num Form, outras Units podem ser adicionadas a esta lista. 26 • A seguir temos a definição dos tipos do programa, identificada pela palavra type. Neste ponto temos a definição de uma classe TForm1 que é derivada da classe base TForm. Ao se acrescentar componentes no Form também será gerado no código definição correspondente aos mesmos. (O conceito de classes e objetos será explicado no Capítulo 2) • O próximo item é a definição de variáveis e constantes globais, através da palavra reservada var. Neste ponto é criada uma variável com visibilidade global (pode ser vista em outras units nas quais a mesma seja incluída na cláusula uses) • A palavra chave implementation delimita a segunda seção da unit, onde serão colocadas as funções e variáveis que serão acessadas apenas por ela mesma (não são visíveis em outras units). • O símbolo {$R *.dfm} faz a associação da unit com seu respectivo form e não deve ser modificado. Uma unit de funções não possui esta diretiva. • Ao Final da Unit, temos uma linha com end. Ele é o marcador de final de arquivo. Qualquer coisa colocada após esta linha será ignorada. • Opcionalmente, uma unit pode ter ainda duas seções: initialization e finalization, com comandos que são executados quando a aplicação é iniciada ou finalizada. O PARÂMETRO “SENDER” E OS OPERADORES “IS” E “AS” O OPERADOR “IS” O operador IS é utilizado para lidar com classes e objetos. Ele verifica se um objeto é de uma determinada classe. Tipicamente ele aparece da seguinte forma: If NomeObjeto is NomeClasse then.... Mais adiante veremos um exemplo. O OPERADOR “AS" O operador AS também é utilizado lidar com classes e objetos. Ele diz ao delphi que determinado objeto deve ser tratado como se fosse de determinada classe (e ele tem que ser, senão ocorrerá um erro). Este processo é chamado de TypeCast. Normalmente ele é utilizado após a utilização do operador IS. Ex: If NomeObjeto is NomeClasse then Begin (NomeObjeto as NomeClasse).ação/propriedade; ou NomeClasse( NomeObjeto).ação/propriedade End; 27 O PARÂMETRO SENDER Agora veremos o que é o parâmetro SENDER : OBJECT que aparece na maioria dos eventos. Veremos também exemplos para os operadores IS e AS. O parâmetro SENDER é uma referência ao componente que acionou o evento, como se fosse um nome diferente para o componente dentro do procedimento, e através dele podemos acessar as propriedades e métodos do componente. 'Sender' é declarado como 'TObject', o que quer dizer que ele é um objeto genérico do Delphi, que pode ser um componente qualquer. Se você quiser acessar uma propriedade específica de um componente, por exemplo, Color, precisa dizer ao Delphi que temos certeza que 'Sender' é um componente da classe TEdit. Por exemplo: (Sender as TEdit).Color := clYellow; Ou TEdit(Sender).color := clYellow; Ou with Sender as TEdit do Color := clYellow; A expressão Sender as TEdit força o Delphi a tratar 'Sender' como um objeto da classe TEdit, ou seja, um componente Edit. Usando essa expressão, podemos acessar propriedades do componente. Isso é extremamente interessante em casos onde um mesmo evento é utilizado por muitos componentes. Isso mesmo! Um único evento associado a vários componentes, e estes nem precisam ser do mesmo tipo! Nesses casos, eles devem ter como ancestral a mesma classe. Exemplo: Imagine 2 botões, um da classe TBUTTON e outro da classe TBITBTN que devem executar a mesma ação: Ao serem clicados, devem exibir uma mensagem na tela e sua propriedade CAPTION deve ser alterada para “LIGADO”. Normalmente programaríamos o evento onClick dos botões da seguinte forma: procedure TForm1.Button1Click(Sender: TObject); procedure TForm1.BitBtn1Click(Sender: TObject); begin begin Button1.Caption := 'Ligado'; BitBtn1.Caption := 'Ligado'; ShowMessage('Botão foi ligado.'); ShowMessage('Botão foi ligado.'); end; end; 30 EVENTOS MAIS UTILIZADOS NO DELPHI: Alguns eventos estão presentes na maioria dos objetos. Vamos detalhá-los aqui. Obs: Alguns trechos foram retirados do help do Delphi 2005. Para maiores detalhes consulte-o. ONCREATE Este evento ocorre durante a criação de um objeto na memória. Você poderia, por exemplo, inicializar variáveis ou mesmo criar outros objetos neste evento. Ex: procedure TForm1.FormCreate(Sender: TObject) // evento oncreate do formulário; begin LimpaCamposTela; // procedure que limpará os campos da tela v_DadosSalvos := False; // inicializando uma variável end; ONKEYPRESS Use este evento para capturar as teclas pressionadas sobre o objeto. Este evento captura apenas teclas da tabela ASC II, utilizando para isso uma variável KEY do tipo char. Teclas como shift ou F1 não são possíveis de serem capturadas neste evento. Não é possível também capturar seqüências de teclas, como Shift+A. Para estes casos utilize o evento onKeyDown ou onKeyUp. A variável KEY representa o caractere ASC II da tecla pressionada. Para anularmos a tecla pressionada atribuímos #0 ao parâmetro KEY. Ex: // neste exemplo capturamos a tecla ENTER através de seu caractere ASC II procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: Char); // evento de um TEdit begin if key = #13 then begin showmessage('Você pressionou ENTER.'); Edit2.setfocus; // manda o foco para o componente edit2 end; end; // neste exemplo capturamos teclas através de seu valor e não se seu nº na tabela ASC II procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: Char); begin if key in ['a' .. 'z', 'A'..'Z'] then begin // permite apenas as letras de ‘a’ à ‘Z’ maiúsculas e minúsculas showmessage('Você pressionou a tecla: [' + key + ']' ); // exibe a tecla pressionada end; end; ONKEYDOWN Este evento faz tudo o que o evento onKeyPRess faz porém é capaz de processar sequencias de teclas, como CTRL+Z por exemplo. Também é capaz de capturar teclas como TAB, F1, CAPS LOCK, NUM LOCK, INSERT, etc... A variável KEY é do tipo word (um inteiro que não aceita negativo). Para anularmos a tecla pressionada atribuímos 0 ao parâmetro KEY. A variável Shift é do tipo TShiftState e pode conter um dos seguintes valores: 31 Value Meaning ssShift The Shift key is held down. ssAlt The Alt key is held down. ssCtrl The Ctrl key is held down. ssLeft The left mouse button is held down. ssRight The right mouse button is held down. ssMiddle The middle mouse button is held down. ssDouble The mouse was double-clicked. Ex: // neste exemplo exibimos o nº da tecla pressionada. Ideal para se descobrir o nº de uma tecla. procedure TForm1.Edit2KeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); begin showmessage( 'Você pressionou a tecla nº: ' + intToStr(key) ); end; // Este exemplo foi programado em um objeto FORM. Ele anula a seqüência de teclas // ALT+F4, seqüência esta que fecha um formulário. //Para que este exemplo funcione, a propriedade KeyPreview do formulário deve estar como TRUE. procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); begin if (key = 115) and (ssALT in Shift) then // 115 é o nº da tecla F4. utilizamos o operador IN pois Shift // pode conter um conjunto de teclas, como CTRL e ALT. key := 0 // anulamos a tecla pressionada; end; ONCLICK Este evento é disparado sempre que clicamos sobre o objeto. Ex: procedure TForm1.FormClick(Sender: TObject); begin showmessage('Você clicou no formulario!'); end; ONDESTROY Este evento é disparado quando um objeto é destruído da memória. Por exemplo, quando executamos o método FREE de um objeto o removemos da memória. Neste instante o evento onDestroy é disparado e poderíamos, por exemplo, destruir outros objetos. Dependendo do tipo de aplicação este evento é pouco utilizado. ONDBLCLICK Este evento é disparado sempre que executamos um duplo clique sobre o objeto em questão. Este evento não ocorrerá se o evento OnClick também foi programado. 32 Ex: procedure TForm1.FormDblClick(Sender: TObject); begin showwmessage('Você deu um duplo clique no formulario!'); end; ONMOUSEMOVE Este é um evento pouco utilizado em aplicações normais (cadastros p.ex.). Ele é disparado sempre que o mouse é movimentado sobre o objeto. É possível ainda saber o status de teclas como CTRL, ALT, SHIFT,etc. além das posições X e Y do mouse. Ex: procedure TForm1.Edit1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); begin showMessage('Tira esse mouse daqui!'); end; ONENTER Este evento é disparado quando o objeto recebe o foco. Ex: procedure TForm1.Edit1Enter(Sender: TObject); begin (sender as TEdit).color := clYellow; // altera a cor do objeto passado no parâmetro SENDER end; ONEXIT Este evento é disparado quando o objeto perde o foco. Ex: procedure TForm1.Edit1Exit(Sender: TObject); begin (sender as TEdit).color := clWhite; // altera a cor do objeto passado no parâmetro SENDER end; ASSOCIANDO UM PROCEDIMENTO À UM EVENTO EM TEMPO DE EXECUÇÃO A associação entre procedimentos e eventos pode ser feita também durante a execução do programa. Para isso, basta usar o nome do evento como uma propriedade e atribuir um valor que é o nome do procedimento de evento. Por exemplo, para associar o evento OnEnter do objeto Edit1 com o procedimento "EditRecebeuFoco", basta usar o seguinte comando: Edit1.OnEnter := EditRecebeuFoco; 35 Descendente da classe TWinControl. Este objeto é utilizado quando possuímos várias opções, porém apenas uma deve ser selecionada. Utilize-o dentro do componente para que seja criado um grupo de opções e este não seja agrupado à outros grupos. Sua principal propriedade é a checked que indica se objeto foi selecionado ou não. Descendente da classe TWinControl. Este objeto permite a seleção de uma ou mais opções. Descendente da classe TWinControl. Este objeto é utilizado quando possuímos várias opções, porém apenas uma deve ser selecionada. Utilize a propriedade ItemIndex para saber se alguma opção foi selecionada. Esta propriedade inicia do Zero e -1 indica que nada foi selecionado. Descendente da classe TWinControl. Este componente é similar ao TRadioButton, porém seus itens são criados através da propriedade ITENS, onde cada linha indica uma nova opção. Ex: Para saber se uma determinada opção foi selecionada devemos utilizar a propriedade ItemIndex, que indicará -1 caso nada tenha sido selecionado. Vale lembrar que os itens iniciam do ZERO. Descendente da classe TWinControl. Utilizado para agrupar componentes. Descendente da classe TWinControl. Utilizado para agrupar componentes. MANIPULANDO DATA E HORA NO DELPHI O Tipo de dado utilizado no Delphi para armazenar Data e Hora é o TDateTime. Apesar de ter recebido nome próprio ele é do tipo DOUBLE, ou seja, igual a um dos tipos predefinidos para a representação de números reais. A parte inteira é utilizada para armazenar os Dias que se passaram desde o dia 30/12/1899. A parte fracionada armazna as Horas do dia. Veja alguns exemplos na tabela abaixo: Valor Data e hora 0 30/12/1899, 0:00h 1,25 31/12/1899, 6:00h -1,75 29/12/1899, 18:00h 35291,625 14/08/1996, 15:00h Sendo TDateTime do Tipo Double, algumas operações numéricas passam a ter um significado especial. Por exemplo, para comparar duas datas, basta compará-las como se estivéssemos comparando dois números: 36 Var DataPagamento, DataVencimento : TdateTime; ..... If DataPagamento > DataVencimento then Showmessate(‘Pago no prazo.’); As seguintes operações são válidas para o tipo TDateTime: Sendo D1, D2,D3 : TDateTime: e Dias : Integer; D1 := D1 + 1; // D1 passa a ser o dia seguinte D1 := D1 - 1; // D1 passa a ser o dia anterior D3 := D1 – D2; // diferença entre as 2 datas Dias := Trunc(D1 – D2); // Diferença em dias entre as datas (Trunc pega só a parte inteira) D1 := D1 + StrToTime(’00:30:00’); //Adiciona 30 min. à hora. Pode ocorrer mudança de dia. D1 := StrToDate(‘25/12/2005’); // atribui uma nova data, neste caso a hora será perdida. D2 := StrToTime(’14:25:00’); // atribui uma nova hora, neste caso a data será perdida. D3 := StrToDate(‘25/12/2005’) + StrToTime(’14:25:00’); // atribui uma nova data e hora. D2 := Frac( D3 ); // pega só a hora da D3. Frac retorna a parte fracionada. Existem diversas rotinas para manipular Data e Hora que podem ser encontradas no help do Delphi (especialmente no Delphi 7) entituladas “Date Time Routines”. Dentre as principais podemos destacar: Now : retorna a data e hora atual; Date : retorna a data atual; Time : retorna a hora atual; StrToDate, StrToTime, TimeToStr, DateToStr, DateTimeToStr, StrToDateTime : conversão. DayOf, MonthOf, YearOf : Retornam dia, mês e ano respectivamente de uma data informada. FormatDateTime : Importantíssima função para formatação de data e hora. Não colocaremos aqui todos os seus parâmetros devido a grande quantidade. O exemplo abaixo: label1.caption := formatDateTime('"São Bernardo do Campo," dd "de" mmmm "de" yyyy', date); Produzirá o resultado: São Bernardo do Campo, 17 de setembro de 2005 Obs: As “ASPAS” estão em vermelho. Elas separam a parte “fixa” das variáveis. 37 APLICAÇÕES ROBUSTAS - TRATAMENTO DE EXCEÇÕES Fonte: http://www.geocities.com/SiliconValley/Bay/1058/except.html O tratamento de exceção é um mecanismo capaz de dar robustez a uma aplicação, permitindo que os erros sejam manipulados de uma maneira consistente e fazendo com que a aplicação possa se recuperar de erros, se possível, ou finalizar a execução quando necessário, sem perda de dados ou recursos. Para que uma aplicação seja segura, seu código necessita reconhecer uma exceção quando ela ocorrer e responder adequadamente a esta. Se não houver tratamento consistente para uma exceção, será exibida uma mensagem padrão descrevendo o erro e todos os processamentos pendentes não serão executados. Uma exceção deve ser respondida sempre que houver perigo de perda de dados ou de recursos do sistema. No exemplo acima, poderia-se clicar no botão OK e continuar a executar a aplicação. Mas muitas vezes o erro ocorrido impede a continuação da operação, ou leva a perda de informações valiosas. O ideal seria que se pudesse tratar estes erros ocorridos, evitando a perda de dados ou a necessidade de encerrar a aplicação. Além de tratar o erro, a rotina de tratamento de erros poderia enviar ao usuário uma mensagem em português , mais significativa. A forma mais simples para responder a uma exceção é garantir que algum código limpo é executado. Este tipo de resposta não corrige o erro, mas garante que sua aplicação não termine de forma instável. Normalmente, usa-se este tipo de resposta para garantir a liberação de recursos alocados, mesmo que ocorra um erro. O tratamento mais simples seria uma simples mensagem ao usuário com a proposta dele tentar novamente a execução da operação que tenha causado o erro, conforme podemos ver no exemplo abaixo: Outra forma de se responder a uma exceção é tratando o erro , ou seja, oferecendo uma resposta específica para um erro específico. Ao tratar o erro destrui-se a exceção , permitindo que a aplicação continue a rodar, mas o mais ideal mesmo é que além da mensagem, coloque os procedimentos devidos que sua aplicação deverá fazer caso ocorra uma exceção. Um exemplo disto é uma operação de gravação em uma tabela, caso ocorra um erro, ela deverá ser cancelada. Existe também a prática da geração de logs que gravam no disco, um arquivo texto contendo todos os dados possíveis do erro ocorrido de forma que possa chegar a causa o mais rápido possível. 40 FrmOptions.Release; end; A aplicação sempre executará os comandos inseridos na parte finally do bloco, mesmo que uma exceção ocorra. Quando um erro ocorre no bloco protegido, o programa pula para a parte finally, chamada de código limpo, que é executado. Mesmo que não ocorra um erro, estes comandos são executados. No código a seguir, foi alocada memória e gerado um erro, ao tentar-se a divisão por 0 (zero). Apesar do erro, o programa libera a memória alocada: Procedure Tform1.Button1click (Sender : Tcomponent ); Var Ponteiro : Pointer; Inteiro, Dividendo : Integer; Begin Dividendo:= 0; GetMem(Ponteiro, 1024); Try Inteiro: = 10 div dividendo; Finally FreeMem(Ponteiro, 1024); End; End; GERAÇÃO DE EXCEÇÕES Você pode provocar uma exceção usando a cláusula raise. raise EDatabaseError.Create('Erro ao alterar registro.'); Também é possível criar seus próprios tipos de exceções. type EInvalidUser = class (Exception); raise EInvalidUser.Create('Você não tem acesso a essa operação.'); Se você quiser que uma exceção continue ativa, mesmo depois de tratada, use a cláusula raise dentro do bloco de tratamento da exceção. Geralmente isso é feito com exceções aninhadas. try Tbl.Edit; TblContador.Value := TblContador.Value + 1; Tbl.Post; except ShowMessage(''Erro ao alterar contador.'); raise; end; EXIBINDO O ERRO GERADO PELA EXCEÇÃO 41 Para exibir a mensagem de erro que foi gerada pela exceção devemos criar uma programação especial no bloco except. Ex: try A := StrToFloat(Edtit.Text); B := StrToFloat(Edtit.Text); ShowMessage(Format('%f / %f = %f', [A, B, A + B])); except On E:Exception do begin ShowMessage('Números inválidos. Erro gerado: ' + E.message); end; end; EXCEÇÕES SILENCIOSAS Pode-se definir exceções que não mostrem um quadro de mensagem para o usuário quando aparecem. São chamadas exceções Silenciosas. O caminho mais curto para criar esta exceção é através da procedure Abort. Esta procedure automaticamente gera uma exceção do tipo Eabort, que abortará a operação sem mostrar uma mensagem. O exemplo abaixo aborta a operação de inclusão de itens em um Listbox quando tentamos inserir o terceiro elemento: For i := 1 to 10 do Begin Listbox.Items.Add(Inttostr(i)); If i = 3 then Abort; End; MANIPULANDO EXCEÇÕES GLOBAIS O Delphi oferece um evento chamado OnException, ligado à a classe Tapplication, que permite manipular qualquer exceção que ocorra em seu programa, mesmo que não sabendo em que parte do programa ela ocorreu. Inicialmente, deve-se criar manualmente, como um método de Tform1, a chamada para o método que tratará os erros : Tform1 = Class (Tform) Procedure Trata_Erros (Sender : Tobject ; E : Exception ); End; Depois de declarar a procedure, deve-se atribuí-la ao evento OnException de Tapplication. A atribuição é feita no evento OnCreate do formulário principal : Procedure Tform1.FormCreate ( Sender : Tobject ); Begin Application.OnException := Trata_Erros; End; O método Trata_Erros será agora chamado quando qualquer exceção ocorrer. Pode-se determinar mensagens para erros específicos, ou mensagens gerais : 42 Procedure Tform1.Trata_Erros (Sender : Tobject; E : Exception); Begin If E is EdatabaseError then Showmessage('Erro no banco de dados'); Else Showmessage('Há erros no programa'); End; ENTENDENDO UM POUCO SOBRE O QUE É UM GPF A bem da verdade as exceções não tratadas ou mal tratadas em uma aplicação são as uma das causas do GPF (Falha geral de proteção) que ocorrem no Windows e que geralmente o derrubam. Como isto acontece? Se seu programa conseguir sobreviver à uma exceção sem que ele tenha caído (O que não é muito difícil em se tratando de exceções silenciosas), ele pode perder o seu endereçamento da memória. Daí começar a acessar uma área protegida, ou uma área que está sendo usada por outro programa, é questão de tempo. Um problema destes, normalmente acaba com uma mensagem de erro do Windows que fecharia tanto este aplicativo como os outros envolvidos no problema e em seguida, uma simples reinicialização da máquina volta ao normal. Mas podem ocorrer problemas mais sérios como uma colizão com a FAT, o que resultaria na perda de todo ou parte do conteúdo do HD. Obviamente que um caso destes é muito raro de acontecer, mas que existe a probabilidade existe! O GPF (General Protection Faults) é um evento de hardware, causado pela própria CPU. No Windows ocorrem basicamente por culpa dos aplicativos, que não possuem um tratamento consistente de suas exceções internas. Como consequência deste problema pode ocorrer os seguintes eventos: - Tentativa, pela aplicação, de acessar memória fora da área determinada pelo sistema operacional; - Tentativa de acessar um ponteiro inválido, podem fazer com que a aplicação acesse uma área protegida; - Tentativa de acessar uma área de memória que está sendo usada por outro aplicativo ou biblioteca (dll); - Tentativa de execução de dados, ao invés de código (programa); - Tentativa de efetuar uma operação binária inválida (Não confunda com operação aritmética); - Tentativa de acesso indevido ao hardware; CONFIGURANDO O DELPHI PARA NÃO “PARAR” EM EXCEÇÕES O Delphi vem configurado para parar em todas as exceções, mesmo aquelas onde há utilização de blocos protegidos. Para desligar esta opção: No Delphi 7: Menu TOOLS | DEBUGGER OPTIONS | LANGUAGE EXCEPTIONS | desmarque a opção “Stop on Delphi Exceptions” No Delphi 2005: Menu TOOLS | OPTIONS | DEBUGGER OPTIONS | ….. CONCLUSÃO: Depois de ler este artigo, você pode concluir que não deve, sob nenhuma circunstância, negligenciar o tratamento de exceções dentro de sua aplicação uma vez que sempre que elas ocorrem, acabam afetando não só a esta referida aplicação mas como outras ainda. Um exemplo disto é o caso de uma aplicação que dá um lock em uma tabela no banco de dados e em seguida tentará abri-la. Caso 45 CRIANDO UMA BIBLIOTECA DE PROCEDURES, FUNÇÕES, ETC. Uma bliblioteca de funções, procedures, constantes, etc. nada mais é que uma UNIT (unidade .pas), ou seja um arquivo.pas sem um formulário atrelado. Nele podemos inserir procedures, funções, constantes, etc. que serão utilizadas em todo o programa. Por exemplo: função para validar CPF, constante contendo a cor dos formulários, procedure para validar datas, etc. Para criar uma biblioteca é muito simples: vá ao menu FILE | NEW | OTHER | DELPHI FILES | UNIT. A estrutura de uma unit vazia é a seguinte: unit Unit1; { este é o nome da biblioteca. Não altere-o por aqui!!!!. Use a opção Save-as} interface { aqui colocamos a cláusula USES e na seqüência constantes,variáveis e o protótipo das funções/procedures } implementation {aqui nós efetivamente programamos os protótipos informados na seção Interface } end. Exemplo Completo de uma biblioteca: unit uUnit; interface uses SysUtils, Dialogs; const x=50; {esta é uma constante} var y : integer; {esta é uma variável global} function soma(a,b : longint) : longint; function converte( valor : string) : longint; Function ValidaData( data : string; exibemsg : boolean =true ) : boolean; function InteiroValido( valor : string; exibemsg : boolean ) : boolean; implementation // soma 2 numeros function soma(a,b : longint) : longint; Begin result := a + b; end; // tenta converter um string para um longint e se nao conseguir devolve zero. function converte( valor : string) : longint; begin try result := strToInt( trim(valor) ); except result := 0; 46 end; end; // valida uma data. pode exibir uma msg de erro dependendo do parâmetro exibemsg. Function ValidaData( data : string; exibemsg : boolean =true) : boolean; Begin try StrToDate(data); result := true; except result := false; if exibemsg = true then showmessage('Data inválida!!!'); end; end; // verifica se um numero inteiro eh valido function InteiroValido( valor : string; exibemsg : boolean ) : boolean; begin try strToInt(valor); result := true; except result := false; if exibemsg = true then showmessage('Número Inteiro Inválido!!!'); end; end; end. Para utilizar esta biblioteca basta colocar o nome dela na seção USES das outras units. Ex: unit uMain; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, uUnit, StdCtrls, Buttons, Mask, ExtCtrls; Depois utilizamos normalmente como se fosse uma função local: procedure TForm1.Button1Click(Sender: TObject); begin edit3.text := intToStr( soma( converte(edit1.text) , converte(edit2.text ) ) ); end; 47 TRABALHANDO COM FORMULÁRIOS Ao iniciar um novo projeto, o Delphi já cria automaticamente um novo formulário. Porém, um formulário pode ser muito pouco para nossas pretenções. O Delphi permite a criação e manipulação de vários formulários. Para criar um novo formulário vá ao menu File | New | Form – Delphi for Win32. Para exibir um formulário temos basicamente 2 opções: Os métodos show e showmodal. A principal diferença entre eles é que ao exibir um form com o evento show ainda nos é permitido acessar a tela anterior (a que evocou o novo form). Já o método showmodal faz com que a tela exibida seja a única possível de ser acessada no sistema. Isso é muito comum nas telas de opções do Delphi. Outro detalhe interessante é que o método showmodal quando executado interrompe a execução das linhas de programa após a sua chamada. Sendo assim, esses “códigos pendentes” só serão executados quando o formulário exibido for fechado. No exemplo abaixo, a mensagem só aparecerá após o Form2 ser fechado. Form2.showmodal; Showmessage(‘O Form2 foi fechado.’); Se tivéssemos utilizado o método Show no lugar do ShowModal a mensagem seria exibida assim que form fosse exibido. O Delphi cria automaticamente todos os formulários do programa assim que o sistema é carregado. Isso pode ser muito ruim no caso de um sistema com muitos formulários pois consome muita memória. Seria interessante que os formulários fossem criados à medida que forem sendo utilizados, e destruídos quando não forem mais necessários. Para manipular quais formulários serão ou não criados automaticamente no Delphi, vá ao menu PROJECT | OPTIONS na aba Forms. Deste lado temos os formulários que são criados automaticamente pelo Delphi. Normalmente o formulário principal (aquele que contém o menu do programa) é criado automaticamente, assim como datamodules e eventuais telas que são muito utilizadas. Este será o primeiro formulário exibido quando o programa for executado. Deste lado temos os formulários que NÃO são criados automaticamente pelo Delphi. Estes formulários devem ser criados e destruidos em tempo de execução. 50 • Compatibilidade limitada entre versões: obriga o usuário a atualizar os computadores para a mesma versão que utilizou para desenvolver o programa. • Apresenta vantagens para quem usa Access ou SQL Server, oferecendo melhor qualidade de drivers que os dos provedores de OLE DB normais. • Não serve para desenvolvimento independente de plataforma: não disponível no Linux ou em outros sistemas operacionais. • O pacote ADO foi denominado pela Borland de dbGo, e seus componentes principais são: ADOConnection (para conexão ao banco de dados), ADOCommand (para execução de comandos SQL) e ADODataSet (para execução de requisições que retornam um conjunto de resultados). Além desses, existem 3 componetes de compatibilidade: ADOTable, ADOQuery e ADOStoredProc, que podem ser usados para portar aplicativos baseados no BDE. E ainda há o componente RDSConnection, que permite acessar dados em aplicativos multicamadas (multitier) remotos. • A Microsoft está substituindo o ADO pela sua versão .NET, a qual se baseia nos mesmos conceitos básicos. Assim, o uso do ADO pode fornecer uma boa preparação para quem caminha na direção de aplicativos .NET nativos (embora a Borland também planeje portar o dbExpress para aquela plataforma). "INTERBASE EXPRESS">INTERBASE EXPRESS (IBX): • Não é um mecanismo de banco de dados independente de servidor, mas um conjunto de componentes para acessar um servidor de banco de dados específico. • Esse conjunto de componentes aplica-se a diferentes ambientes (Delphi, Oracle) e plataformas (Linux). • Oferecem melhor desempenho e controle, à custa de flexibilidade e portabilidade. • Transações: Cada operação de edição/postagem é considerada uma transação implícita, mas deve-se alterar esse comportamento manipulando as transações implicitamente. Deve-se evitar as transações que esperam entrada do usuário para serem concluídas, pois o usuário poderia ficar foram indefinidamente e a transação permaneceria ativa por um longo tempo, consumindo peformance do sistema. Deve-se isolar cada transação corretamente, por meio do nível de isolamento SnapShot para relatórios e ReadCommited para formulários interativos. Uma transação começa automaticamente quanto você edita qualquer conjunto de dados ligados a ela. O comando CommitRetaining não abre uma nova transação, mas permite que a transação atual permaneça aberta. • Componentes Ibx: IbSQL: executa instruções SQL que não retornam um conjunto de dados (instruções DDL ou instruções update e delete); IbDataBaseInfo: consultar estrutura e status do banco de dados; IbSqlMonitor: depurador do sistema (o SQL monitor do Delphi é específico para o BDE); IbEvents: recebe eventos enviados pelo servidor. 51 CONFIGURANDO UMA CONEXÃO COM BD USANDO ADO NO BORLAND DELPHI 2005 Configurando o objeto CONNECTION para conectar no SQL SERVER 2000: • Duplo clique no objeto • Clique no Botão BUILD Selecione o componente de conexão na palheta DBGO. Selecione também um objeto QUERY O Componente DATASOURCE fica na palheta DATA ACCESS A propriedade DATASOURCE é comum a todos os componentes da palheta DATA CONTROLS. 52 • Escolha a conexão selecionada e clique em NEXT • ALTERE a propriedade LoginPrompt para FALSE. Escolha o servidor (computador) que gerencia o Banco. Selecione uma forma de autenticação. Usaremos no laboratório a segunda opção. Informe no usuário: SA e na senha: 123 Selecione um banco de dados Teste a conexão para ver se tudo está OK Clique em OK para finalizar Se esta opção (Allow saving password) estiver disponível, ligue-a. 55 • Agora vamos programar 2 eventos do FORMULÁRIO: • Evento onformShow do formulário: procedure TForm1.FormShow(Sender: TObject); begin if not ADOConnection1.Connected then // abre a conexão com o banco de dados ADOConnection1.Open; ADOQuery1.close; // já deveria estar fechada, mas se não estiver, nós a fechamos aqui; ADOQuery1.open; end; • Evento onFormClose do formulário: procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction); begin ADOQuery1.close; // fechamos a conexão ADOConnection1.close; // uma boa prática de programação: fechar as query´s abertas. end; Objeto SEXO Propriedade ITEMS: Masculino Feminino Propriedade Values: M F Objeto COR Propriedade ITEMS: BRANCA PRETA PARDA AMARELA INDÍGENA Propriedade Values: 1 2 3 4 5 56 VALIDANDO OS DADOS DO CADASTRO Existem várias formas de se realizar as validações em um cadastro. Pode ser no botão de OK, no evento beforpost da Query, etc.. Vamos ver como ficaria no evento onBeforePost do objeto adoQuery: procedure TForm1.ADOQuery1BeforePost(DataSet: TDataSet); begin // para cancelar a gravação neste evento, precisamos gerar uma exceçao com raise ou abort. try StrToInt( edCodigo.text ) except // se usar o raise não dá para usar o setfocus.. mas ... raise exception.create('Código Inválido!'); end; if length(edNome.text) = 0 then begin showmessage('Preencha o nome corretamente.'); abort; // ... você pode usar o conjunto mensagem + abort! end; // você pode validar também diretamente pelos campos da tabela, ao invés dos // componentes de tela. ex: try StrToDate( ADOQuery1.fieldbyname('alun_nascimento').asString ) ; except raise exception.create('Data Inválida!'); end; if ADOQuery1.fieldbyname('alun_sexo').IsNull then // isnull significa que o campo nao foi preenchido! raise exception.create('Informe o sexo'); // outra forma de ver se foi preenchido seria: if ADOQuery1.fieldbyname('alun_cor').asString = '' then // e existem ainda outras formas. raise exception.create('Informe a cor'); end; 57 CRIANDO UM CADASTRO COM MAIS DE UMA TABELA (FK) No cadastro anterior, as cores foram colocadas fixas no código. Se houver a necessidade de adicionarmos mais cores teremos que mudar no código. Seria interessante que as cores fossem selecionadas de uma tabela, e é isso que vamos fazer agora. Primeiro vamos criar a tabela de cores. Não esqueça de criá-la no banco NORTHWIND!!! create table cor (cor_cod int primary key, cor_desc varchar(20) ); Cadastrando as cores: insert into cor values (1, 'BRANCA'); insert into cor values (2, 'PRETA'); insert into cor values (3, 'PARDA'); insert into cor values (4, 'AMARELA'); insert into cor values (5, 'INDÍGENA'); select * from cor; Coloquem na tela os seguintes componentes: ADOQUERY (mude o nome para quCor) e DATASOURCE (mude o nome para dsCor) Faça as devidas conexões como já descrito anteriormente e no objeto quCor digite : select * from cor Insira no lugar do componente TRadioGroup que controlava as cores o seguinte componente que está na palheta DataControls: DBLookupComboBox (este componente é capaz de exibir todos os registros de uma tabela) e preencha as seguintes propriedades dele: DataSource : o mesmo que controla os outros componentes (DataSource1) DataField: o campo em que será gravado o dado escolhido pelo usuário (alun_cor) ListSource : DsCor (o datasource da tabela cujos dados serão listados) ListField : cor_desc (o campo que sera listado) KeyField: cor_cod (o campo chave desta tabela e que está relacionado com o campo datafield) A tabela quCor deve ser aberta no evento onShow do Formulário e fechada no evento onClose também do formulário. 60 CADASTROS SEM O TDBNAVIGATOR O objeto TDBNavitator é um bom quebra-galho para executar as principais operações sobre um objeto Tdataset (query, table,etc...). Porém em algumas situações seria interessante que nós usássemos nossa própria barra de controle. Para isso precisamos conhecer os principais métodos e propriedades de um objeto TDataset: Prior registro anterior Next próximo registro First primeiro registro Last ultimo registro Cancel cancela a edição/inserção Post grava o registro Edit edita o registro Insert Insere um novo registro na posição atual Append Insere um novo registro no final da tabela Isempty Returna verdadeiro se a tabela está vazia e false caso contrário Open Abre a tabela Close Fecha a tabela Locate Realiza uma pesquisa na tabela. Veremos em detalhes mais adiante. RecordCount Retorna a quantidade de registros. BOF Retorna verdadeiro se a tabela estiver apontando para o primeiro reg. EOF Retorna verdadeiro se a tabela estiver apontando para o último reg. Sendo assim, podemos usar botões para representar estes métodos em nosso cadastro. Uma propriedade interessante da classe TDATASET é a State, que retorna o estado atual de uma tabela. Os principais estados são: dsInsert indica que a tabela está em nodo de inserção dsedit indica que a tabela está em nodo de edição dsbrowse indica que a tabela está em nodo de navegação 61 UMA TELA CRIADA SEM O TDBNAVIGATOR Programação dos botões : procedure TForm1.btnNovoClick(Sender: TObject); begin ADOQuery1.append; end; procedure TForm1.btnAlteraClick(Sender: TObject); begin ADOQuery1.edit; end; procedure TForm1.btnApagaClick(Sender: TObject); begin if not ADOQuery1.isempty then if application.messagebox('Deseja realmente apagar?', 'Atenção', mb_yesno + mb_iconquestion + mb_defbutton2) = id_yes then ADOQuery1.delete; end; procedure TForm1.btnOKClick(Sender: TObject); begin ADOQuery1.post; end; procedure TForm1.btnCancelClick(Sender: TObject); 62 begin ADOQuery1.cancel; end; procedure TForm1.btnPrimeiroClick(Sender: TObject); begin ADOQuery1.first end; procedure TForm1.btnAntClick(Sender: TObject); begin ADOQuery1.prior; end; procedure TForm1.btnProxClick(Sender: TObject); begin ADOQuery1.next; end; procedure TForm1.btnUltimoClick(Sender: TObject); begin ADOQuery1.last; end; Para habilitar/desabilitar os botões de controle, podemos utilizar o evento StateChange do componente TDataSource que controla o cadastro. Este evento é disparado sempre que o estado da tabela muda. Exemplo: ao iniciar uma edição, cancelar a gravação, começar a inclusão, etc... procedure TForm1.DataSource1StateChange(Sender: TObject); begin btnAnt.enabled := ADOQuery1.State = dsBrowse; btnProx.enabled := ADOQuery1.State = dsBrowse; btnPrimeiro.enabled := ADOQuery1.State = dsBrowse; btnUltimo.enabled := ADOQuery1.State = dsBrowse; btnNovo.Enabled := ADOQuery1.State = dsBrowse; btnAltera.Enabled := ADOQuery1.State = dsBrowse; btnApaga.enabled := ADOQuery1.State = dsBrowse; btnOK.enabled := ADOQuery1.State in [ dsInsert, dsEdit]; btnCancel.enabled := ADOQuery1.State in [ dsInsert, dsEdit]; end; 65 CONTINUANDO COM A CONSTRUÇÃO DA TELA DE CONSULTA O MÉTODO LOCATE DA CLASSE TDATASET Antes de prosseguirmos com a tela de consulta, precisamos entender o método LOCATE . Ele permite uma pesquisa em uma tabela, posicionando o cursor no registro pesquisado e retornando TRUE se encontrou ou FALSE se não encontrou. Ex: Vamos pesquisar o aluno de número 5: If adoquery1.Locate('alun_cod', 5, [])= true then Showmessage(‘Encontrado’) Else Showmessage(‘NÃO Encontrado !!!’) MAS ATENÇÃO: Ao pesquisar um registro a tabela muda seu estado para dsBrowse, portanto, se a tabela estava antes da pesquisa em modo de edição ou inserção, ela tentará executar o POST para passar para o estado dsBrowse e então executar o método locate. Conecte todos os objetos, inclusive a propriedade connection do Adoquery ao objeto de conexão que está no DATAMODULE e digite na propriedade SQL da Query: select * from alunos where Alun_cor between :p_cor_INI AND :p_cor_FIM Eventos que devem ser preenchidos: procedure TfmConsulta.FormShow(Sender: TObject); begin // preenchendo as cores do combobox adoquery1.close; combocor.Clear; Mude as seguintes propriedades da grade: Color= clmenu Readonly=True Options.dgRowSelect=true Options.dbAlwaysShowSel ection = true 66 dm.quCor.Close; dm.quCor.open; while not dm.quCor.eof do // enquanto nao for o fim do arquivo... begin ComboCor.Items.Add(dm.quCor.fieldbyname('cor_cod').asString + ' - ' + dm.quCor.fieldbyname('cor_desc').asString ); dm.quCor.next; end; ComboCor.Items.Add('*** TODAS ***'); dm.quCor.close; Edit1.Clear; ComboCor.SetFocus; end; A programação abaixo faz com que a query seja aberta de acordo com a cor que o usuário escolher: procedure TfmConsulta.ComboCorSelect(Sender: TObject); var p_ini, p_fim : integer; begin // descobrindo o valor da cor para usar como parâmetro na query if comboCor.text = '*** TODAS ***' then begin p_ini := 0; p_fim := 9999999; end else begin // copiar até 1 caractere a menos que a posição do traço (-), tirar os espaços e converter. p/ inteiro p_ini := StrToInt(Trim(copy(comboCor.text,1, pos('-', comboCor.text) -1 )) ); p_fim := StrToInt(Trim(copy(comboCor.text,1, pos('-', comboCor.text) -1 )) ); end; // preenchendo os parâmetros da query ADOQuery1.close; ADOQuery1.Parameters.ParamByName('p_cor_ini').Value := p_ini; ADOQuery1.Parameters.ParamByName('p_cor_fim').Value := p_fim; ADOQuery1.open; end; O código abaixo permite que o usuário realize uma pesquisa incremental na tabela. As opções loPartialKey, loCaseInsensitive são utilizadas apenas em campos TEXTO. procedure TfmConsulta.Edit1Change(Sender: TObject); begin if (ADOQuery1.Active) and (trim(Edit1.text) <> '') then begin adoquery1.Locate('alun_nome', trim(Edit1.text), [loPartialKey, loCaseInsensitive ]); end; end; 67 VERIFICANDO SE UM REGISTRO EXISTE EM UMA TABELA Quando vamos incluir um novo registro, precisamos averiguar se não violaremos a chave primária, tentanto, por exemplo, gravar um registro com código repetido.O método locate não serve para verificar se um registro já existe em uma tabela quando utilizamos para a pesquisa a mesma tabela onde estamos cadastrando o nosso registro, pois, como já visto, o registro muda seu estado para dsBrowse. Faremos esta verificação de outra forma. Para isso podemos fazer uma pesquisa ANTES de gravar o registro, procurando pelo código que o usuário está tentando cadastrar. A chave primária completa deve ser utilizada na busca. Se a pesquisa retornar algo é porque este código já existe na base de dados e não pode ser aceito. Caso contrário a gravação poderá prosseguir normalmente. Um bom local para este tipo de programação é no evento onBeforePost do DataSet que se vai gravar o registro. Utilizaremos uma query para realizar esta pesquisa, pode ser uma query auxiliar pois preencheremos sua instrução SQL em tempo de execução. Ex: procedure TForm1.ADOQuery1BeforePost(DataSet: TDataSet); begin // para cancelar a gravação neste evento, precisamos gerar uma exceçao com raise ou abort. try StrToInt( edCodigo.text ); except raise exception.create('Código Inválido!'); end; // é importante que o campo edCodigo esteja preenchido caso contrário a query abaixo não funcionará! // Validando se o código informado já existe na tabela alunos. A query precisa retornar apenas 1 campo.... quAux.close; quAux.Sql.text := ‘select alun_cod from alunos where alun_cod = ‘ + edCodigo.text; quAux.open; // se a query não estiver vazia é porque já existe um aluno com este código.... if quAux.isempty = false then begin quAux.close; raise exception.create(‘Já existe um aluno cadastrado com este código. Tente outro!'); end; quAux.close; ...... continuação das outras validações..... end; 70 TRABALHANDO COM VETORES DINÂMICOS Em algumas situações precisamos trabalhar com os dados de uma tabela em um vetor. Por exemplo, se quisermos ordenar, escolher um elemento aleatoriamente, fazer cálculos, etc. Para isso precisamos criar um vetor que tenha a mesma capacidade da tabela. Mas como fazer isso em tempo de execução se precisamos definir o tamanho do vetor em tempo de programação? Para situações como essa podemos usar a procedure SetLength que modifica o tamanho de um vetor. Porém, ao criarmos este vetor não podemos especificar o tamanho inicial. Podemos usar também a função LENGTH para retornar a quantidade de elementos de um vetor. Observe que a posição inicial de um vetor dimensionado pelo SetLength é 0 (zero) . EX: Delphi syntax: procedure SetLength(var S; NewLength: Integer); // onde S é o vetor e NewLength é o tamanho. Var vetor : array of integer; Begin SetLength(vetor, 10 ); // cria um vetor de inteiros de tamanho 10. SetLength(vetor, 1200 ); // muda o tamanho para 1200 sem perder o que já havia nas 10 primeiras posições. SetLength(vetor, 200 ); // muda o tamanho para 200 ... e é claro, perde o que havia nas outras posições. showmessage(' O vetor tem : ' + intToStr( length(vetor) ) + ' posições.'); // exibe o tamanho do vetor. End; Um exemplo completo: Este exemplo lê a tabela products do banco northwind e guarda os campos productID, productName e unitprice em um vetor dinâmico do tipo TDados, que é um registro definido no código. Faça a conexão com o banco de dados NorthWind e digite na query QuProducts: Select * from products Mude a propriedade enabled do botão [listar o vetor] para false. Button1 Button2 Memo1 71 A vetor foi definido na seção PRIVATE do código, criamos assim uma variável global, que só tem visibilidade nesta unit. private vetor : array of Tdados; Programando os botões: procedure TForm1.Button1Click(Sender: TObject); var i : integer; begin Button2.enabled := true; if not ADOConnection1.Connected then ADOConnection1.Open; quProducts.close; quProducts.open; quProducts.last; // é bom fazer isso para que Delphi carregue todos os registros na memória quProducts.first; SetLength(vetor, quProducts.RecordCount ); // redimensiona o vetor i := 0; while not quProducts.eof do begin vetor[i].Codigo := quProducts['productID']; vetor[i].descricao := quProducts['productname']; vetor[i].preco := quProducts['unitprice']; Para poder armazenar mais de uma informação por elemento do vetor, vamos criar um registro. 72 quProducts.next; inc(i); // incrementa a variável I end; quProducts.close; showmessage('Concluído! O vetor tem : ' + intToStr(length(vetor)) + ' posições.'); end; procedure TForm1.Button2Click(Sender: TObject); var i : integer; begin Memo1.Lines.Clear; Memo1.Lines.add('Primeira posição começa em : ' + intToStr(low(vetor)) ); for i:=low(vetor) to High(vetor) do // low = primeira posição do vetor e high = última begin memo1.Lines.Add('Código: ' + intToStr(vetor[i].codigo)); memo1.Lines.Add(Descrição: ' + vetor[i].descricao); memo1.Lines.Add('Preço: ' + FormatFloat('#0.00', vetor[i].preco) ); // *FormatFloat formata um Float!!! memo1.Lines.Add('**********************************************'); end; Memo1.Lines.add('Última posição : ' + intToStr( high(vetor)) ); end; *O comando FormatFloat formata um valor do tipo Real,Doublé,etc.. em um formato String. Delphi syntax: function FormatFloat(const Format: string; Value: Extended): string; Exemplos: The following table shows some sample formats and the results produced when the formats are applied to different values: Format string- 1234 -1234 0.5 0 1234 -1234 0.5 0 0 1234 -1234 1 0 0.00 1234.00 -1234.00 0.50 0.00 #.## 1234 -1234 .5 #,##0.00 1,234.00 -1,234.00 0.50 0.00 #,##0.00;(#,##0.00) 1,234.00 (1,234.00) 0.50 0.00 #,##0.00;;Zero 1,234.00 -1,234.00 0.50 Zero 0.000E+00 1.234E+03 -1.234E+03 5.000E-01 0.000E+00 #.###E-0 1.234E3 -1.234E3 5E-1 0E0 75 ) ON [PRIMARY] GO CREATE TABLE [dbo].[Clientes] ( [cliente_cod] [int] NOT NULL , [cliente_nome] [nvarchar] (50) COLLATE SQL_Latin1_General_CP1_CI_AS NULL , [Agencia] [int] NULL ) ON [PRIMARY] GO CREATE TABLE [dbo].[Movimento] ( [CCorrente] [nvarchar] (10) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL , [Data_Hora] [datetime] NOT NULL , [Tipo] [char] (1) COLLATE SQL_Latin1_General_CP1_CI_AS NULL , [CC_Creditada] [nvarchar] (10) COLLATE SQL_Latin1_General_CP1_CI_AS NULL , [valor] [money] NULL ) ON [PRIMARY] GO ALTER TABLE [dbo].[CCorrente] WITH NOCHECK ADD CONSTRAINT [PK_CCorrente] PRIMARY KEY CLUSTERED ( [Ccorrente] ) ON [PRIMARY] GO ALTER TABLE [dbo].[Clientes] WITH NOCHECK ADD CONSTRAINT [PK_Clientes] PRIMARY KEY CLUSTERED ( [cliente_cod] ) ON [PRIMARY] GO ALTER TABLE [dbo].[Movimento] WITH NOCHECK ADD CONSTRAINT [PK_Movimento] PRIMARY KEY CLUSTERED ( [CCorrente], [Data_Hora] ) ON [PRIMARY] GO ALTER TABLE [dbo].[CCorrente] ADD CONSTRAINT [FK_CCorrente_Clientes] FOREIGN KEY ( [Cliente_cod] ) REFERENCES [dbo].[Clientes] ( [cliente_cod] ) ON DELETE CASCADE ON UPDATE CASCADE GO ALTER TABLE [dbo].[Movimento] ADD CONSTRAINT [FK_Movimento_CCorrente] FOREIGN KEY ( [CCorrente] ) REFERENCES [dbo].[CCorrente] ( [Ccorrente] ) ON DELETE CASCADE ON UPDATE CASCADE , CONSTRAINT [FK_Movimento_CCorrente1] FOREIGN KEY ( [CC_Creditada] ) REFERENCES [dbo].[CCorrente] ( [Ccorrente] ) GO A aplicação completa pode ser encontrada nos arquivos das aulas. 76 FILTRANDO DADOS DE UMA TABELA O Delphi possui uma maneira simples de filtrar os dados de objetos da classe TDATASET. Quando nos referimos à classe TDATASET estamos também nos referindo a todos os seus descendentes, como o TADOQUERY, TTABLE, etc. Sempre que possível devemos optar por filtrar os dados na própria instrução SQL, como na cláusula WHERE, porém, em algumas situações isso não é possível e então a propriedade FILTER torna-se interessante. Quando a tabela está processando um filtro sua propriedade STATE passa para dsState. Ao executarmos a propriedade RecordCount sobre uma tabela “Filtrada” este indicará a quantidade de registros que passaram pelo filtro. Para que o filtro seja executado a propriedade FILTERED deve ser alterada para TRUE. Para desligar o filtro basta alterá-la para False. Para filtrar uma tabela devemos construir o filtro. Esta etapa pode ser realizada de 2 formas:  Através da propriedade FILTER da Tabela: A propriedade Filter : String do objeto TDataSet permite que seja informado um filtro no formato String. Este filtro pode ser informado em tempo de execução. Exemplos: o Tabela.Filter := ‘codigo = 3’; o Tabela.Filter := ‘codigo = 3 and salario < 5000’; o Tabela.Filter := ‘((codigo = 3) and (salario < 5000)) or ( idade > 100) ‘; // expressões o Tabela.Filter := ‘nome = ‘ + quotedStr(‘MARIA DA SILVA’) ; // nome igual ao informado o Tabela.Filter := ‘nome like ‘ + QuotedStr(‘%silva%’); // todos que tem silva no nome o Tabela.Filter := ‘desconto > acrescimo’; // comparando 2 campos da tabela  Através do Evento onFilterRecord da Tabela: Este evento permite que o filtro seja elaborado de forma mais apurada. Podemos utilizar estruturas de decisão, repetição, etc. na construção do filtro. Este evento é acionado para cada registro da tabela em questão a fim de verificar se ele “passará” pelo filtro. A variável ACCEPT indica se o registro deve ou não ser aceito. Ex: procedure TForm1.quFuncionariosFilterRecord(DataSet: TDataSet; var Accept: Boolean); begin // só são aceito registros cujo salario é maior que os descontos if Dataset.fieldbyname('valor_salario').asFloat >= Dataset.fieldbyname('valor_Atrasos').asFloat + Dataset.fieldbyname('valor_convenio').asFloat then accept := true else accept := false; end; No exemplo acima poderíamos ter utilizado a tabela quFuncionarios ao invés do parâmetro DATASET. 77 FRAMES Um Frame é, digamos assim, uma mistura das facilidades de manipulação de um Painel com a robustez e reutilização de código dos Formulários. O propósito deste componente é criar uma classe incorporada de vários outros componentes. Uma das coisas mais interessantes do Frame que é ele pode também ser adicionado a sua palheta de componentes, assim como os Componet Templates, só que com muito mais facilidade e, sendo assim instanciado futuramente em outras aplicações de forma independente da original. A utilização de Frames é muito simples, basta clicar em 'File | New | Other | Delphi Files | Frame'. Um novo Frame é criado na forma de um formulário. Podemos então incluir os objetos que desejamos utilizar, escrever alguns manipuladores de evento para os componentes. Depois basta simplesmente incluir em um Formulário um objeto Frame da palheta de componentes Standard. Selecione o Frame criado na janela exibida e clique em Ok. Será adicionado um objeto parecido com o TPanel no Formulário contendo os objetos incluídos no Frame que você criou. As alterações feitas em objetos TFrame são automaticamente refletidas nas instâncias que a referenciam. O código é mantido na unidade do Frame. Na prática, o uso dos Frames é útil quando se deseja utilizar o mesmo grupo de componentes em vários formulários dentro de um aplicativo. Os Frames podem ser encarados como objetos. Podemos criar variáveis públicas que podem atuar como propriedades, além de métodos e eventos tornando assim os Frames mais genéricos e poderosos. Ex: Vamos criar um frame que encapsule as ações mais comuns realizadas em um cadastro, como Inclusão, Alteração, Exclusão, Botões de Navegação,etc. Isto é muito útil pois em aplicações profissionais todas as telas de cadastro costumam (pelo menos deveriam) seguir um padrão, com as mesmas funcionalidades. Crie uma nova aplicação. O Objetivo será criar 2 telas de cadastro, podemos utilizar o banco NorthWind e criar cadastros para as tabelas products e Regions. Crie uma tela principal (um menu) e adicione 2 botões, um para cada cadastro. Agora vamos criar o Frame: File | New | Other | Delphi Files | Frame Deixe o frame com a seguinte aparência: Adicione à clausula USES de nosso frame as seguintes units: ADODB, DB. Elas serão necessárias para a manipulação de objetos ADO e TDataSet. 80 Primeiro crie uma procedure publica em nosso FRAME. Vá á clausula PUBLIC e adicione abaixo de nossa variável TABELA a seguinte declaração: public { Public declarations } TABELA : TADOQuery; Procedure ControlaBotoes; end; Agora implemente a procedure como segue: procedure TFrameBotoes.ControlaBotoes; begin btNovo.enabled := TABELA.state = dsBrowse; btEdita.enabled := TABELA.state = dsBrowse; btDelete.enabled := TABELA.state = dsBrowse; BtFirst.enabled := TABELA.state = dsBrowse; btPrior.enabled := TABELA.state = dsBrowse; BtNext.enabled := TABELA.state = dsBrowse; btLast.enabled := TABELA.state = dsBrowse; btOK.enabled := TABELA.state <> dsBrowse; btCancel.enabled := TABELA.state <> dsBrowse; end; Chegou a hora de utilizar esta tralha toda! No nosso programa de cadastro de produtos, coloque o seguinte código no evento OnStateChange do objeto TDATASOURCE: procedure TForm1.dsProductsStateChange(Sender: TObject); begin FrmBotoes.ControlaBotoes; end; onde FRMBOTOES é o nome da instância do FRAME no formulário. Pronto! Execute a aplicação e veja como ficou! Agora faça o mesmo no cadastro de regiões. Se você quiser incluir novos botões ao Frame, como por exemplo um botão para Fechar a tela é só incluir no FRAME principal e programá-lo. Todas as telas que instanciaram o Frame serão atualizadas automaticamente. Os arquivos completos podem ser encontrados nos arquivos das aulas. 81 OBJETO TFIELD Os campos de um Dataset são sempre representados por componentes invisíveis, derivados da classe TFIELD. O acesso ao valor corrente de um campo pode ser feito através de métodos e propriedados do TField associado ao campo. Os objetos que representam o campo não são da classe TFIELD, mas de classes derivadas dele. Eles são chamados de TFields, mas a classe específica a que pertencem depende do tipo do campo associado, definido na criação da tabela. TADTField TDateField TReferenceField TAggregateField TDateTimeField TSmallIntField TArrayField TFloatField TSQLTimeStampField TAutoIncField TFMTBCDField TStringField TBCDField TGraphicField TTimeField TBinaryField TGuidField TVarBytesField TBlobField TIDispatchField TVariantField TBooleanField TIntegerField TWideStringField TBytesField TInterfaceField TWordField TCurrencyField TLargeintField TDataSetField TMemoField Exemplos de descendentes da classe TFIELD Existem 2 formas de criar esses componentes:  Criação dinâmica, feita automaticamente pelo Delphi;  Criação de TFields persistentes, feita durante o projeto, através do FieldsEditor. A Classe TFIELD possui diversas propriedades. Veja as mais utilizadas retiradas do Help do Delphi 2005: Properties of TField Alignment Determines how the field's data is displayed within a data-aware control. AsBCD Represents the field's value as a TBcd value. AsBoolean Represents the field's value as a boolean value. AsCurrency Represents the field's value as a Currency value. AsDateTime Represents the field's value as a TDateTime value. AsFloat Represents the field's value as a do uble value. AsInteger Represents the field's value as a 32-bit integer. AsSQLTimeStamp Represents the field's value as a TSQLTimeStamp. AsString Represents the field's value as a string (Delphi) or an AnsiString (C++). AsVariant Represents the Value of the field as a Variant. 82 Calculated Determines whether the value of the field is calculated by the OnCalcFields event handler of its dataset. CanModify Specifies whether a field can be modified. DataSet Identifies the dataset to which a field component belongs. DataType Identifies the data type of the field component. DisplayLabel Contains the text to display in the corresponding column heading of a data grid. DisplayName Represents the name of the field for display purposes. DisplayText Represents the field's value as it is displayed in a data-aware control. DisplayWidth Specifies the number of characters that should be used to display a field's value by a cooperating data-aware control. EditMask Contains a mask that restricts the data that can be entered into a data field. FieldKind Indicates whether a field represents a column in a dataset, a calculated field, or a lookup field. FieldName Indicates the name of the physical column in the underlying table or query result to which a field component is bound. FieldNo Indicates the ordinal position of the field's column in the underlying table or query result. IsNull Indicates whether the field has a value assigned to it. KeyFields Identifies the field or fields in the dataset that must be matched in a lookup dataset when doing a lookup. Lookup Determines whether the field is specified as a lookup field. LookupCache Determines whether the values of a lookup field are cached or looked up dynamically every time the current record in the dataset changes. LookupDataSet Identifies the dataset used to look up field values. LookupKeyFields Identifies the field or fields in the lookup dataset to match when doing a lookup. LookupList Indicates a cache of values from the LookupDataSet indexed by a set of values from the KeyFields property. LookupResultField Identifies the field from the lookup dataset whose value becomes the Value property of the field component. NewValue Represents the current value of the field component including pending cached updates. OldValue Represents the original value of the field (as a Variant). 85 VALIDANDO ATRAVÉS DO EVENTO ONVALIDADE DOS OBJETOS TFIELD Os componentes TField permitem que se faça uma validação através do evento OnValidate. A forma como o Delphi sabe se tudo está OK é através de uma exceção. Para notificar o Delphi que o valor não deve ser aceito, basta disparar uma exceção O interessante de se realizar validações neste evento é que o usuário não consegue passar para o próximo campo enquanto não corrigir o problema no campo atual. Isso é extremamente interessante quando se está editando em um componente do tipo DBGrid. Este evento é disparado sempre que o valor do campo for alterado. Via código ou via interferência do usuário. Ex: procedure TForm1.quClienteNomeValidate(Sender: TField); begin if length(sender.asString) < 3 then begin showmessage('O nome deve ter pelo menos de 3 letras'); abort; end; end; No código acima podemos observar algumas coisas importantes: • O parâmetro SENDER : TFIELD representa o campo ao qual este evento foi associado, neste caso ao campo Nome facilmente identificado pelo próprio nome da procedure. • Poderíamos ter utilizado diretamente o nome do campo, como veremos mais adiante • A exceção é gerada no comando ABORT; que gera uma exceção silenciosa. Outra forma que produziria o mesmo resultado seria: procedure TForm1.quClienteNomeValidate(Sender: TField); begin if length(quClienteNome.AsString ) < 3 then begin raise exception.create('O nome deve ter pelo menos de 3 letras'); end; end; Neste caso utilizamos diretamente o nome do componente TFIELD quClienteNome e geramos a exceção através do comando raise. 86 CAMPOS CALCULADOS Campos calculados são campos pertencentes ao Dataset mas que não pertencem à tabela original. Eles são usados para exibir valores calculados dinâmicamente, geralmente com base no valor de outros campos do Dataset. O valor destes campos é calculado através do evento onCalcFields do objeto TDataset. Mas cuidado: Este evento é executado com muita freqüência portanto seu código deve ser pequeno e eficiente. Na execução deste evento o Delphi muda o estado do Dataset para CalcFields e então podemos atribuir valores apenas aos campos calculados. Vamos ver um exemplo: Para este exemplo utilizaremos a tabela Products do banco NorthWind do SqlServer. Nesta tabela temos um cadastro de produtos.Utilizaremos 2 campos em particular: UnitsInStock e UnitPrice. O primeiro indica a quantidade de produtos no estoque e o segundo o preço unitário. Imagine que seja necessário exibir em uma grade o Saldo no estoque, um valor calculado resultante da Fórmula: SaldoNoEstoque = UnitsInStock * UnitPrice. Este campo saldo não existe na tabela física, portanto este é um caso típico onde podemos utilizar com eficácia os campos calculados. É fato que neste exemplo poderíamos resolver o problema colocando na própria instrução SQL o novo campo como: ‘Select UnitsInStock e UnitPrice as “SaldoNoEstoque”, ...”, porém imagine que esses campos fossem editáveis na grade, o que nos obrigaria a recalculá-los assim que o usuário alterasse algo. Crie uma tela com um dbGrid, TAdoConnection, TADOQuery e um TDataSource, ligue-os e na instrução SQL do TADOQuery digite: ‘select productName, ProductId, unitPrice, unitsInStock, categoryId from products’ sem os apóstrofos é claro! Agora clique com o botão direito sobre o objeto TAdoQuery e adicione todos os campos “ADD ALL FIELDS”. Assim que adicionar todos os campos clique novamente com o botão direito e escolha a opção “NEW FIELD”. A tela à esquerda deverá surgir: Nesta tela podemos preencher as propridades do campo calculado, como o nome, tipo, tamanho, etc. Preencha-a como a da imagem. Observe que a opção CALCULED deve ser selecionada. Clique em OK e o campo aparecerá no Fields Editor. Agora precisamos programar o evento onCalcFields do objeto TADOQuery. Para isso selecione o objeto e dê um duplo clique neste evento. Em sua programação digite: Dataset['SaldoNoEstoque'] := DataSet.FieldByName('unitPrice').asFloat * DataSet.FieldByName('unitsInStock').asFloat ; 87 Durante o desenvolvimento sua tela deverá se parecer com a da figura abaixo: Após executar o programa, o Delphi irá executar o evento onCalcFields para cada registro. Não esqueça de abrir a conexão e a Query no evento onShow e fechar no onClose!!! Sua tela deverá se parecer com a tela abaixo: Os campos calculados podem ser utilizados também para dar significado à campos codificados, como por exemplo o campo Sexo, que só armazena ‘M’ ou ‘F’ mas deve exibir ‘Masculino’ ou ‘Feminino’. 90 O EVENTO ONGETTEXT O evento onGetText pertence a classe TFIELD e é utilizado para mascarar o conteúdo de um campo, exibindo um valor diferente do originalmente armazenado. Ele pode ser muito útil para valores codificados. Exemplo: um campo SEXO que só armazena ‘M’ ou ‘F’ mas deve exibir ‘Masculino’ ou ‘Feminino’. Isso também pode ser feito com campos calculados, porém neste caso teríamos que criar um novo campo, já com a utilização evento OnGetText não precisamos criar um novo campo pois o novo valor será exibido no mesmo campo onde a informação está contida! Ex: Para este exemplo utilizaremos o mesmo exemplo utilizado no tópico CAMPOS LOOKUP. Porém, para a realização deste exemplo não são necessários campos Lookup ou Calculados. Nosso objetivo aqui será fazer com que o campo Discontinued apresente em vídeo os valores SIM para (1) e Não para (0). Porém veja que o campo discontinued é do tipo Bit e o Delphi reconhece este tipo como sendo BOOLEAN, portanto deveremos tratar como True ou False ao invés de 1 e 0. Roteiro: 1. Primeiro, insira mais um campo à instrução SQL da query quProducts : discontinued. Este campo armazena os valores 0 ou 1 para indicar se o produto em questão foi discontinuado (true) ou não (false); 2. Agora, crie o campo em tempo de projeto clicando com o botão direito no Fields Editor e selecionando a opção ADD ALL FIELDS; 3. Ainda no Fields Editor, selecione o novo campo adicionado (Discontinued) e programe o evento onGetText da seguinte forma: procedure TForm1.quProductsdiscontinuedGetText(Sender: TField; var Text: string; DisplayText: Boolean); begin if sender.AsBoolean = true then // pode ser sender ou então quProductsdiscontinued Text := 'Sim' else Text := 'Não'; end; 4. O parâmetro TEXT indica o conteúdo que deve ser exibido para o campo passado no parâmetro SENDER. 5. Execute o programa e veja como ficou. Antes de processar o evento onGetText Após processar o evento onGetText 91 O EVENTO ONSETTEXT O evento onSetText pertence a classe TFIELD e é utilizado para gravar um valor diferente daquele exibido nos componentes Data Controls. Sempre que utilizarmos o evento onGetText e a tabela em questão por passível de alterações, como em um cadastro, onde temos edição e inserção, devemos programar também o evento onSetText. No exemplo dado no tópico onGetText, alteramos o valor de exibição do campo Discontinued de true para Sim e de false para Não. Se o usuário fosse realizar qualquer alteração neste campo ele informaria também Sim ou Não, mas nunca True ou False. Porém, ao efetivarmos as alterações na tabela física precisamos gravar os valores corretos. Neste caso, não podemos gravar Sim tão pouco não! Sendo assim, programamos no evento onSetText o que deve ser gravado para cada opção foi utilizadano GetText. Para completarmos o exemplo dado no item “O EVENTO ONGETTEXT” vamos programar o evento onSetText do campo Discontinued através do Fields Editor. Deixe-o assim: procedure TForm1.quProductsdiscontinuedSetText(Sender: TField; const Text: string); begin if Text = 'Sim' then sender.value := true else sender.value := false; end; Onde SENDER representa o campo que contém o dado, no caso quProductsdiscontinued. O parâmetro TEXT representa o valor que é exibido para o campo através do onGetText. TABELAS TEMPORÁRIAS COM O TCLIENTDATASET O objeto TClientDataSet permite a criação de tabelas temporárias (em memória) de forma muito simples. Sendo ela descendente da classe TCustomClientDataset( que descende de TDataSet), podemos realizar todas as operações básicas de tabelas. As tabelas temporárias podem ser utilizadas como fazíamos nos vetores de registro, porém sua utilização é mais fácil visto que ela funciona exatamente como uma tabela: permite inclusões, alterações, etc. Permite até que os dados sejam salvos no formato binário ou XML. Precisamos inclusive abri-lá antes de utilizá-la. Inclua no USES de seu projeto (menu VIEW | UNITS e escolha o arq. de projeto) as seguintes bibliotecas: MidasLib e crtl. Caso contrário você terá de disponibilosar o arquivo MIDAS.DLL junto com a sua aplicação. Para criar uma tabela temporária, vá a paleta DATA ACCESS e escolha o componente TClientDataSet. Este componente também é utilizado para acessar dados com a tecnologia do DBEXpress, portanto precisamos fazer umas “mutretas” para que ele também funcione “desconectado” de um banco de dados. 92 Para tanto precisamos criar o objeto via código e este processo só pode ser realizado uma vez. Um bom local para fazer isso é no evento onCreate do formulário pois este só é disparado uma vez. Là inserimos a linha: TabTemporaria.createDataset; Onde TabTemporaria é a tabela temporária da classe TClientDataSet. Se tentarmos abrir a tabela em tempo de programação receberemos o erro: Isso acontece porque como já dissemos anteriormente este componente pode trabalhar conectado à um banco de dados. Para evitar esse erro, clique com o botão direito do mouse sobre o objeto TClientDataSet e escolha a opção Create DataSet. Feito isso podemos utilizar a tabela normalmente. Podemos criar campos, “clonar” os campos de uma outra tabela através da opção “Assign local data”, tudo isso via Fields Editor. Podemos criar um cadastro inteiro, com dbGrid´s, dbEdit´s,etc. e ao final salvar os dados em um arquivo XML para posterior recuperação. Algumas características interessantes do objeto TClientDataSet: • Método EmptyDataSet: Apaga todos os registros da tabela temporária.Ex: ClientDataSet1.EmptyDataSet; • Método SaveToFile: Permite exportar dados no formato XML e binário. Ex: ClientDataSet1. SaveToFile('Dados Exportados.xml', dfXml); • Método LoadFromFile: Permite importar dados no formato XML e binário. Ex: ClientDataSet1.LoadFromFile('Dados Exportados.xml'); • Propriedade IndexFieldNames: Permite mudar o índice da tabela temporária, ordenando-a em tempo de execução. Caso haja mais de um campo, estes devem ser separados por ponto-e- vírgula. Ex: ClientDataSet1.IndexFieldNames := 'data_nascimento;nome'; // indexa por 2 campos 95 CRIANDO UM CADASTRO MASTER - DETAIL Existem diversas formas de se construir um cadastro Mestre-Detalhe ou Máster–Detail. Uma das formas mais simples é aquela que utiliza um cadastro com DBEdit´s para a tabela Master e um objeto DBGrid para os dados da tabela Detail. O faremos aqui utilizando esta estrutura. Vamos criar uma tela para venda de produtos. Teremos um cadastro de Nota e itens da nota. Abaixo listaremos o código das principais telas do sistema. Utilizaremos o banco NorthWind e as tabelas Products e Curstomers. Criaremos mais 2 tabelas, Nota e NotaItem (veja código abaixo). As telas de pesquisa de produtos e pesquisa de clientes não tem nada a não ser a instrução SQL que retorna todos os dados de suas respectivas tabelas, além da configuração do DBGrid. O menu tem apenas uma chamada à tela de Vendas. Basicamente, os passos para a criação deste cadastro são: 1. Utilizando um novo formulário baseado no formulário padrão de cadastro, crie um cadastro para os dados da Nota. 2. Na mesma tela, coloque uma nova query (quItens) e um DbGrid. 3. A abertura da query quItens está condicionada ao evento AfterScroll da tabela Nota. 4. Altere os botões GRAVAR, EXCLUIR e CANCELAR da nota pois agora eles deverão gravar e excluir também os dados da tabela quItens. { -- Script para as tabelas Nota e NotaItem if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[Nota]') and OBJECTPROPERTY(id, N'IsUserTable') = 1) drop table [dbo].[Nota] GO if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[NotaItem]') and OBJECTPROPERTY(id, N'IsUserTable') = 1) drop table [dbo].[NotaItem] GO CREATE TABLE [dbo].[Nota] ( [NotaID] [int] NOT NULL , [nota_data] [datetime] NULL , [customerID] [nchar] (5) COLLATE Latin1_General_CI_AS NULL ) ON [PRIMARY] GO CREATE TABLE [dbo].[NotaItem] ( [NotaID] [int] NOT NULL , [ProductID] [int] NOT NULL , [Qtde] [int] NULL , [Valor_unit] [numeric](18, 2) NULL ) ON [PRIMARY] GO ALTER TABLE [dbo].[Nota] WITH NOCHECK ADD CONSTRAINT [PK_Nota] PRIMARY KEY CLUSTERED ( [NotaID] ) ON [PRIMARY] GO ALTER TABLE [dbo].[NotaItem] WITH NOCHECK ADD CONSTRAINT [PK_NotaItem] PRIMARY KEY CLUSTERED ( [NotaID], [ProductID] ) ON [PRIMARY] GO 96 Hierarquia dos Formulários utilizados no programa. TFmPadrao TFmPadraoPesquisa TfmPesquisaCliente TfmPesquisaProduto TFmPadraoCad TForm TfmMenu TDm TDatamodule TfmCadVendas 97 Código do formulário Padrão – TFmPadrao procedure TfmPadrao.FormClose(Sender: TObject; var Action: TCloseAction); var i : integer; begin for i:=0 to componentcount -1 do if components[i] is TDataSet then (components[i] as TDataSet).close; action := caFree; end; procedure TfmPadrao.FormKeyPress(Sender: TObject; var Key: Char); begin if key = chr(13) then begin if Not(activeControl is TMemo) and Not(activeControl is TDbGrid) then begin selectnext(activeControl, true, true); key := chr(0); end; end; end; Código do Formulário Padrão para Cadastros– TFmPadraoCad
Docsity logo



Copyright © 2024 Ladybird Srl - Via Leonardo da Vinci 16, 10126, Torino, Italy - VAT 10816460017 - All rights reserved