Você está na página 1de 100

Delphi 4.

0 Cliente / Servidor com Oracle


Esta apostila melhor visualizada com resoluo de 800x600 e no mnimo 256 cores.

Direitos Autorais 1997, 1998 para Griaule (razo social: I.A. Pesquisa & Desenvolvimento de Sistemas Ltda.) Nenhuma parte desta publicao poder ser reproduzida ou transmitida, no todo ou em parte, em qualquer forma ou meio, seja eletrnica ou mecnica, seja qual for o propsito, sem a expressa autorizao por escrito da Griaule. Delphi, Borland, ReportSmith, dBASE e InterBase so marcas registradas da Borland International, Inc. Microsoft, MS, Windows, Windows 95, Microsoft SQL Server so marcas comerciais ou marcas registradas da Microsoft Corporation. Paradox uma marca registrada da Ansa Software, uma empresa da Borland. TrueType uma marca registrada da Apple Corporation. Oracle uma marca registrada da Oracle Corporation. Todas as outras marcas e nomes de produtos so de propriedade de seus respectivos portadores. Contedo: Mrcio Pontes Marla C. vila Converso para mdia eletrnica: Renato de A. Martins Atualizao para utilizao do Oracle: Salim Michel Esber

Objetos e classes: revisando


O Delphi utiliza objetos e classes para quase tudo. Componentes e formulrios so objetos, bem como listas de strings, objetos de desenho (Canvas) e outros objetos no visuais. Um objeto composto de campos de dados, que podem conter dados de qualquer tipo bsico (integer, double, string, boolean etc.) e mtodos, procedimentos e funes que manipulam esses dados. Um objeto tambm pode conter propriedades, que primeira vista parecem com um campo de dados normal, mas que geralmente provocam aes quando modificadas. Por exemplo: editNumero.Enabled := True; {propriedade boolean} editNumero.SetFocus; {mtodo procedimento} if Clipboard.HasFormat(cf_text) then

{mtodo funo boolean: HasFormat} s := Clipboard.AsText; {propriedade string}

Classes
Todo objeto pertence a uma classe. A classe define a estrutura e funcionamento de vrios objetos semelhantes. Por exemplo, um componente de edio chamado 'editNumero' um objeto da classe 'TEdit'. Cada objeto chamado tambm de uma instncia da classe. Uma classe tambm um tipo de dados. Se C o nome de uma classe, uma varivel declarada do tipo C uma varivel de objeto, que pode conter referncias a objetos da classe C. Por exemplo, abaixo estamos definindo uma classe chamada TPonto e declarando variveis do tipo TPonto: type TPonto = class x, y: integer; end; var pt1, pt2: TPonto; Cada objeto especfico (como 'pt1' e 'pt2') chamado tambm de uma instncia da classe, criada de acordo com a definio da classe. No exemplo acima, tanto 'pt1' quanto 'pt2' tm a mesma estrutura, cada um com os campos x e y. Todos os objetos de uma mesma classe tm as mesmas caractersticas (propriedades), mas cada um tem seus prprios valores para essas caractersticas. Quando voc cria um objeto, ele comea como uma cpia idntica do modelo definido pela classe. Depois voc pode alterar essas propriedades, diferenciando um objeto do outro. Resumindo, existem trs formas de se pensar em uma classe: Um modelo (ou "frma") usado para criar objetos Um tipo de dados que pode ser usado para declarar variveis Um conjunto de objetos (instncias) que tm a mesma estrutura

Variveis de Objeto
No basta declarar uma varivel de objeto para ter um objeto. A varivel no contm propriamente os dados do objeto, mas sim uma referncia indireta ao objeto (internamente o endereo de memria do objeto, ou um ponteiro para o objeto). O nome da varivel no o nome do objeto, mas sim o nome que denota essa referncia. Uma varivel de objeto pode estar em trs situaes distintas, dependendo do seu escopo: Indefinida: uma varivel local no inicializada contm "lixo", ou seja, dados indefinidos que no referenciam um objeto vlido. Voc deve inicializar essa varivel antes de usar, com nil ou com uma referncia a um objeto vlido. Com valor nil: o valor nil (nada) um valor especial que pode ser atribudo a uma varivel de objeto (ou a um ponteiro). Ele indica que a varivel no aponta para um objeto vlido. Se uma varivel = nil, voc no deve usar os campos do objeto. Variveis de objeto globais da unidade so inicializadas com nil. Referenciando objeto: a varivel contm uma referncia que aponta para um objeto vlido, que foi criado anteriormente. Nesse caso voc pode utiliz-la para trabalhar com o objeto. Voc pode inicializar a varivel, criando um objeto. Para isso, use o mtodo Create (o construtor da classe), com o nome da classe, por exemplo: pt1 := TPonto.Create; {depois de construir, usar o objeto} pt1.x := 10; pt1.y := 20; O construtor aloca um espao de memria para conter os dados do objeto e retorna o endereo dessa memria. Dependendo da classe, o construtor pode ter parmetros usados para inicializar o objeto. Algumas classes tm construtores com outros nomes ao invs de Create. Para liberar a memria usada pelo objeto, use o mtodo Free. Se voc no liberar, o espao de memria do objeto continua alocado, mesmo que no existam mais variveis para acess-lo. Por exemplo: pt1.Free; Quando voc atribui uma varivel de objeto a outra, no feita uma cpia fsica do objeto. A nica coisa copiada a referncia ao objeto. Depois da atribuio, ambas as variveis referenciam o mesmo objeto, por exemplo: pt2 := pt1; Depois do comando acima, os nomes 'pt1' e 'pt2' so sinnimos, pois lem ou alteram o mesmo objeto. Se voc fizer 'pt2.x := 10', ento 'pt1.x' tambm ter o valor 10.

Herana
Quando voc cria uma nova classe, ela baseada em uma classe que j existe. Com isso, a classe herda (recebe por herana) todos os dados e mtodos da outra classe, mas pode acrescentar ou redefinir alguns de acordo com o que necessrio. O mecanismo de herana [inheritance] permite que voc programe apenas o que diferente entre a sua classe e a outra (programao por exceo). A classe da qual so herdadas as caractersticas chamada de classe base ou ancestral [ancestor] ou ainda superclasse e a classe criada chamada de classe derivada ou descendente [descendent] ou ainda subclasse. Herana no uma cpia. Qualquer alterao feita em uma classe ancestral automaticamente repercute nas subclasses. Por exemplo, podemos derivar uma nova classe de TPonto: type TPontoColorido = class(TPonto) cor: integer; end; A classe 'TPontoColorido' tem todos os campos de 'TPonto' (x e y), mesmo no sendo especificado, e tem mais um novo campo 'cor'. Note que a superclasse especificada entre parnteses. Herana tem vrios nveis. Uma subclasse pode ter subclasses e assim por diante. Se voc criar uma classe sem especificar a superclasse, o Delphi assume implicitamente a classe 'TObject'. Essa classe ancestral direta ou indireta de todas as outras. Herana usada em vrias situaes: Componentes: um controle de edio um tipo de controle (a classe TEdit derivada de TControl), controles so um subtipo de componentes (TControl subclasse de TComponent), componentes so um tipo de objetos (TComponent descendente de TObject), etc. Excees: a classe Exception derivada de TObject e ancestral de todas as classes de excees. Cada classe de exceo pode ter subclasses etc. Formulrios: a classe TForm a base para todos as classes de formulrio. Todo formulrio que voc cria tem uma classe TNomeFormulrio, derivada de TForm, que herda sua estrutura, mas acrescenta novos componentes. Como veremos, voc pode criar uma classe de formulrio derivada de uma j existente. AQUI.

Hierarquia de classes
Quando a herana usada sucessivamente, com uma classe derivada tambm tendo classes derivadas, criada uma hierarquia [hierarchy] de classes, que pode ser representada com uma estrutura em rvore. Na raiz da rvore est a classe TObject e abaixo dela as classes derivadas. Voc pode ver a hierarquia das classes do Delphi e do seu programa com o Browser (logo aps compilar o programa, clique em View|Browser).

Classes de formulrio
Formulrios tambm so objetos. Quando voc cria um novo formulrio, o Delphi cria uma classe de formulrio e um objeto de formulrio dessa classe. O nome da classe de formulrio sempre inicia com um T, seguido do nome do objeto de formulrio (que voc define na propriedade Name do formulrio). A vantagem de ter uma classe de formulrio que isso torna possvel criar dinamicamente outros objetos de formulrio, alm do inicial, e alterar suas propriedades. Crie um novo projeto (File|New Application). Voc ver inicialmente o formulrio principal. Altere o nome para 'FormBase'. Coloque um boto com o Caption= 'Fechar' e o nome 'btnFechar'. Clique duas vezes no boto e complete chamando o mtodo Close: procedure TFormPrincipal.btnFecharClick(Sender:TObject); begin Close; end; Na seo de interface da unidade, logo aps a clusula uses, voc ver a declarao da classe de formulrio: type TFormBase = class(TForm) btnFechar: TButton; procedure btnFecharClick(Sender:TObject); private { Private declarations }

public { Public declarations } end; var FormBase: TFormBase; A primeira linha: TFormBase = class(TForm) quer dizer que a classe do formulrio derivada da classe 'TForm'. Dentro da definio da classe (que vai at a palavra end), existe uma declarao para cada componente do formulrio (no caso, apenas o boto 'btnFechar'). Se voc adicionar, remover, ou renomear qualquer componente, o Delphi altera automaticamente essa seo. No nosso caso, temos: btnFechar: TButton onde 'btnFechar' o nome do componente e 'TButton' a classe do componente. Depois das declaraes de componentes, comeam as declaraes para os procedimentos de evento. Para cada procedimento de evento, o Delphi adiciona uma declarao dentro da classe, como: procedure btnFecharClick(Sender: TObject); O corpo do procedimento, que fica na seo de implementao, contm os comandos que implementam realmente o procedimento. O cdigo at a palavra private mantido automaticamente pelo Delphi e voc no deve alter-lo. Voc pode adicionar campos de dados ou mtodos nas sees private ou public. O que fica na seo private acessvel apenas na unidade atual, mas o que for declarado na seo public visvel externamente em outras unidades. O Delphi acrescenta tambm uma declarao de varivel para o formulrio (FormPrincipal: TFormPrincipal). Essa varivel, durante a execuo, ir conter uma referncia a um objeto de formulrio. Se o formulrio estiver na lista "Auto-create forms" (em Project|Options..., pgina 'Forms') ento esse objeto ser criado automaticamente no incio da execuo. Seno, ele deve ser criado explicitamente pelo programa, por exemplo: FormBase := TFormBase.Create(nil);

Herana Visual de Formulrios


Se voc prever que ter vrios formulrios semelhantes no programa, pode fazer todos eles derivados a partir de uma nica classe base (usando o mecanismo de herana). Isso permite: - Padronizar a aparncia e comportamento dos formulrios - Criar as funes padro do programa apenas uma vez (na classe base) em vez de copi-las quando necessrio - Reutilizao melhor do cdigo em outros projetos. Por exemplo, numa aplicao de banco de dados, muitos formulrios trabalham com apenas uma tabela e so bem semelhantes entre si. Vejamos como podemos usar as caractersticas comuns entre eles. Salve o projeto e d o nome de BASE.PAS para a unidade e ALMOXARIFADO.DPR para o projeto.

Criando uma classe de formulrio derivada


Lembre-se que quando voc criou o FormBase, foi criada uma classe de formulrio chamada 'TFormBase'. Vamos criar agora uma classe derivada desta. Para fazer isso, clique em File|New.... Voc ver o repositrio do Delphi, um conjunto de itens disponveis na criao de um novo projeto. No repositrio, sempre estar disponvel uma pgina com o nome do Projeto atual, no caso "Almoxarifado". Clique nesta pgina e selecione o formulrio "FormBase", como na figura abaixo: Clique no boto 'Ok' . Agora voc ver um formulrio 'FormBase1', que inicialmente tem as mesmas propriedades do 'FormBase' (exceto Name e Caption). Ele recebe como herana o boto 'btnFechar' e seu funcionamento.

Alterando propriedades
Coloque os dois formulrios lado a lado na tela e faa algumas experincias. Primeiro no 'formbase' altere a propriedade Caption do boto "Fechar" para "Fechar agora". Note que isso vai alterar simultaneamente o boto "Fechar" no formulrio derivado. Mova o boto "Fechar agora" no formulrio base e o boto tambm se move no formulrio derivado. O que acontece que todas as propriedades do boto "Fechar" so herdadas pelo formulrio derivado. Qualquer alterao na classe base automaticamente herdada. Mas se voc alterar propriedades no formulrio derivado, isso no acontece. Clique no formulrio derivado e altere a propriedade Caption do boto "Fechar agora" para "Fechar". Isso no vai afetar o original. E agora, se

voc voltar ao original e alter-lo, isso no afeta o derivado. Tambm se voc mover o boto no formulrio derivado, a sua posio (as propriedades Left e Top) no estar mais vinculada ao original. Para cada propriedade de cada componente no formulrio derivado, se voc alter-la, ela perde o vnculo com o formulrio original. Se voc quiser vincular a propriedade novamente, clique sobre ela (no caso, Caption do boto "Fechar") com o boto direito e clique em Revert to inherited [reverter ao herdado]. Se voc clicar sobre o componente e escolher Revert to inherited, ento todas as propriedades sero revertidas (inclusive Left e Top, que determinam a posio e Width e Height, que determinam o tamanho). Note tambm que: - Se voc acrescentar, excluir ou renomear um componente ao FormBase, ele ser acrescentado, excludo ou renomeado no formulrio derivado. - No formulrio derivado, voc no pode excluir ou renomear um componente que foi herdado do FormBase, s os que voc tenha acrescentado na prpria classe.

Criando bancos de dados e tabelas


Para o nosso exemplo, vamos criar um alias de banco de dados e algumas tabelas nesse banco de dados. Usaremos o formato Paradox. Clique em Database|Explore e crie um novo alias de banco de dados, usando o drive 'STANDARD' , com o nome 'Curso', e no seu parmetro PATH= coloque o caminho dos arquivos do curso. Usando o Database Desktop, crie duas tabelas com os seguintes nomes e estrutura: tabela Produto.db Campo Tipo Tamanho Required Field CodProduto S(short) X Nome A(alpha) 50 QuantEstoque S(short) QuantMinima S(short) CodFornecedor S(short) tabela Fornecedor.db Campo Tipo Tamanho Required Field CodFornecedor S(short) X Nome A(alpha) 50

Usando um formulrio padro de banco de dados


Acrescente os seguintes componentes no FormBase: - um componente Table, que acessa um alias de banco de dados - um componente DataSource, conectado ao Table - um componente DBNavigator, conectado ao DataSource Disponha os componentes como na figura abaixo: Altere os nomes dos componentes para: tblGenerico (), dsGenerico () e dbnGenerico (o DBNavigator). No componente Table, 'tblGenerico', em DatabaseName, coloque o nome do alias, 'Curso'. Em 'dsGenerico', faa a propriedade DataSet = 'tblGenerico'. Em 'dbnGenerico', faa a propriedade DataSource= 'dsGenerico'. Agora vamos criar um procedimento de evento no formulrio. Selecione o evento OnShow e clique duas vezes para criar o procedimento. Complete com o seguinte: procedure TFormBase.FormShow(Sender:TObject); begin tblGenerico.Open; end; A funo desse procedimento ser abrir a tabela sempre que o formulrio for mostrado na tela.

Alterando as classes derivadas


Retorne ao formulrio 'FormBase1'. Vamos faz-lo acessar a tabela de produtos. Altere o nome do formulrio para 'FormProduto', o Caption para "Produtos" e salve a unidade como DPRODUTO.PAS (D de derivado). Clique em 'tblGenerico' nesse formulrio. Altere a propriedade TableName, selecionando 'PRODUTO.DB'. Depois clique duas vezes sobre o componente para abrir o editor de campos. Clique com o boto direito, selecione Add Fields..., mantenha todos os campos e clique Ok. No editor de campos, mantenha todos os campos selecionados (CodProduto, Nome, ..., QuantEstoque) e arrasteos com o mouse, soltando-os sobre o formulrio de produtos. Organize os controles de edio da forma que achar melhor.

Agora para criar o formulrio de fornecedores, faa um processo semelhante. Primeiro, para criar o formulrio, clique em File|New..., clique na pgina "Almoxarifado", escolha "FormBase" e clique Ok. Com isso voc tem uma nova classe derivada de 'TFormBase'. No formulrio novo, altere o nome 'FormFornecedor', o Caption para "Fornecedores" e salve a unidade como DFORNECEDOR.PAS. Clique no componente Table, altere a propriedade TableName para 'FORNECEDOR.DB'. Depois clique duas vezes sobre o componente para abrir o editor de campos, adicione todos os campos e arraste-os para o formulrio. Para chamar esses formulrios, vamos criar um menu principal . Crie um novo formulrio, altere o nome para 'formPrincipal', a propriedade Caption 'Menu Principal'. Coloque um componente MainMenu() no formulrio e clique duas vezes nele para criar um menu. Como iremos utilizar este Menu para chamar outros exemplos que sero realizados durante o curso, iremos acrescentar outros items , para implementao posterior.Crie a seguinte estrutura de menus: Caption Name Shortcut &Cadastro &Pessoas P&rodutos &Fornecedores &Sair &Consulta mnuCadastro mnuCadPessoa N1(Separador) mnuCadProduto mnuCadFornecedor mnuCadSair mnuConsulta Ctrl+ R Ctrl+ P

&Produto mnuConsProduto Reduza o tamanho do formulrio na vertical, ou a propriedade Height, de forma que ele no ocupe muito espao na tela, deixando apenas a barra de menu invisvel. Agora clique no menu em Cadastro|Sair . Vamos chamar o mtodo Close, para fechar o formulrio e salve o formulrio como MENU.PAS. Para chamar o formulrio 'formProduto no Menu, no procedimento de evento OnClick , do menu Cadastro| Produtos , acrescente o seguinte comando : procedure TformPrincipal.mnuCadProdutosClick(Sender: TObject); begin formProduto.show end; Para o Fornecedor, clique no menu Cadastro|Fornecedores e, no procedimento de evento, coloque o comando a seguir: procedure TformPrincipal.mnuCadFornecedorClick(Sender: TObject); begin formFornecedor.show end; Para isso funcionar , voc precisa adicionar uma clusula uses, no incio da seo de implementao do formulrio. Adicione o seguinte: Uses DProduto, DFornecedor; Como o 'formBase' uma classe que ser utilizada para criar outros formulrios, no precisamos que durante a execuo do projeto seja criado um objeto para esta classe , portanto, iremos retir-la da lista de 'auto-create' do delphi. Para isto clique no menu Projetc|options, ir aparecer uma janela como a figura abaixo: Para retirar o 'formBase' da lista de 'auto-create' , clique neste formulrio na opo 'Auto-create forms' , em seguinte clique no boto (), e este formulrio ir ficar em 'Available forms', com isso , no ser o objeto para o formulrio durante a execuo do programa.Mude tambm a opo 'Main Form' para 'formPrincipal', deste forma ele ser o primeiro formulrio a ser mostrado no projeto. Cadastre alguns produtos e fornecedores.

Repositrio
O repositrio do Delphi um local para armazenamento de objetos, como formulrios e projetos, que facilita o compartilhamento desses objetos por vrios projetos. Quando voc clica em File|New... para criar um novo objeto, voc pode escolher um dos itens do repositrio.

Acrescentando um formulrio ao repositrio


Abra o projeto ALMOXARIFADO.DPR, e dentro dele o formulrio "FormBase" (base.pas). Esse formulrio est sendo compartilhado dentro do mesmo projeto, mas se voc quiser reutiliz-lo em outros projetos, pode acrescent-lo ao repositrio. Para isso, clique com o boto direito e em Add to Repository. Voc deve informar o ttulo do item, uma descrio e qual a pgina onde ele ser inserido (se voc digitar um nome de pgina que no existe, uma nova ser criada), alm do seu nome para indicar qual o autor desse item. Opcionalmente voc pode escolher um cone que ser usado para representar o item. Para o exemplo digite o seguinte: Title: Formulrio para uma tabela Description: Formulrio de banco de dados para uma tabela Page: Forms (default) Author: seu nome O boto "Browse" permite voc procurar um cone , para representar sua classe de formulrio, caso no informe ele ir mostrar o seguinte cone . Clique Ok. O novo item ser adicionado ao repositrio. Agora crie um novo projeto com File|New Application e veremos como o item pode ser reutilizado.

Formas de criar novos objetos


Se voc clicar em File|New... e na pgina "Forms", voc ver que o novo item ("Formulrio para uma tabela") est dentro do repositrio. Agora ele est disponvel em qualquer projeto, permitindo criar novos formulrios a partir dele. Existem trs opes para criar novos objetos a partir do repositrio: copiar [copy] o item, herdar [inherit] do item (criar uma nova classe derivada) ou usar [use] o item diretamente. Se voc marcar a opo "Copy" e clicar Ok, o Delphi cria uma cpia separada do objeto original que est no repositrio. Qualquer alterao no original posteriormente no vai afetar essa cpia e qualquer alterao na cpia independente do que est no repositrio. Note que a unidade do formulrio no foi salva e est em memria como "Unit2". Isso permite voc salvar com um nome qualquer. A opo "Inherit" (herdar) faz diferente: faz uma referncia ao original (ou seja, acrescenta o formulrio original dentro do projeto) e cria uma nova classe derivada da original, TFormBase. O novo formulrio ser chamado de 'FormBase1', com a classe 'TFormBase1'. A unidade do formulrio no foi salva ainda. Nesse caso, qualquer alterao no original, que est no repositrio, ser herdada pela classe derivada. A opo "Use" no copia o objeto original, mas compartilha com o projeto atual. Nesse caso, alteraes feitas no item dentro do projeto afetam o item no repositrio e vice-versa. Se vrios projetos usarem o mesmo item, todos eles compartilham o mesmo item.

Adicionando um projeto ao repositrio


Para reutilizar um projeto inteiro, voc pode acrescent-lo ao repositrio com o menu Project|Add to Repository... e informar a descrio do item como antes.

Gerenciando o repositrio
Para gerenciar as pginas do repositrio e os itens de cada pgina, voc pode clicar em Tools|Repository. Na caixa de dilogo voc pode criar, renomear ou excluir uma pgina do repositrio (voc s pode excluir se ela estiver vazia). possvel tambm mudar a ordem das pginas. Voc pode mover os itens entre pginas arrastando o item da lista da esquerda e soltando sobre a pgina desejada. Tambm possvel renomear ou alterar caractersticas de um item ou excluir o item.

Compartilhando o repositrio numa equipe


Quando uma equipe de desenvolvedores trabalha em conjunto, importante que eles possam compartilhar o repositrio, de forma que novos itens adicionados a ele estejam disponveis para toda a equipe. Para compartilhar o repositrio, voc deve usar um diretrio na rede que seja acessvel a todos os desenvolvedores. Por exemplo, se G: uma letra de drive que aponta para a rede, voc pode usar G:\REPOS. Voc deve tambm copiar os arquivos do repositrio do Delphi para esse diretrio. Os arquivos do repositrio do Delphi so armazenados num subdiretrio OBJREPOS, abaixo do diretrio onde o Delphi foi instalado (geralmente C:\Arquivos de Programas\Borland\Delphi 4). Alm desses arquivos, o Delphi usa um arquivo de texto chamado DELPHI32.DRO, localizado no subdiretrio BIN do Delphi. Para compartilhar o repositrio na rede, faa o seguinte: Crie um diretrio compartilhado (e.g. G:\REPOS). Verifique que todos os desenvolvedores tm acesso a ele e usam a mesma letra de drive;

Copie o repositrio de um computador para o diretrio compartilhado (G:\REPOS), ou seja, todos os arquivos do subdiretrio OBJREPOS do Delphi, mais DELPHI32.DRO, do subdiretrio BIN; Em cada um dos computadores, no Delphi, clique em Tools|Environment Options. Na pgina Preferences Em "Shared Repository", digite o caminho do diretrio compartilhado. Agora lembre-se que qualquer alterao ou acrscimo feito por um desenvolvedor afeta todos os outros. importante tambm notar que quando voc quiser compartilhar um objeto, ele deve ser salvo num diretrio compartilhado tambm, acessvel a todos (por exemplo, G:\BIBLIOTECAS).

Criando componentes dinamicamente


Componentes so objetos e qualquer objeto pode ser criado dinamicamente durante a execuo do programa. Um componente deve ser criado em um formulrio (ou mdulo de dados, para componentes no visuais). Para vermos como criar componentes dinamicamente, crie um novo projeto.

Exemplo: criando componentes


Nesse projeto, quando for clicado o mouse no formulrio, vamos criar um componente TButton na posio do cursor. No formulrio, altere o nome para FormCriaComp e o ttulo para "Criao de componentes". Coloque um controle Label com Caption="Texto" e um controle Edit com o nome de 'editTexto'. No evento OnMouseDown do formulrio, vamos criar um componente na posio onde foi clicado o mouse. Crie um procedimento nesse evento e faa o seguinte: procedure TFormCriaComp.FormMouseDown( Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); var botao:TButton; begin botao := TButton.Create(Self); with botao do begin Parent := Self; Caption := editTexto.Text; Left := X; Top := Y; Visible := True; end; end; Quando voc cria um componente, usa o construtor Create da classe TComponent. Esse construtor exige como parmetro o formulrio onde o componente ser criado. No caso, passamos 'Self' (o formulrio atual), que o mesmo que 'FormCriaComp' nesse caso. O formulrio 'Self' ser o dono [owner] do componente. Mas no caso de um controle (componente visual) preciso dizer tambm quem ser o pai [parent] desse controle. Se for o prprio formulrio, o controle fica sobre o formulrio. Mas se houver outro controle que capaz de conter controles, o pai pode ser esse outro controle. A propriedade 'Parent' de um componente determina qual o pai dele. Depois o Caption tirado do texto digitado em 'editTexto', e a posio do novo componente (propriedades Left e Top) definida como o ponto onde foi clicado o mouse no formulrio. A propriedade Visible faz com que o componente aparea no formulrio. Salve o projeto como CRIACOMP.PAS e CRIACOMPP.DPR . Execute o programa. Para testar, digite um texto, que ser o Caption do rtulo. Depois clique no formulrio e um boto ser criado.

Detalhes de tratamento de eventos


Um evento uma forma que um componente tem de notificar o formulrio de algum acontecimento. Se voc clica com o mouse em um boto, por exemplo, ele deve notificar o formulrio desse clique, para que o cdigo do formulrio possa fazer alguma coisa em resposta. Quando o componente aciona um evento, ele executa um procedimento de evento, um procedimento que faz parte da classe de formulrio. Quando voc cria um procedimento na pgina de eventos, voc est associando um evento de um componente com um procedimento de evento do formulrio.

Eliminando um Procedimento de Evento


Se voc quiser eliminar um procedimento de evento, no apague o corpo dele no cdigo. Apague apenas os comandos entre o begin e o end, e declaraes de variveis (var), se houver. Quando voc compila o programa, ou quando salva o projeto, o Delphi nota que o procedimento est vazio, e o remove automaticamente do programa.

Nomes de procedimentos
O nome de um procedimento de evento usado para declarar esse procedimento na classe do formulrio. Pode ser qualquer nome, desde que siga as regras de identificadores em Delphi. Quando voc cria um procedimento de evento, clicando duas vezes no componente ou em um evento qualquer, o Delphi atribui um nome padro, da forma NomeComponenteNomeEvento. Se voc tem um boto chamado btnFechar no formulrio, e clicar duas vezes, o Delphi cria um procedimento chamado 'btnFecharClick' (note que o On omitido): procedure TForm1.btnFecharClick(Sender:TObject)' begin end; Para mudar o nome do procedimento, clique na pgina de eventos do Object Inspector, no evento desejado e digite o novo nome. Mas voc no deve mudar o nome diretamente no texto da unidade. Se fizer isso, o Delphi no conseguir localizar mais o procedimento e mostrar vrias mensagens de erro. O nome declarado em vrios lugares: na declarao da classe de formulrio, na pgina de eventos e na unidade. Se voc alterar em um dos lugares, tem que alterar todos.

Associando um Procedimento Existente


Se depois de criar um procedimento, voc criar um novo componente que vai fazer a mesma coisa, voc pode lig-lo a um procedimento j existente. Por exemplo, um boto e um item de menu que estejam associados mesma ao. Para fazer isso, selecione o componente que voc quer associar, clique no evento e no boto de seta . Aparece na lista os nomes dos procedimentos compatveis com aquele evento. Um procedimento compatvel se ele tem exatamente o mesmo nmero de parmetros e o mesmo tipo de dados e passagem para cada parmetro correspondente. Para desvincular um procedimento do componente, clique no nome do evento e tecle [Delete]. O componente no vai mais chamar aquele procedimento.

Identificando qual controle acionou o evento


No Delphi voc pode associar um mesmo procedimento de evento com dois ou mais componentes. Se voc selecionar vrios componentes e criar um procedimento de evento, ele estar associado dessa forma. O problema saber qual dos componentes acionou o evento. O Delphi fornece essa informao para o procedimento atravs do parmetro 'Sender'. Essa varivel uma referncia ao componente que acionou o evento, como se fosse um nome diferente para o componente dentro do procedimento, e atravs dele podemos acessar as propriedades e mtodos do componente. 'Sender' declarado como 'TObject', o que quer dizer que ele um objeto genrico do Delphi, que pode ser um componente qualquer. Se voc quiser acessar uma propriedade especfica de um componente, por exemplo, Color, precisa dizer ao Delphi que temos certeza que 'Sender' um componente Edit. Por exemplo: (Sender as TEdit).Color := clYellow; A expresso Sender as TEdit fora o Delphi a tratar 'Sender' como um objeto da classe TEdit, ou seja, um componente Edit. Usando essa expresso, podemos acessar propriedades do componente. Outra sintaxe poderia ser usada: with Sender as TEdit do Color := clYellow;

Associando um Procedimento em Tempo de Execuo


A associao entre procedimentos e eventos pode ser feita tambm durante a execuo 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 com o procedimento "EditRecebeuFoco", basta usar o seguinte comando: editOperando1.OnEnter := EditRecebeuFoco;

Tratando Eventos com Componentes Dinmicos


Um componente criado dinamicamente no tem nenhum tratamento de eventos definido, mas como eventos podem ser alterados como propriedades, voc pode criar um procedimento de evento manualmente e durante a execuo, associar esse procedimento ao componente. Para isso abra o projeto CRIACOMPP, caso ele no esteja aberto. Dentro da definio da classe do formulrio, na parte private (ou public, no importa no caso), acrescente uma declarao de mtodo: procedure TrataClick(Sender:TObject); Agora, na seo de implementao, crie o corpo do mtodo 'TrataClick', da seguinte forma: procedure TFormCriaComp.TrataClick(Sender:TObject); begin ShowMessage('Voc clicou no boto '+ (Sender as TButton).Caption); end; Esse mtodo assume que 'Sender' um componente da classe TButton, e mostra uma mensagem com a propriedade Caption do Sender. A cada vez que criarmos um componente, vamos associar esse mtodo ao evento OnClick do componente. Para isso, volte ao procedimento 'FormMouseDown' e acrescente a linha abaixo depois de Visible := True: OnClick := TrataClick; Quando voc usa o nome do evento como uma propriedade, o valor atribudo deve ser o nome de um mtodo do formulrio. Agora, quando voc clicar em um dos botes criados, o procedimento 'TrataClick' ser executado e vai mostrar o Caption desse boto numa mensagem. Nota: a varivel OnClick do tipo procedure, um tipo de dados que permite fazer referncia a procedimentos. Veremos isso em detalhe posteriormente.

Criando formulrios dinamicamente


Um formulrio, sendo um objeto, tambm pode ser criado dinamicamente. Na verdade, para cada formulrio que voc desenha na tela, o Delphi cria uma classe de formulrio, como j vimos. Se o nome do formulrio FormExemplo, o nome da classe TFormExemplo. Voc pode criar objetos de formulrio em tempo de execuo com a sintaxe: form := TFormExemplo.Create(Application); O parmetro do construtor Create pode ser nil, pode ser o objeto Application do Delphi, ou pode ser um outro formulrio, dependendo da situao. Voc pode criar vrias instncias da mesma classe de formulrio, que sero objetos distintos durante a execuo do programa.

Auto-criao de formulrios
Para cada classe de formulrio do projeto, esse formulrio pode ser auto-criado [auto-created] no incio da execuo ou no. Se o formulrio 'FormExemplo' for auto-criado, significa que a varivel de formulrio FormExemplo j inicializada com um objeto da classe TFormExemplo. Isso feito no arquivo do projeto (.DPR), com comandos do tipo: Application.CreateForm(TFormExemplo, FormExemplo); mais fcil fazer um programa com todos os formulrios auto-criados, porque todos os formulrios estaro disponveis para serem chamados a qualquer momento. Em compensao todos os formulrios estaro ocupando memria no incio da execuo, mesmo aqueles que esto invisveis e no esto sendo usados. Se voc desativar auto-criao para uma classe de formulrio, significa que antes de chamar o formulrio voc deve criar um objeto de formulrio e referenci-lo com alguma varivel (provavelmente a prpria varivel FormExemplo). Mas a vantagem de fazer isso que o incio da execuo do programa leva bem menos tempo. Nas opes do projeto voc pode determinar para cada formulrio se ele ser auto-criado ou no. O formulrio principal do projeto o primeiro formulrio da lista "Auto-create forms". Logo deve haver pelo menos um formulrio nessa lista ou o projeto no tem formulrio principal.

Alterando a opo "auto-create"


Vejamos o que deve ser alterado num projeto para no usar auto-create. Abra o projeto ALMOXARIFADO.DPR criado anteriormente. Clique em Project|Options, na pgina 'Forms'. Na lista da esquerda, "Auto-create forms", clique no 'FormProduto', segure a tecla [Ctrl] enquanto clica em 'FormFornecedor' e clique no boto [>] para mover os formulrios para a direita. Se 'FormBase' estiver do lado esquerdo, faa o mesmo com ele.

Agora o nico formulrio auto-criado o formulrio principal, 'FormPrincipal'. Clique Ok e abra esse formulrio. Abra o procedimento de evento do item Cadastro|Produtos do menu. Antes de chamar o 'FormProdutos', vamos acrescentar um comando para criar o formulrio: procedure TformPrincipal.mnuCadProdutosClick(Sender: TObject); begin formProduto := TFormProduto.Create(Application); formProduto.show end; Faa o mesmo para o item Cadastro|Fornecedores com relao ao 'FormFornecedor': procedure TformPrincipal.mnuCadFornecedorClick(Sender: TObject); begin formFornecedor := TFormFornecedor.Create(Application); formFornecedor.show end; Cada vez que um desses procedimentos chamado, o formulrio correspondente criado e mostrado. Execute o programa e verifique que agora ele inicia muito mais rapidamente. Quando voc clicar em um dos menus, ele pode levar um pouco mais de tempo para abrir cada formulrio. Mas existe ainda um problema. Quando voc clica no boto novamente, ele abre uma nova cpia do formulrio e mantm o antigo. Vamos resolver isso no prximo passo. Outro problema, que no visvel, acontece quando voc fecha o formulrio. O Delphi no destri o objeto de formulrio. Ele mantido em memria, mas invisvel ao usurio. Com isso, se forem criados vrios formulrios, eles nunca so liberados, o que provoca vazamento de memria na aplicao.

Destruindo o formulrio ao fechar


Para evitar o segundo problema acima, precisamos indicar para o Delphi que o formulrio ser destrudo ao ser fechado. Para isso, acrescente um procedimento de evento no FormBase, que ser herdado pelos outros. No evento OnClose, faa: procedure TFormBase.FormClose(Sender:TObject; var Action:TCloseAction); begin Action := caFree; end; O parmetro 'Action' do procedimento indica o que ser feito quando o usurio fechar o formulrio. Ele passado por referncia para que voc possa modific-lo. Voc pode colocar um dos seguintes valores: 'caFree' para liberar o objeto da memria, 'caMinimize' minimiza o formulrio, 'caHide' torna o formulrio invisvel (o default) ou 'caNone', que no faz nada (ignora a ao de fechar). Ns colocamos Action := caFree para forar a destruio do objeto. Agora o formulrio principal tem que saber se um objeto de formulrio foi destrudo ou no. Para isso, crie a seguinte funo na unidade principal, logo aps a implementation: //retorna True se 'F' um formulrio aberto function FormularioAberto(F:TForm):boolean; var k:integer; begin result := False; if F = nil then exit; //com certeza est aberto for k := 0 to Application.ComponentCount -1 do if Application.Components[k] = F then begin result := True; exit; end; end; A funo procura o formulrio dentro da propriedade Components do objeto 'Application'. Essa propriedade um vetor contendo os componentes (formulrios) adicionados aplicao. Note que isso funciona porque os formulrios so criados com .Create(Application), o que faz com que o formulrio seja adicionado a Application.Components. Se a funo acha o formulrio, retorna True (ele j est aberto), seno False. Agora altere os procedimentos anteriores, que chamam os formulrios. Antes de criar um objeto formulrio, usamos a funo FormularioAberto para saber se ele j foi aberto: procedure TformPrincipal.mnuCadProdutosClick(Sender: TObject); begin if not FormularioAberto(FormProduto) then

FormProduto :=TFormProduto.Create(Application); FormProduto.Show; end; procedure TformPrincipal.mnuCadFornecedorClick(Sender: TObject); begin if not FormularioAberto(FormFornecedor) then FormFornecedor :=TFormFornecedor.Create(Application); FormFornecedor.Show; end; Note que o Show incondicional - o formulrio mostrado nas duas situaes, quando j existia e quando criado um novo.

Componentes de banco de dados


O Delphi possui vrios componentes, visuais e no-visuais, relacionados com as tarefas de acesso a bancos de dados. Os componentes no-visuais, da pgina Data Acess, acessam os dados e do suporte aos outros componentes, visuais. Os controles de dados [Data Controls] se conectam a um componente no-visual (geralmente um DataSource) para mostrar seus dados na tela e permitir edio. Na pgina Data Access, existem dois componentes principais: O componente Database [banco de dados] mantm toda a informao necessria para acessar um banco de dados, inclusive o formato de banco de dados usado (DriverName), os parmetros de conexo (Params). Permite criar um alias local dentro do programa, o que evita a necessidade de um alias externo. Ele mantm aberta uma conexo com o banco de dados, que usada por todos os outros componentes. Nem sempre necessrio usar um componente Database, mas ele muito til em qualquer situao O componente DataSource [fonte de dados] serve como intermedirio entre um dataset (ver abaixo) e vrios controles visuais e transfere os dados de um para o outro.

DataSets
Um dataset [conjunto de dados] um componente que permite manipular um conjunto de linhas (registros) e colunas (campos). Esse conjunto pode estar ou no em um banco de dados. Ele pode ser o contedo exato de uma tabela ou pode ser baseado em duas ou mais tabelas, ou ainda pode ser gerado dinamicamente no servidor, por um procedimento armazenado. A classe TDataSet a base para todas as classes de datasets. Ela contm propriedades, como Active, State, EOF e mtodos para abrir e fechar o dataset (Open, Close), para movimentao de linhas (First, Next, Prior, Last) e para editar o contedo do dataset, alterando, incluindo ou excluindo registros (Edit, Insert, Post, Cancel, Delete). Possui tambm eventos que permitem interceptar uma ao antes que [before] ela seja efetuada (BeforeOpen, BeforePost, BeforeDelete, etc.) e fazer algum tratamento depois que [after] ela seja efetuada (AfterPost, AfterDelete, etc.), alm de outros eventos usados em situaes especficas (OnNewRecord, OnCalcFields, OnPostError, ...). As subclasses de TDataSet existentes no Delphi aumentam essas capacidades para finalidades especficas: Table Acessa uma tabela fsica do banco de dados. Query Executa um comando SQL de consulta (SELECT) e retorna o resultado como um conjunto de registros, em alguns casos permitindo modificao. Ou executa um comando de manipulao do SQL, que no retorna resultado algum. StoredProc Executa um procedimento armazenado [stored procedure] em um SGBD cliente/servidor. Pode retornar resultados ou no. ClientDataSet Conecta-se remotamente com um componente Provider executando em outra aplicao, possivelmente em outro computador. Veremos mais tarde como utiliz-lo.

Objetos TField
Cada campo de um DataSet representado por um objeto de campo, da classe TField. Algumas propriedades desse objeto so tiradas dos campos da tabela. Outras podem ser modificadas pelo programa para determinar como o campo ser formatado, como ele ser mostrado num DBGrid etc. Voc pode adicionar objetos de campo com o editor de campos [fields editor], clicando duas vezes no DataSet. Se o editor de campos estiver vazio, objetos de campo so criados automaticamente para cada campo, mas sem

nome. De qualquer forma voc pode ter acesso a eles usando a propriedade Fields (acessa por posio) ou o mtodo FieldByName (acessa por nome): tblProduto.Fields[0].Value := 3; tblProduto.FieldByName('CodProduto').Value := 3; A propriedade Name do objeto de campo definida pelo Delphi a partir do nome do componente Table e do nome do campo da tabela. Por exemplo, se o componente se chama 'tblProduto' e o campo se chama 'QuantEstoque', o nome do objeto de campo 'tblProdutoQuantEstoque'. Mas no h problema em mudar esse nome. Quando o nome do campo contm espaos, eles so ignorados. Quando ele contm caracteres ilegais em Object Pascal, como ou letras acentuadas, ele retirado do nome. O campo Preo, por exemplo, ter um objeto 'tblProdutoPreo' (sem o C). Para cada tipo de dados, existe uma classe derivada de TField. Se o campo 'QuantEstoque' do tipo "Short" (Paradox), criado um objeto da classe TSmallintField. Se o campo 'Nome' do tipo "Alpha" (Paradox) ou "char", "varchar" (InterBase, SQL Server, Oracle), criado um objeto da classe TStringField. A propriedade Value permite consultar/modificar o valor de um campo. Ela retorna um valor do tipo variant, um tipo "mutante" em Delphi, que pode conter dados de vrios tipos de dados diferentes. Para converter os dados do campo em outro tipo, pode-se usar uma das propriedades de converso (AsTipo). Por exemplo, a propriedade AsString permite trabalhar com qualquer campo como se ele fosse uma string. Outras propriedades de converso so: AsBoolean, AsCurrency, AsDateTime, AsFloat, AsInteger e AsVariant.

Outras propriedades
Outras propriedades de um objeto TField so: Alignment Determina o alinhamento do texto dentro do campo (quando ele no est sendo editado). DataSize Para consulta apenas. Quantidade de bytes de memria ocupados pelo valor do campo, exceto no caso de BLOBs, onde o valor sempre zero. DataType Para consulta apenas. O tipo do campo. DisplayLabel Nome mostrado em um grid. DisplayText Texto que mostrado no campo (valor convertido para string) quando ele no est sendo editado. J formatado. DisplayWidth Largura usada em uma coluna de um grid. EditMask Mscara de edio. FieldName Nome do campo fsico. IsNull Para consulta apenas. Verdadeiro se o campo tem o valor NULL (no informado). ReadOnly Verdadeiro se o campo no pode ser modificado. Required Se verdadeiro, o campo obrigatrio. Deve ser informado. Size Tamanho do campo, quando ele do tipo string. Text Texto que mostrado no campo quando ele est sendo editado. ValidChars Conjunto de caracteres que determina quais caracteres so vlidos para digitao dentro de um campo. Visible Se falso, o campo no aparece em um grid.

Controles de dados
Os controles de dados mostram informao de um campo de uma determinada tabela. Os mais usados so o DBEdit, para editar um campo diretamente e o DBGrid, para mostrar vrios campos e registros. Outros componentes teis so: DBText - mostra um campo, mas no permite editar (como um Label). DBMemo - edita um texto de vrias linhas. DBRichEdit - edita um texto de vrias linhas, com formatao de fontes e tamanhos (como o RichEdit). DBCheckBox - mostra um campo lgico (verdadeiro ou falso) como um valor marcado ou desmarcado. DBRadioGroup - mostra um campo que tem algumas escolhas possveis como um grupo de "botes de rdio". A propriedade Items determina o texto que aparece nos itens e a propriedade Values determina o valor gravado no campo para cada item. Semelhante ao RadioGroup da pgina Standard. DBComboBox e DBListBox - mostram uma lista de valores possveis de um campo. DBLookupComboBox e DBLookupListBox - mostram uma lista de valores possveis de um campo, consultando uma outra tabela.

Mdulos de dados
Numa aplicao que utiliza bancos de dados, existem vrios componentes de banco de dados como componentes Table, Query, DataSource, Database(citados anteriormente) e esses componentes ficam geralmente espalhados pela aplicao. Um mdulo de dados [data module] um local central para guardar os componentes de dados usados pela aplicao. Ele semelhante a um formulrio, mas s pode conter componentes no-visuais; controles no podem ser colocados em um mdulo de dados. Alm dos componentes da pgina Data Access, voc pode ter outros componentes como Timer, OpenDialog etc. Geralmente um programa tem apenas um mdulo de dados, que contm, alm dos componentes de banco de dados, todo o processamento de eventos desses componentes, como por exemplo validao de campos e de registros. Alm disso, voc pode criar rotinas (mtodos pblicos) no mdulo de dados para ajudar o restante do programa. A principal vantagem de um mdulo de dados que se houver alterao nas tabelas do banco de dados, essa alterao pode ser feita em um nico lugar. Outra vantagem que, se vrios projetos diferentes usam o mesmo banco de dados, eles podem compartilhar o mesmo mdulo de dados. Vamos alterar o projeto ALMOXARIFADO.DPR, para utilizar mdulo de dados que ir manter as tabelas. Esse mdulo inicialmente ir usar as tabelas PRODUTO.DB e FORNECEDOR.DB criadas anteriormente.

Criando um mdulo de dados


Para criar um mdulo de dados, clique em File|New na pgina 'New' clique no cone Data Module() . O mdulo de dados semelhante a um formulrio com um fundo branco, mas no aparece para o usurio. Ele tem menos propriedades tambm. Altere a propriedade Name e coloque 'DMAlmoxarifado'. Salve o projeto, informando os nomes: Para 'Unit1' (do formulrio): MAlmoxarifado.PAS

Ligando um formulrio ao mdulo


No formulrio 'formProduto', o componente DataSource precisa fazer referncia ao mdulo de dados 'DMAlmoxarifado'. Para que isso seja possvel, voc deve acrescentar uma clusula uses ao formulrio, fazendo referncia unidade 'MAlmoxarifado'. Voc pode fazer isso com File|Use unit.

Convertendo um programa j Existente


Agora vamos colocar os componentes de banco de dados no mdulo. Comece com um componente Database. Altere o nome para 'dbAlmoxarifado'. Clique duas vezes sobre ele: digite em "Name", o nome "DBAlmoxarifado", em "Driver Name", selecione STANDARD e clique em Defaults. Embaixo, no parmetro PATH=, coloque o diretrio onde esto as tabelas (C:\CursoDelphi). Depois clique Ok. Coloque no mdulo dois componentes Table, um para cada tabela . Nos dois Table, altere DatabaseName para "DBAlmoxarifado", conectando o componente com o Database. Defina os nomes dos componentes como tblProduto, tblFornecedor. Clique duas vezes em 'tblProduto', adicione todos os campos ([Ctrl+A], depois Ok) , faa o mesmo processo para o componente tblFornecedor. Como iremos utilizar os componentes table no Data Module no faz mas sentido ter o componente tblGenerico no formulrio 'FormBase', pois, o cadastro de produtos ir utilizar o componente tblProduto e o cadastro de fornecedores ir utilizar o componente tblFornecedor. Ento delete o componente tblGenerico no formulrio 'formBase' e retire o comando que abre o componente , no evento OnShow. O comando a ser excludo ser: tblGenerico.open; Abra o formulrio 'formProduto'. Ir aparecer uma janela perguntado se voc deseja excluir as referncias desse componente tblGenerico ou se deseja criar este componente na classe TformProduto, isso ocorre porque a classe TFormProduto foi herdada da classe TFormBase, e esta classe utilizava o componente tblGenerico.Deixe a opo para excluir a referencia marcada e clique no boto 'Ok'. A mensagem mostrada na figura abaixo: Faa o mesmo para o formulrio 'formFornecedor'. Abra o formulrio 'formProduto'. Como todos os componentes esto associados ao componente DataSource existente neste formulrio , podemos ento alterar somente a propridade DataSet do componente DataSource para 'DMAlmoxarifado.tblproduto', com isso estamos utilizando o componente tlbProduto do mdulo 'DMAlmoxarifado'. No formulrio 'formFornecedor', iremos alterar a propriedade DataSet do componente DataSource para 'DMAlmoxarifado.tblfabricante', com esta alterao o formulrio est utilizando o componente tblFornecedor do mdulo 'DMAlmoxarifado'.

Se voc executar o programa observe que no mostrado nenhum registro das tabelas e nos cadastros o DBNavigator esta desabilitado, isto esta ocorrendo porque os componente tables ainda no foram abertos, mas iremos abri-los mais na frente.

Usando DBCtrlGrid
O componente DBCtrlGrid semelhante ao DBGrid: ele mostra uma clula para cada registro. Mas em vez de ter um layout fixo, com linhas e colunas, ele permite que voc projete o layout desejado. Nesse controle voc projeta um painel modelo, colocando vrios controles e ele replica esse painel vrias vezes. Abra o projeto ALMOXARIFADO.DPR. Crie um novo formulrio, com o nome 'FormListaProduto' e Caption= "Listar Produtos". Clique em File|Use unit e selecione a unidade 'MAlmoxarifado'. No mdulo 'DMAlmoxarifado' acrescente um componente DataSource , altere seu nome para 'dsProduto' e na propriedade DataSet coloque 'tblProduto'. No Cadastro de Produto e Almoxarifado deixamos o compomente DataSource fazendo parte do formulrio, mas, isto tambm poderia ser alterado, ou seja, alm do componente Table , colocar tambm os componentes DataSource no mdulo de dados.. Voltando ao formulrio 'formListaproduto', coloque um componente DBCtrlGrid no formulrio. Altere Align para alClient. Em DataSource, selecione 'DMAlmoxarifado.dsProduto'. Agora vamos colocar sobre esse componente controles de dados. Coloque dois componentes Label e dois DBEdit no formulrio, como indicado na figura: Os componentes aparecem apenas no primeiro painel, mas sero replicados em tempo de execuo. A propriedade DataSource de cada um j definida automaticamente, mas voc precisa definir o DataField de cada um. Coloque, respectivamente "Nome" e "QuantEstoque" na propriedade DataField. Salve a unit como LISTAPRODUTO.PAS. No formulrio principal, iremos chamar este formulrio, precisamos acrescentar uma referncia deste formulrio , utilize o item de menu File|Unit . Como nos formulrios anteriores utilizamos a opo para criar os formulrios dinamicamente o mesmo ser feito para este formulrio. Primeiro precisamos retirar este formulrio da lista "Auto-Create", em seguida no evento OnClick do item de menu Consulta|Produto . Faa o seguinte: if not FormularioAberto(FormListaProduto) then FormListaProduto :=TFormListaProduto.Create(Application); FormListaProduto.Show; Como o formulrio 'formListaProduto', est sendo criado dinmicamente , iremos destruir o formulrio quando o usurio fech-lo.Ento no evento Onclose deste formulrio acrescente o cdigo a seguir: action := caFree; Outras propriedades teis desse componente so: RowCount e ColCount (definem o nmero de linhas e colunas que aparecem no controle) e Orientation (que define se o grid ordena as linhas horizontalmente ou verticalmente. Para que este exemplo funcione necessrio abrir o componente 'tblProduto', no item abaixo iremos discutir sobre isto.

Abrir os componentes Table do mdulo


Um problema existente com a utilizao de mdulos, aonde devemos abrir e fechar o componente Table, pois, podemos ter vrios formulrios utlizando o mesmo componente . Neste exemplo estamos utilizando o mesmo componente tblProduto para os formulrios 'formProduto' e 'formListaproduto', se passamos a abrir o componente tblProduto no envento OnShow de cada formulrio, o que pode ocorrer que o componente table pode deixar uma determinada tabela aberta sem que ela esteja sendo utilizada no programa, isso ocorre quando os dois formulrios que a utiliza estejam fechados. Mas se passarmos a fechar todas as vezes que o formulrio for fechado, pode ocorrer que o usurio pode estar utilizando o 'formProduto' antes de fechar este formulrio ele resolve visualizar o 'formListaProduto' ', se neste momente o formulrio 'formListaProduto' for fechado , o componente tblproduto tambm ser fechado, como isso o formulrio 'formProduto' no ir mostrar nenhum produto, porque o tblproduto foi fechado para ele tambm. Quando vrios formulrios utilizam o mesmo compomente Table, qualquer modificao feita em um formulrio ser afetado no outro tambm, por exemplo, se movimentar a tabela para o prximo registro. Para resolvermos este problema iremos criar um procedimento para abrir tabelas e um procedimento para fechar tabelas. No procedimento AbrirTabelas iremos contar a quantidade de formulrios que utiliza este componente s, esse quantidade ser armazenada na propriedade tag de cada componente Dataset. A propriedade tag se acrescentar algum valor para ela no tem efeito nenhum, pois o Delphi deixa o programador utiliz-ls para a finalidade que desejar, neste caso iremos utilizar para ter um controle da quantidade de formulrios que utiliza este componente.

No procedimento FecharTable iremos verificar se a propriedade Tag 1 , pois se for este o nico formulrio que utiliza o componente . Independente se for ou no o formulrio nico iremos decrementar a propriedade Tag. Como podemos criar procedimentos ou funes nos mdulos de dados, os procedimentos AbrirTabela e FecharTabela sero criados no mdulo 'DMAlmoxarifado'. Abra este mdulo e acrescente os cdigo a seguir: procedure AbrirTabela(tabela :TDataSet); begin tabela.tag := tabela.tag + 1; tabela.open; end; procedure FecharTabela(tabela :TDataSet); begin tabela.tag := tabela.tag - 1; if tabela.tag = 0 then tabela.close; end; Nos procedimentos foram colocado um parmetro do tipo TDataSet , porque esses procedimentos podem ser usados para abrir ou fechar qualquer componente da classe TDataSet, no somente o compomente Table. O procedimento AbrirTabela ser colocado no evento OnCreate , quando os formulrios forem criados as tabelas utilizadas por ele sero abertas. E no evento OnClose iremos colocar o procedimento FecharTabela . Abra o formulrio 'formProduto' , crie um procedimento para o evento OnCreate e acrescente o cdigo abaixo para abrir as tabelas utilizadas por este formulrio: procedure TformProduto.FormCreate(Sender: TObject); begin inherited; AbrirTabela(DMAlmoxarifado.tblProduto); end; No evento OnClose , para fechar os componentes utilizados por este formulrio acrecente o cdigo abaixo: procedure TformProduto.FormClose(Sender: TObject;var Action: TCloseAction); begin inherited; FecharTabela(DMAlmoxarifado.tblProduto); end; Iremos repetir a mesma operao para os formulrios 'formFornecedor' e 'formListaProduto'. Abra o formulrio 'formListaProduto' , no evento OnCreate coloque: procedure TformListaProduto.FormCreate(Sender: TObject); begin AbrirTabela(DMAlmoxarifado.tblProduto); end; No evento OnClose , faa: procedure TformListaProduto.FormClose(Sender: TObject;var Action: TCloseAction); begin action := cafree; FecharTabela(DMAlmoxarifado.tblProduto); end; Abra o formulrio 'formFornecedor' , no evento OnCreate e acrescente o cdigo abaixo: procedure TformFornecedor.FormCreate(Sender: TObject); begin inherited; AbrirTabela(DMAlmoxarifado.tblFornecedor); end; No evento OnClose , para fechar os componentes utilizados por este formulrio acrecente o cdigo abaixo: procedure TformFornecedor.FormClose(Sender: TObject;var Action: TCloseAction); begin inherited; FecharTabela(DMAlmoxarifado.tblFornecedor); end;

Salve o programa e execute. Para testar abra o formulrio 'Lista de Produtos' , note que ao mostrar o formulrio, os componentes sero duplicados para cada registro. No fecha esse formulrio, e abra o formulrio de 'Cadastro de Produtos', no 'Lista de Produtos', caso tenha mais de um registro cadastrado , clique por exemplo no segundo registro , observe que o 'Cadastro de Produto' tambm foi modificado, isso ocorre porque os dois formulrios esto utilizando o mesmo compomente Table , e qualquer modificao feito em um afeta no outro, caso esteja aberto.

4 - Cliente / Servidor
Diferenas na programao cliente / servidor A linguagem SQL Usando o Oracle e o SQL Server Criando uma tela de login Monitorando comandos SQL

Diferenas na programao cliente/servidor


Um sistema cliente/servidor particionado em duas partes: o cliente aplicao que executa numa estao de rede e implementa a interface com o usurio. Essa aplicao fica responsvel por validar as entradas do usurio, e iniciar pesquisas de acordo com um pedido do usurio. Ela se comunica remotamente com o servidor, que um sistema gerenciador de banco de dados (SGBD) executando em um computador central, chamado servidor de banco de dados. O servidor responsvel pela manuteno das estruturas de dados necessrias em arquivos, pelos detalhes internos do acesso aos dados, pelo controle de acesso (usurios autorizados e suas senhas) e pela manuteno da integridade dos dados. A comunicao entre cliente e servidor feita atravs da linguagem SQL, tanto para operaes de definio de dados, quanto para consulta ou atualizao dos dados. Quando o SGBD recebe um pedido para selecionar alguns dados, ele acessa localmente os dados no servidor e retorna apenas o resultado pedido. No caso de uma atualizao, ele apenas informa que a atualizao foi feita (e quantas linhas foram atualizadas). No Delphi voc pode comear a desenvolver sua aplicao usando um banco de dados desktop e depois migrar para cliente/servidor, com poucas modificaes no programa, pois a forma de acesso atravs do programa praticamente a mesma. Neste captulo veremos o SGBD Oracle, que atualmente popular pela sua eficincia. O objetivo no vermos todas as caractersticas do Banco de Dados, mas principalmente como trabalhar com o Delphi acessando o Oracle. Em notas, traaremos um paralelo da conexo com o Oracle e com o SQL Server, da Microsoft.

Evitando acesso seqencial tabela


A principal coisa para ter em mente tentar usar o ambiente cliente/servidor para o que ele foi projetado. Programas que lem toda a tabela seqencialmente, como um arquivo enorme, para fazer totalizaes ou alteraes so ineficientes nesse ambiente. Por exemplo: o trecho de programa abaixo pode funcionar bem para tabelas Paradox, mas no recomendvel num ambiente cliente/servidor: while not tblProduto.EOF do begin if tblProdutoCodFornecedor.value = codFornecedor then begin tblProduto.Edit; tblProdutoPreco.Value := (1 + porcent/100) * tblProdutoPreco.Value; tblProduto.Post; end; tblProduto.Next; end;

Em vez de fazer esse processamento seqencial, que l todos os registros mesmo os que no so necessrios, melhor usar um comando SQL que faz a mesma coisa que esse tipo de programa: update Produto set Preco = Preco * :fatorAumento where CodFornecedor = :codFornecedor

Nomes de campos e tipos de dados


Cada tipo de banco de dados tem as suas prprias regras para os nomes de campos que podem ser usados e os tipos de dados que podem haver para cada campo. S a ttulo de exemplo, o Paradox aceita nomes de campo com at 25 caracteres, que podem conter espaos ou caracteres acentuados, por exemplo "Preo Venda" ou "Situao". O Oracle (bem como o SQL Server) no aceita espaos em nomes, mas permite caracteres acentuados. Os campos teriam que ser renomeados para "Preo_Venda" ou "Situao". J o InterBase no aceita espaos nem caracteres acentuados e os campos teriam que ser "Preco_Venda" e "Situacao". Resumindo, para que uma aplicao seja fcil de converter de um formato para outro, recomendvel usar nomes que seguem todos os padres, ou seja, sem acentos nem espaos. Outra questo so os tipos de dados. Cada banco de dados tem seus prprios tipos. Embora com nomes diferentes, esses tipos podem ser compatveis entre si. A tabela abaixo resume algumas diferenas entre tipos. Essa tabela no completa, mas voc pode saber quais os mapeamentos de tipos ao usar o Data Migration Wizard do Delphi: Paradox Oracle SQL Server A20 (alpha, tam.20) varchar2(20) ou char(20) varchar(20) ou char(20) S (short) number smallint I (long integer) integer int M (memo) long text G (graphic) long raw image D (date) date datetime T (time) date datetime N (number) number float $ (money) number money + (autoincrement) no existe definido como IDENTITY Em bancos de dados cliente/servidor, o tipo varchar(n) representa uma informao de tamanho varivel, com no mximo n caracteres e o tipo char(n) representa um campo de tamanho fixo, que sempre completado com espaos. O SQL Server no tm tipos separados para data e hora, mas datetime guarda as duas informaes juntas no mesmo campo. No caso de valores numricos, SQL Server oferecem os tipos numeric(n,p) e decimal(n,p), que so mais exatos que o "number" do Paradox. Ou double precision, que tem uma preciso maior. Esses tipos guardam valores com menos exatido do que numeric, decimal e money.

Bloqueio de registros
Quando voc trabalha com tabelas Paradox ou dBase, o Delphi trata o bloqueio de registros automaticamente. Ele usa o que chamado de bloqueio pessimista, porque ele assume que muito provvel outro usurio tentar editar o mesmo registro. Nesse modo de trabalho, o registro bloqueado logo que comea a ser modificado (com Edit) e permanece bloqueado at que ele tenha sido salvo (com Post) ou sua edio seja cancelada (com Cancel). Num ambiente cliente/servidor, o bloqueio pessimista no adequado. Se vrios usurios trabalham com a mesma tabela, quando um usurio A bloqueia um registro, um outro usurio B fica bloqueado pelo SGBD. O programa aparece "travado" para B at que A resolva liberar o registro. Num SGBD cliente/servidor, o Delphi e o SGBD usam um bloqueio otimista, onde o registro bloqueado apenas por um curto perodo de tempo, durante a operao de alterao (Post). Mas no bloqueado pelo simples fato de estar sendo modificado em memria. Como veremos, uma forma melhor de controlar o bloqueio e de garantir a integridade do banco de dados so as transaes.

A linguagem SQL
SQL (Structured Query Language - Linguagem estruturada de consulta) uma linguagem usada para comunicao com bancos de dados, que permite especificar comandos de consulta e manipulao de dados. Ela no uma linguagem de programao completa, mas sempre chamada a partir de outra linguagem.

A maioria dos bancos de dados seguem o padro ANSI SQL-92, que define um conjunto de comandos bsicos. Mas cada banco de dados tem algumas diferenas na forma de implementao do SQL e tem algumas caractersticas a mais que no fazem parte do padro. Os comandos do SQL podem ser divididos nas seguintes categorias (note que isso no abrange todos os comandos, s os comandos ANSI que sero vistos nesse curso): DDL: Data Definition Language - comandos usados para definir a estrutura dos dados, como CREATE DATABASE, DROP DATABASE, CREATE TABLE, DROP TABLE, ALTER DATABASE, ALTER TABLE, e mais alguns outros. DML: Data Manipulation Language - comandos que operam sobre os dados. Podem ser divididos ainda em: Comando de consulta: o comando SELECT consulta dados armazenados no banco de dados. Comandos de alterao: os comandos INSERT, UPDATE e DELETE e outros manipulam dados, alterando o contedo de uma tabela do banco de dados.

SQL Local
No Delphi, possvel usar SQL para operaes com tabelas Paradox ou dBase. Nesse caso, usa-se uma variao do padro SQL chamada SQL Local. O SQL local tem algumas limitaes em relao ao SQL padro, especialmente nos comandos DDL. Ele tambm tem algumas diferenas de sintaxe, por exemplo: como tabelas e campos podem ter espaos no meio do nome, ele permite colocar o nome da tabela ou do campo com aspas, incluindo a extenso de arquivo, como: select Nome, Produto."Preo Venda" from "Produto.db" as Produto Note que a maioria dos bancos de dados no suporta espaos nos nomes de campo, como o Paradox suporta. Muitos tambm no suportam nomes acentuados, ou com . Pensando nisso, se voc quiser portar as tabelas mais facilmente para outro formato, melhor usar nomes de campos sem espao ou acentos, como Preco_Venda em vez de "Preo Venda". Note tambm que, no SQL Local, possvel omitir a extenso do arquivo: select * from Produto Com isso, o Delphi procura "PRODUTO.DB ", "PRODUTO.DBF", nessa ordem, at encontrar. Isso tambm ajuda a portar a aplicao para outros formatos de banco de dados.

Testando comandos SQL


Para executar um comando SQL e ver seus resultados imediatamente, voc pode usar o SQL Explorer do Delphi. Clique em Database|Explore. Do lado esquerdo, abaixo de "Databases" selecione o alias de banco de dados onde voc vai executar a consulta. No nosso caso, crie um novo alias, chamado "Curso", se no existir. Clique em "Databases" com o boto direito e em New e escolha o drive 'STANDARD' . Digite o nome "Curso" para o alias e tecle [Enter]. Do lado direito, na pgina "Definition", clique no parmetro PATH e digite o caminho do diretrio onde esto os arquivos do curso (p.ex.: C:\CursoDelphi). No SQL Explorer, clique no boto Apply para salvar as alteraes. Agora clique no boto [+] ao lado do nome "Curso" para conectar-se ao alias. O cone do alias ficar com uma borda em destaque, indicando que ele est aberto. Clique na pgina "Enter SQL" e digite o seguinte comando: select * from Produto Esse comando vai mostrar todos os dados da tabela "Produto.db". Clique no boto para executar a consulta (ou tecle [Ctrl+E]).

Usando o Oracle e o SQL Server


Oracle um SGBD cliente/servidor que roda em Windows NT e Linux, e permite ter estaes-cliente em vrios tipos de plataforma, como Windows 95, Windows NT e o prprio Linux (no SQL Server, as estaes Clientes podem ser Windows 3.11, Windows 95, Windows NT, DOS e Macintosh). Iremos utiliz-lo como exemplo de banco de dados Client/Server. O que iremos mostrar deste Banco de Dados ser somente o suficiente para utilizarmos o Delphi com o ambiente Client/Server.

Configurando o servidor
A instalao dos servidores de banco de dados no ser vista em detalhes, s suas linha gerais. Um servidor Oracle pode rodar em duas plataformas, Linux e NT. Ambas exigem 48Mb de memria RAM para serem instaladas, e em torno de 200Mb livres para serem instalados.

Se a verso do SQL Server for 6.5 exige o Windows NT , mas se a verso for 7.0 o servidor pode ser instalado no Windows NT, 95 ou 98. Ele exige um computador com 16 Mb de memria, embora mais memria seja recomendvel. Do ponto de vista do desenvolvimento, importante chamar a ateno para duas opes de instalao no servidor: o character set [conjunto de caracteres] e sort order [ordem de classificao]. O character set determina quais so os cdigos que o servidor usa para representar caracteres. No Oracle, a opo character set padro a "USASCII", caracterizando os caracteres padres mundias ASCII. No SQL Server, recomendvel usar o character set default durante a instalao (ISO 8859-1) que vai usar o mesmo conjunto de caracteres do Windows. J a ordem de classificao recomendvel mudar para "Dictionary order, case-insensitive, accent-insensitive" que vai permitir pesquisar no banco de dados ignorando acentos. Essas opes s podem ser mudadas reinstalando o SQL Server.

Instalando o software cliente


Aps instalar o servidor, necessrio instalar o software cliente nas estaes. Isto feito com o mesmo programa que instala o servidor (SETUP.EXE). Esse instalador vai colocar no seu computador todas as biblicotecas usadas para a comunicao com o servidor de banco de dados (tanto Oracle quanto SQL Server). So instaladas tambm ferramentas que vo permitir o gerenciamento do banco de dados remotamente. Observao: Caso esteja utilizando o SQL Server, voc deve mudar uma opo para evitar problemas com campos do tipo data (datetime) no Delphi. Clique em "SQL Client Configuration Utility" no menu de programas "Microsoft SQL Server Utilities". Desmarque a opo "Use international settings" e clique Ok. Se o Delphi ou qualquer programa dele estiver rodando, feche-os e inicie novamente. Para testar a conexo com o banco de dados, voc pode usar o utilitrio Oracle Schema Manager. Este um dos utilitrios possveis em que voc pode testar a conexo com o servidor Oracle. Clique em Schema Manager que est localizado em Oracle Enterprise Manager no menu de Programas. Aparecer uma tela de login. Informe o Nome do usurio, a senha, e em Service coloque o nome do servidor de banco de dados Oracle na rede. Uma vez conectado, voc ver todos os objetos (tabelas, vises) que este usurio conectado tem direito de acesso. Observao: Se voc estiver usando o SQL Server, execute o "SQL Enterprise Manager", no menu de programas "Microsoft SQL Server Utilities". Da primeira vez que ele for executado, ele pedir para registrar o servidor, que simplesmente guardar algumas informaes sobre o servidor em seu computador. Para registrar, digite o nome do computador servidor (em todos os exemplos, citaremos esse nome como CURSO). Mantenha Use Standard Security marcado, e digite 'sa' em User Name. Deixe a senha, Password, vazia, a no ser que tenha sido alterada em seu servidor. Voc deve ver uma rvore com o nome do servidor no topo. Clique no cone [+] ao lado desse nome. Os itens que esto aqui dentro ainda sero vistos com mais detalhes.

Criando um banco de dados


Vamos criar um banco de dados e depois migrar os dados das tabelas Paradox para ele. No momento de instalao do servidor Oracle, voc define um nome para a instncia do banco de dados que ir ser criado pelo Oracle. Tal instncia que identificar o banco de dados no Oracle. O Oracle j cria um banco de dados no momento de sua instalao, podendo ser usado pelos seus usurios. Foge ao escopo deste curso a criao e manuteno do bancos de dados, mas sim como o Delphi os utiliza. Pergunte a quem instalou o servidor Oracle ou ao prprio DBA do servidor o nome da instncia. Caso voc esteja usando o SQL Server, proceda da seguinte forma: Clique no servidor de dados (dentre os j registrados) que deseja utilizar em seguida clique em "Databases" com o boto direito no Enterprise Manager e clique em New Database. O nome do banco de dados ser "Curso". Altere a guia "Location" se quiser especificar em que unidade de seu computador o banco de dados dever ser criado. O padro /Data de dentro da raiz do diretrio onde o SQL Server foi instalado. Em "Size", coloque 5Mb, que ser o suficiente para nossos exemplos. Repare que na guia "Transaction Logs", o SQL Server colocar um tamanho de 1Mb. Clique em Ok, e o banco de dados ser criado. Agora vamos criar um alias para esse banco de dados no Database Explorer do Delphi. Clique em "Databases" com o boto direito, em New escolha 'ORACLE' e digite o nome "OracleCurso". Voc precisa agora configurar os parmetros de conexo. Eis os mais importantes:

SERVER NAME
A "string de conexo" ou alias do SQL*Net ou Net8. Esse o mesmo nome usado, por exemplo, para se conectar usando o SQL*Plus do Oracle.

USER NAME
Nome de usurio no Oracle. Isso apenas um default, que geralmente alterado para um nome diferente a cada conexo.

LIST SYNONYMS
LIST SYNONYMS = {NONE | PRIVATE | ALL} Um sinnimo do Oracle um outro nome criado para uma tabela j existente, de forma a facilitar o acesso por outros usurios. Um sinnimo pode ser definido com um owner [proprietrio] especfico, ou como sinnimo pblico, acessvel a todos usurios. O Delphi pode sempre acessar um sinnimo como se fosse uma tabela real. A opo LIST SYNONYMS define quais os sinnimos que ficam disponveis para visualizar em uma lista de tabelas do banco de dados (por exemplo, que aparece na lista da propriedade TableName): NONE: nenhum sinnimo aparece em listas de tabelas. S aparecem tabelas "reais" e vises [views]. PRIVATE: aparecem os sinnimos cujo proprietrio o usurio conectado, mas no aparecem os sinnimos pblicos. ALL: aparecem todos os sinnimos, incluindo os pblicos.

NET PROTOCOL
NET PROTOCOL = {TNS | NETBIOS | TCP/IP | NAMED PIPES | APPC | ASYNC | 3270 | IPX/SPX } Use sempre TNS, que o protocolo suportado pelas verses mais recentes do Oracle, usando SQL*Net (7.x) ou Net8 (8.x). Se voc usar outro valor, consulte a documentao do Oracle para definir o SERVER NAME.

OBJECT MODE
OBJECT MODE = {TRUE | FALSE} Use FALSE para o Oracle 7.x e verses do Oracle8 que no tenham instalado a Objects Option. Use TRUE para o Oracle8 com Objects Option. Com essa opo, o Delphi passa a suportar a programao baseada em objetos do Oracle8 e os novos tipos de dados do Oracle8, como OBJECT, VARRAY e nested table.

ENABLE INTEGERS
ENABLE INTEGERS = {FALSE | TRUE} Quando FALSE, qualquer campo numrico do Oracle (NUMBER) interpretado pelo BDE como sendo um campo de ponto flutuante (tipo "Float"). Se voc mudar para TRUE, os campos NUMBER(x), com x < 5 so interpretados pelo Delphi como do tipo "Smallint" (inteiro pequeno, de 16 bits) e com 5 <= x < 10 so interpretados como do tipo "Integer" (inteiro grande, de 32 bits).

ENABLE BCD
ENABLE BCD = {FALSE | TRUE} Quando FALSE, campos NUMBER so tratados como do tipo "Float" (Double), o que pode provocar erros de arredondamento. Quando TRUE, campos NUMBER so tratados como do tipo Currency no Delphi/BDE, permitindo operaes mais exatas.

ROWSET SIZE
ROWSET SIZE = n (um nmero inteiro) O Delphi busca n linhas de cada vez do servidor para o cliente. Aumentar esse nmero pode melhorar o desempenho. No nosso caso, vamos preencher os campos Server Name, com o nome do servidor Oracle na Rede, e User Name, com o nome do usurio cadastrado no servidor Oracle, mantendo os outros valores default. Isto far com que o alias criado se comunique com a instncia padro criada no momento da instalao. Observao: Caso voc esteja usando o SQL Server, proceda da seguinte forma: Clique em "Databases" com o boto direito, em New escolha o driver 'MSSQL' e digite o nome "SQLCurso". Agora, do lado direito, preencha os seguintes parmetros: em SERVER NAME coloque "INSTRUTOR" (ou o nome de computador do seu servidor SQL), em DATABASE NAME coloque "Curso" (o nome do banco de dados criado). Para facilitar a conexo, em USER NAME digite 'sa'. Para migrar os dados que esto na tabela Paradox(Produtos e Fornecedores) para o Oracle (bem como para o SQL Server), execute o " Data Pump" do menu de programas ("Iniciar|Programas|Borland Delphi 4"). Esse um programa que permite migrar dados de um formato de banco de dados para outro. Na tela inicial, selecione "Select by alias name" se j existe um alias referenciando utilize "Curso". Caso contrrio, escolha "Select by directory" e selecione "C:\CursoDelphi" (diretrio do Banco de Dados Paradox). Clique em Next.

Como alias de destino, escolha 'OracleCurso' (ou 'SQLCurso', caso seja um servidor SQL Server), que ns criamos anteriormente. Clique em 'Next'. Voc ter que informar a senha do usurio. Na prxima tela, selecione quais tabelas voc quer migrar ou clique em [>>] para incluir todas. Clique em Next. O Data Pump vai mostrar para cada tabela o status "Unchanged" (sem mudana) se no houve necessidade de alterar nomes de campos. Vai mostrar o status "Modified names" caso algum nome de campo tenha sido alterado (lembre-se de que o Oracle e nem o SQL Server aceitam espaos em nomes). Caso ele mostre "Modified names", clique duas vezes sobre a tabela afetada. Voc ver do lado esquerdo a lista de nomes de campos. Se algum deles tiver espaos, o Data Pump troca o espao por '_'. Nem sempre isso que voc quer, mas voc pode clicar no campo e digitar o novo nome em "Field Name", abaixo de "Target". Voc pode tambm ver ou modificar o tipo de dados de destino. Aps conferir e corrigir essas informaes, clique em "Next". Agora, para fazer o processo de cpia, clique no boto UpSize. O Data Pump comea a copiar as tabelas. Aps terminar, ele vai mostrar um relatrio de todas as operaes feitas, que voc pode guardar numa tabela Paradox (Write a copy of this report to a file) se quiser conferir depois. Clique em "Done" para finaliz-lo.

Acessando o banco de dados


O BDE pode acessar o Oracle (bem com o o SQL Server) de duas formas: ODBC: atravs de um driver ODBC, fornecido juntamente com o Oracle. Nesse caso existe uma camada de software a mais (o driver), o que pode reduzir o desempenho significativamente. Disponvel no Delphi Developer. SQL Links: um driver SQL Link para o Oracle fornecido juntamente com o Delphi Client/Server. Esse driver se comunica nativamente com o BDE, portanto com velocidade bem maior. A informao necessria para acessar uma tabela no SQL Server : Nome do servidor: nome do computador na rede; Login de usurio: login vlido para o SQL Server (exceto se for usada segurana integrada com o NT); Senha do usurio: senha correspondente ao login informado. A informao necessria para acessar uma tabela no SQL Server a mesma, com apenas um detalhe a mais: Nome do banco de dados: qual banco de dados ser usado no servidor; Todas essas informaes, exceto a senha de usurio, podem ser guardadas em um alias, que pode ser uma alias global do BDE ou um alias local criado com um componente Database. A senha deve ser informada no momento da execuo (tipicamente no incio do programa) e obviamente tambm o nome de usurio (a no ser que voc esteja desenvolvendo uma aplicao de teste). A opo LANGDRIVER, em um alias do Delphi, determina qual o "language driver" que ser usado para converter caracteres de/para o Oracle (e tambm SQL Server) . Se esta opo estiver configurada incorretamente, caracteres acentuados no sero mostrados ou gravados corretamente, porque diferentes "language drivers" tm diferentes codificaes para os caracteres. Essa opo tambm define a classificao dos caracteres. Para o SQL Server, se o servidor foi instalado com o character set default, ISO 8859-1, use o language driver chamado "'ascii' ANSI",e para o Oracle, escolha a mesma opo caso tenha deixado o conjunto de caracteres padro no momento da instalao. Esse driver tem o nome interno de "DBWINUS0". Se por acaso o servidor estiver com o conjunto de caracteres "code page 850", o driver a ser usado deve ser algum compatvel com o cdigo de pgina 850. Essa opo LANGDRIVER pode ser alterada em vrios lugares: - No Database Explorer, ou BDE Administrator, quando voc est alterando parmetros de um alias: nesse caso aparece uma combobox, que permite selecionar um dos drivers disponveis. Nesse caso, escolha "'ascii' ANSI". - No componente Database, propriedade Params. Nesse caso, altere a linha do LANGDRIVER= para conter: LANGDRIVER=DBWINUS0. Se voc no especificar um language driver, o Delphi assume o conjunto de caracteres do DOS, e a ordenao baseada nos cdigos desses caracteres. Isso no funcionar bem com o SQL Server usando o character set default.

Configurando o Componente Database


Abra o projeto ALMOXARIFADO.DPR, que atualmente utiliza Paradox.No mdulo de dados, clique duas vezes no componente Database. Em "Driver Name", selecione ORACLE (ou MSSQL, caso desejado). Clique no boto "Defaults" e altere os parmetros: SERVER NAME=nome do seu servidor Oracle USER NAME=system (ou outro login vlido) LANGDRIVER=DBWINUS0

No SQL Server, voc precisar configurar mais o seguinte parmetro: DATABASE NAME=nome do banco de dados criado no servidor Opcionalmente, num programa de teste, voc pode colocar a senha de acesso ao SGBD diretamente no componente Database. Para isso, modifique o parmetro PASSWORD = , o ltimo da lista, e digite a senha do servidor. Tambm desmarque "Login prompt". Se a propriedade LoginPrompt falsa, significa que no vai mais aparecer a tela de login quando voc conectar-se ao banco de dados. Outra propriedade , KeepConnection, aparece nessa tela como "Keep inactive connection". Se ela for verdadeira, o componente Database fecha a conexo com o banco de dados quando no h nenhuma tabela aberta. Deixe os outros parmetros com os valores default e clique em OK. Altere a propriedade Connected do Database para True para testar se a conexo est funcionando. Depois tente ativar (altere Active=True) para cada uma das tabelas. Verifique se ao especificar os nomes das tabelas na propriedade TableName de cada componente foi mantida a extenso .db, e em caso afirmativo necessrio retir-la. Verifique tambm se algum campo tem nome diferente ou tipo de dados incompatvel. Se houver, voc tem que: - abrir o editor de campos da tabela - remover o campo com problemas - adicionar esse campo novamente - no formulrio que usa a tabela, alterar a propriedade DataField dos controles necessrios. Depois execute o programa e teste o seu funcionamento.

Criando usurios e definindo permisses


Todo banco de dados cliente/servidor tem um controle de segurana, que no permite o acesso ao banco de dados se o usurio no for identificado corretamente. O servidor mantm um cadastro de usurios, cada um com sua senha de acesso. No Oracle, o usurio 'system' tem poderes absolutos (geralmente identificamos estes usurio como sysdba ou at mesmo, dba.). Ele pode tirar (revogar) e dar (conceder) permisses a todos os usurios normais do sistema. Para criar novos usurios no Oracle, abra o Oracle Security Manager, informe o nome de usurio, senha, e em service coloque o nome do servidor Oracle de sua Rede. necessrio efetuar a conexo utilizando-se um usurio com poderes de administrao sobre o banco de dados. Clique com o boto direito em 'Users', e selecione 'New User'. Aparecer uma tela para voc configurar o novo usurio. Digite o nome do usurio, e em Authentication, escolha 'Password'. Isto far com que o usurio tenha sua conta e senha prpria para se conectar ao Oracle. Em TableSpaces, escolha 'Tools' para a opo default e 'Temp' para o opo 'Temporary'. Na guia 'Roles/Privileges', haver uma lista com os privilgios padres do sistema. Procure pelo privilgio 'RESOURCE' e adicione-o clicando na seta para baixo. Este privilgio d direito criao de tabelas, triggers, etc. Observao : Caso voc esteja utilizando o SQL Server, abra o SQL Enterprise Manager, selecione o servidor, e dentro dele clique em Logins com o boto direito e em New login. Digite o nome 'usuario'. Voc deve marcar quais os bancos de dados que esse usurio pode acessar, na coluna "permit", no nosso caso o banco de dados 'Curso'. Dentro de cada banco de dados, ele ter as permisses que voc indicar. Digite e confirme a senha do usurio e clique no boto Add para adicionar esse novo "login". Depois de criar o usurio, voc deve dar permisso a este usurio de acessar os objetos e recursos j existentes no Oracle. Para isto, entre no 'Oracle Security Manager', escolha o usurio que voc acabou de criar, e em 'Object Privileges Granted', clique com o boto direito do mouse e escolha 'Add Privileges to Users'. Aparecer uma tela de configurao de permisses. Escolha quais so os usurios que recebero os privilgios necessrios clicando no nome dos mesmos. Em 'Privilege Type', escolha 'Object Privileges'. Aparecer no canto inferior os esquemas contidos naquele banco de dados. Escolha o esquema do usurio criador do objeto e clique nos itens relacionados queles usurio (tables, views, etc). Expanda o item desejado e clique no objeto. Aparecer em um quadro direita a lista de possveis permisses a serem concedidas para aquele objeto. Escolha as permisses que achar necessrio e clique em 'OK'. Voc pode tambm atribuir permisses usando o comando 'GRANT'. Por exemplo, para se dar permisso de SELECT na tabela 'Alan.Clientes' ao usurio 'Rogerio', voc dever executar o seguinte comando: "Grant select on Alan.Clientes to Rogerio". Observao: Caso voc esteja trabalhando no SQL Server, proceda da seguinte forma: expanda o item "Databases" e selecione o banco de dados "Curso" que foi criado antes. Clique em [+] para expandir esse item, dentro dele expanda "Groups/Users", dentro dele o nome "public" e dentro de public selecione o usurio "usuario". Clique nesse nome com o boto direito e em Permissions. Para cada tabela, o usurio pode ter permisses de SELECT (consultar), UPDATE, INSERT e/ou DELETE individuais. Para simplificar e conceder todas as permisses a este usurio, clique em Grant All. Para salvar as permisses concedidas, clique em Set, depois clique em Close para fechar a caixa de dilogo.

Criando uma tela de login


Agora retorne ao projeto. Se voc deixar a propriedade LoginPrompt do componente Database = True, ele mostra uma tela de login automtica que l o nome de usurio e senha para conexo. Mas voc pode tambm criar uma tela de login personalizada, que o que faremos. Crie um novo formulrio. Altere as seguintes propriedades: Name: FormLogin Caption: Conexo BorderStyle: bsDialog BorderIcons, sub-item biSystemMenu: False Salve a unidade do formulrio como LOGIN.PAS. Coloque dois componentes Label, dois Edit e dois botes, como na figura: Coloque os nomes dos componentes de 'editUsuario', 'editSenha', 'btnOk' e 'btnCancelar'. Para o 'editSenha', altere a propriedade PasswordChar, colocando um * (asterisco). No boto 'btnOk', altere Default=True e em 'btnCancelar', coloque Cancel=True. Clique em File|Use unit e escolha 'MAlmoxarifado', porque vamos precisar usar o componente Database aqui dentro. Crie um procedimento para o boto de Ok: procedure TFormLogin.btnOkClick(Sender: TObject); begin with DMAlmoxarifado.DBAlmoxarifado do begin Connected := False; Params[1] := 'USER NAME=' + editUsuario.Text; Params[20] := 'PASSWORD=' + editSenha.Text; try Connected := True; Self.Close; except on EDatabaseError do ShowMessage('Usurio ou senha invlida!'); end; end; end; A propriedade Params do Database uma lista de strings. Caso voc esteja utilizando o Oracle, o parmetro no ndice 1 o USER NAME e o parmetro 20 a senha. Caso seja o SQL Server, os ndices so 2 e 23, respectivamente. (O ideal seria uma rotina que percorre Params e descobre onde esto os nomes USER NAME e PASSWORD). O programa altera esses parmetros antes de conectar o banco de dados. Note o tratamento de excees: quando ocorre alguma exceo durante a conexo, o programa simplesmente mostra uma mensagem dizendo que a senha invlida. Agora crie um procedimento para o boto Cancelar: ele simplesmente termina a aplicao caso o usurio recuse a se conectar: procedure TFormLogin.btnCancelarClick(Sender: TObject); begin Application.Terminate; end; Vamos chamar esse formulrio a partir do formulrio principal, no evento OnShow. Crie um procedimento nesse evento, contendo apenas o seguinte: procedure TFormPrincipal.FormShow(Sender: TObject); begin FormLogin.ShowModal; end; Clique em File|Unit e clique em 'Login' , pois utilizamos este formulrio no fomulrio principal. Como foi utilizado a classe de Exceo EDataBaseError necessrio acrescentar um uses para a unit 'db'. Teste o programa , mas primeiro com o usurio sa e senha vazia, depois com o usurio que voc criou e a senha dele.

Monitorando comandos SQL


Uma ferramenta muito til do Delphi o SQL Monitor. Ele permite monitorar o que acontece internamente: quais os comandos SQL executados pelo Delphi durante a execuo do programa. Abra o projeto anterior e abra tambm o SQL Monitor no menu [ Iniciar] |Programas|Borland Delphi 4|SQL Monitor. Execute o programa e note quais as mensagens que aparecem no SQL Monitor. Ele vai mostrar os comandos que o programa est executando, entre outras coisas. Ao iniciar o programa, ele deve mostrar uma mensagem "Log started for "Almoxarifado". Durante a execuo, ele vai mostrar vrias linhas de log relacionadas a outras aes, como por exemplo na figura: Alguns tipos de mensagens que voc vai ver na janela do SQL Monitor so: - SQL Prepare: preparao de uma consulta. Fase em que ele monta um comando SQL e envia uma consulta preparada para o servidor, mas ainda sem substituir os parmetros dessa consulta. - SQL Stmt: operao sobre um comando [statement] SQL. Pode conter uma outra indicao como: Fetch, que indica busca de registros de resultado de uma consulta. - SQL Data In: geralmente aparece antes de executar um comando SQL. Indica que o valor de um parmetro est sendo passado para a consulta. - SQL Execute: indica a execuo de um comando SQL. O comando nem sempre aparece completo. s vezes voc ver parmetros dentro do comando, indicados com ? ou :1, :2,... Nesse caso, verifique as linhas SQL Data In que aparecem imediatamente antes, que definem os valores dos parmetros passados. Voc pode filtrar as informaes que o SQL Monitor mostra. Para isso, clique em Options|Trace Options. Para ver estritamente os comandos SQL executados e seus parmetros, deixe marcadas apenas as trs primeiras opes: "Prepared Query Statements", "Executed Query Statements" e "Input Parameters".

5 - Linguagem SQL
Modelo de Dados Comandos DDL Comandos DML Exemplo: Consultando Vrias Tabelas Comandos de atualizao

Modelo de Dados
Durante o curso, iremos desenvolver um mini-sistema para Almoxarifado, onde iremos fazer o controle de sadas de um determinado produto. Isso envolve algumas tabelas adicionais no nosso banco de dados. O modelo abaixo mostra como so relacionadas as tabelas que sero utilizadas no exemplo: As tabelas deste modelo sero criadas utilizando a Linguagem SQL.

Comandos DDL
Os comandos DDL (Data Definition Language) so usados para definir a estrutura das tabelas e outros objetos do banco de dados.

Tipos de Dados
Cada coluna de uma tabela tem um tipo de dados [datatype], que determina que tipo de informao (caracteres, nmeros, datas/horas) pode ser colocada na coluna e quais as caractersticas desses dados. O tipo geralmente determinado quando a tabela criada. O padro ANSI SQL define alguns tipos para uso em vrios bancos de dados: Os tipos de dados existentes so: Para dados Tipo

Caractere char(n), varchar2(n) Numrico exato decimal(p,e) ou numeric(p,e) Numrico aproximado double precision, real Numrico inteiro int, smallint Data e hora datetime Para dados contendo caracteres, char(n) armazena um nmero fixo de caracteres. Por exemplo, uma coluna do tipo char(30) tem sempre 30 caracteres. Se forem informados menos, o restante completado com espaos. J o tipo varchar(n) armazena uma quantidade varivel de caracteres, at o mximo informado e no completa o texto com espaos. Os tipos "numricos exatos", decimal e numeric, permitem armazenar dados exatos, sem perdas devidas a arredondamento. Ao usar esses tipos, voc pode especificar uma preciso, que indica quantos dgitos podem ser usados no total e uma escala, que indica quantos dgitos podem ser usados direita do ponto. Por exemplo, decimal(9,2) permite guardar 7 dgitos antes do ponto decimal e 2 aps, num total de 9, assim o maior valor possvel 9999999,99. Os tipos "numricos inexatos", double precision (ou float) e real, armazenam dados numricos, mas nem sempre mantm a preciso suficiente para armazenar corretamente nmeros de vrios dgitos. Dos tipos inteiros, int usa 32 bits (4 bytes), permitindo armazenar at +/-2,147,483,647 e smallint usa 16 bits (2 bytes) permitindo +/-32767. O tipo datetime armazena informaes de data e hora juntas, com preciso de 1/300 de segundo, entre 1 de janeiro de 1753 e 31 de Dezembro de 9999 . Os comandos da linguagem SQL que iremos utilizar , podem ser executados no Explorer do Delphi.Vamos utilizar o alias OracleCurso

Criando Tabelas
Para criar uma tabela, usa-se o comando CREATE TABLE. Por exemplo, para criar a tabela 'Pessoa', a sintaxe : CREATE TABLE Pessoa ( CodPessoa Number NOT NULL , Nome varchar2 (50) NOT NULL , Sexo char (1) , Rua varchar2 (50) NULL , Bairro varchar2 (30) NULL , Cidade varchar2 (30) default ('Goinia') NULL, Estado char (2) NULL , CEP char (8) NULL , CPF char (11) NULL , DataCadastro date default (sysdate) NULL, ) As colunas para as quais a opo "NULL" no foi especificada permitiro o valor NULL, ou seja, no brigatrio informar um valor para elas (no caso sexo) . J as colunas marcadas com NOT NULL no permitem valores nulos, ou seja, no podem ser deixados sem preencher ao inserir dados (no caso CodPessoa, Nome). No Oracle, NULL a opo padro, sendo que no SQL Server NOT NULL a opo default. Para declarar que uma coluna pode ter valores nulos, preciso especificar a opo NULL. O tipo do campo DataCadastro 'date', (datetime no SQL Server) e para retornar a data atual, caso o usurio no informe, colocamos o valor default sendo a funo sysdate (o mesmo que getdate() no SQL Server). Note que definimos algumas colunas com o tipo char, como CPF e Estado, porque elas geralmente tm tamanho fixo. J outras como Nome, Cidade e Bairro, geralmente tm tamanho varivel, por isso, para economizar espao no banco de dados, usamos varchar2. A opo DEFAULT uma restrio numa coluna, como veremos mais tarde. Ela especifica um valor default que inserido caso nada tenha sido informado. No caso da data de cadastro, usamos o literal 'sysdate', que interpretado como a data atual. No caso da cidade, o default a string "Goinia" caso nada seja informado.

Excluindo uma tabela


Para excluir uma tabela, pode-se usar o comando DROP TABLE, por exemplo: DROP TABLE Pessoa

Alterando uma Tabela


O comando ALTER TABLE altera a estrutura de uma tabela. Ele pode ser usado para acrescentar , remover uma coluna e alterar o tipo ou opes de uma coluna. NO SQL Server 6.5 ou inferior, ALTER TABLE s possibilitava a insero ou remoo de colunas. No SQL Server 7.0, j possvel realizar todas as operaes. Para acrescentar uma nova coluna, usa-se o comando ALTER TABLE com a opo ADD. Por exemplo, vamos acrescentar uma coluna Fone e Fax tabela Pessoa: ALTER TABLE Pessoa ADD (Fone char (7) NULL , Fax char (7) NULL) O tipo de dados da coluna, especificado como no CREATE TABLE. Para alterar a estrutura da tabela acrescentando colunas, voc ter duas opes: Se a tabela estiver vazia, voc poder adicionar tanto colunas NULL quanto NOT NULL. Caso a tabela j conter dados, voc s poder adicionar colunas NOT NULL. No SQL Server, voc s pode adicionar campos que sejam NULL, ou seja, que aceitem valores nulos em seus dados. No SQL Server, como o comando ALTER TABLE no permite modificar uma coluna, necessrio excluir a tabela (DROP TABLE) e recri-la novamente com a estrutura modificada. Se a tabela contm dados, esses dados sero perdidos. O que se deve fazer renomear a tabela, criar uma nova com o nome desejado, e copiar os dados de uma para outra (veremos o processo para copiar dados mais tarde). Por todas essas razes, bom planejar bem as suas tabelas e as colunas de cada uma de acordo com os requisitos do sistema, antes de comear a implementar o seu banco de dados.

Definindo e Usando Restries [Constraints]


Uma restrio [constraint] uma propriedade de uma coluna usada para reforar a integridade de dados. Geralmente restries so definidas quando a tabela criada (com CREATE TABLE), mas podem tambm ser definidas ou retiradas quando a tabela j contm dados (com o comando ALTER TABLE). As restries so verificadas durante a execuo de um comando de alterao (INSERT ou UPDATE). Se o comando no satisfaz uma das restries, o comando cancelado. Toda restrio tem um nome, que voc pode informar nos comandos CREATE TABLE e ALTER TABLE. Se voc no informar um nome, o banco de dados gera um nome automaticamente, geralmente aleatrio. De forma geral, a sintaxe para uma restrio : CONSTRAINT nome_da_restrio definio Onde a definio inicia com as palavras PRIMARY KEY, UNIQUE, CHECK, FOREIGN KEY ou DEFAULT. A palavra CONSTRAINT e o nome_da_restrio podem ser omitidos. Nesse caso, o nome ser gerado automaticamente.

Chave Primria [PRIMARY KEY]


A chave primria [primary key] de uma tabela uma coluna ou seqncia de colunas que identificam unicamente uma linha dentro da tabela, ou seja, seu valor no pode ser repetido para outras linhas. Ao definir uma chave primria, automaticamente criado um ndice na tabela. S pode haver uma chave primria na tabela. Na criao da tabela, essa restrio pode ser definida da seguinte forma (quando a chave primria composta de uma s coluna), para mostrar iremos criar a tabela "COPIAFORNECEDOR" , onde a coluna 'CodFornecedor' ser definida como chave primria da tabela: CREATE TABLE CopiaFornecedor ( CodFornecedor number NOT NULL primary key, Nome varchar (50) NOT NULL ) Note que 'CodFornecedor' deve ter a opo NOT NULL. No possvel criar uma chave primria com colunas que podem ser NULL. Opcionalmente, poderia ser informado um nome para a restrio, por exemplo 'ChaveCopiaFornecedor'. Nesse caso, a segunda linha acima seria: CodFornecedor number not null constraint ChaveCopiaFornecedor primary key, Agora, na tabela Fornecedor, no pode haver duas linhas com o mesmo valor de 'CodFornecedor'. Quando a chave composta de duas ou mais colunas, nesse caso ela tem que ser especificada com a lista de colunas entre parnteses, por exemplo: create table ProdutoFornecedor ( CodProduto int NOT NULL, CodFornecedor int NOT NULL, primary key (CodProduto, CodFornecedor)

) Uma chave primria pode ser acrescentada tabela depois que ela j foi criada, com o comando ALTER TABLE. Por exemplo, vamos acrescentar chaves primrias tabela Pessoa : alter table Pessoa add primary key (CodPessoa) Em qualquer um dos casos pode-se especificar ou no o nome da restrio, na forma CONSTRAINT nome, por exemplo: alter table Produto add constraint PkProduto primary key (CodProduto) alter table Fornecedor add constraint PkFornecedor primary key (CodFornecedor) Para retirar uma restrio, use o comando ALTER TABLE...DROP CONSTRAINT..., por exemplo: alter table Produto drop constraint PkProduto

Unicidade [UNIQUE]
Uma restrio UNIQUE em uma coluna ou grupo de colunas determina que o seu valor deve ser nico na tabela. Esse tipo de restrio usado para chaves alternadas, ou seja, valores que se repetem na tabela alm da chave primria. Pode haver vrias restries UNIQUE na tabela e as colunas de uma restrio UNIQUE permitem valores nulos. Esse tipo de restrio pode ser criada com exatamente a mesma sintaxe do PRIMARY KEY, por exemplo : alter table Fornecedor add constraint UqNomeFornecedor unique (nome) Tambm criado um ndice automaticamente, que no permite valores duplicados.

Chave estrangeira [FOREIGN KEY]


Uma forma importante de integridade no banco de dados a integridade referencial, que a verificao de integridade feita entre duas tabelas. Por exemplo, a tabela 'Produto' ser usada para relacionar dados dos fornecedores de um determinado produto com a tabela 'Fornecedor'. Ela contm a coluna CodFornecedor . A coluna 'CodFornecedor' da tabela 'Produto' deve conter um cdigo vlido que exista na coluna 'CodFornecedor' da tabela 'Fornecedor'. A outra deve ter um cdigo vlido existente na tabela 'Fornecedor'. Como validar essa verificao? Uma chave estrangeira [foreign key] uma restrio de integridade referencial. Ela consiste de uma coluna ou grupo de colunas cujo valor deve coincidir com valores de outra tabela. No nosso caso, vamos adicionar uma chave estrangeira na tabela 'Produto' para a coluna 'CodFornecedor', ela ir fazer referncia a tabela poduto. Como a tabela produto j foi criada iremos alterar a estrutura desta tabela, portanto, iremos usar o comando Alter Table: alter table Produto add foreign key (CodFornecedor) references Fornecedor(CodFornecedor) Note que a chave primria de Fornecedor (CodFornecedor), como foi definido antes. Para poder criar uma chave estrangeira, preciso haver antes uma chave primria ou uma restrio UNIQUE, na tabela referenciada. Outra forma de criar restries durante a criao da tabela , ou seja, com o comando CREATE, ento vamos criar a tabela MovimentacaoProduto, onde a coluna 'CodProduto' esta relacionada com a tabela 'Produto': CREATE TABLE MovimentacaoProduto ( CodMovimentacao int NOT NULL constraint pkMovimentacaoProduto primary key, CodProduto smallint NOT NULL constraint fkCodProduto foreign key references Produto, DataMov datetime NULL default (getdate()), TipoMov char (1) NOT NULL , Quantidade int NOT NULL ) Uma chave estrangeira no cria um ndice automticamente no SQL Server. Mesmo assim recomendvel criar um ndice para maior desempenho. Pode-se especificar o nome da restrio opcionalmente, na forma CONSTRAINT nome, logo antes das palavras "FOREIGN KEY", como foi mostrado em : CodProduto smallint NOT NULL constraint fk_CodProduto foreign Key references Produto .

Default
Um default especifica um valor que ser fornecido para aquele campo. Na definio da tabela, como j vimos, possvel fazer isso: create table Pessoa ( ... DataCadastro datetime default sysdate, ... Cidade varchar(30) default ('Goinia')) O valor de um default pode ser uma constante ou uma chamada de funo, como sysdate (ou getdate(), no SQL Server) Pode-se tambm especificar ou no um nome para o default: Cidade varchar(30) constraint DefPais default 'Brasil' Voc pode acrescentar ou retirar um default no Oracle. Se o default for acrescentado com o comando ALTER TABLE, preciso usar o comando especificar para qual coluna ele vai ser ativado, por exemplo: ALTER TABLE Cliente MODIFY Estado char(2) default 'Go' Caso voc esteja fazendo no SQL Server, a sintaxe seria: ALTER TABLE Cliente ADD default 'GO' for Estado

Verificao [CHECK]
Uma restrio CHECK muito semelhante a uma regra, que verifica os valores que esto sendo inseridos. A vantagem que ele pode fazer referncia a uma ou mais colunas da tabela. Por exemplo, vamos verificar, na tabela 'MovimentacaoProduto', se o 'TipoMov' informado 'E' ou 'S' e se a coluna Sexo na tabela 'Pessoa' 'F' ou 'M'. ALTER TABLE MovimentacaoProduto ADD CHECK (TipoMov in ('E', 'S')) ALTER TABLE Pessoa ADD CHECK (Sexo in ('F', 'M')) Para testar, tente inserir uma linha com Sexo sendo 'G' . Esta linha no ser includa enquanto no for informado o sexo sendo 'F' ou 'M'. Note que a expresso do CHECK deve estar sempre entre parnteses. Pode-se especificar ou no o nome da restrio, na forma CONSTRAINT nome logo antes da palavra "CHECK".

Comandos DML
Os comandos DML (Data Manipulation Language) so utilizados para inserir novas linhas numa tabela, alterar colunas das linhas existentes, excluir linhas e consultar dados. Para realizar consultas nas tabelas , necessrio ter algumas informaes nestas tabelas , para isto acrescente alguns registros nestas tabelas, podemos utilizar o utilitrio 'SQL Explorer do Delphi'.

Comando SELECT - bsico


O comando SELECT consulta dados de uma ou mais tabelas. A sua sintaxe mais simples pode ser resumida da forma: SELECT lista_de_colunas FROM lista_de_tabelas WHERE condies A lista_de_colunas especifica quais colunas sero retornadas como resultado, separadas por vrgulas ou um asterisco (*) que indica todas as colunas da tabela. A clusula FROM, com uma lista_de_tabelas, especifica quais tabelas sero consultadas. E a clusula WHERE especifica condies que devem ser satisfeitas pelas linhas das tabelas.

Exemplo: Consultando Todas as Colunas


Para nossos exemplos, vamos usar o banco de dados curso , que o nosso banco de exemplo. Mas antes necessrio cadastrar informaes nas tabelas deste banco de dados. Pode ser feito utilizando a pasta 'Data' de cada tabela. Execute o comando abaixo no SQL Explorer (pgina Enter SQL) do Alias 'CursoOracle': select * from pessoa

O resultado ir mostrar todas as colunas e todas as linhas da tabela 'pessoa' (ou seja, todo o seu contedo). Vale lembrar que para poder consultar a tabela pessoa, voc dever ter direito de select sobre a mesma. No Oracle os nomes de tabela devem ser especificados da mesma forma com que foram criados e o nome do autor deve ser includo, caso no seja o mesmo que se esteja logado. Por exemplo: Se voc estiver usando o Alias 'CursoOracle', feche o mesmo, e reabra-o usando um usurio diferente. Voc ver que este usurio agora no tem acesso s mesmas tabelas que o usurio anterior tinha. Suponha agora a existncia de duas tabelas 'Pessoa', uma criada por Joo e outra por Maria. Para Joo consultar sua tabela ele teria que fazer: select * from Pessoa Agora, para consultar a tabela Pessoa criada por Maria, ele dever executar: select * from Maria.Pessoa O '*' no comando acima especifica que todas as colunas so retornadas, mas voc pode listar s as que so desejadas. Continue na pgina 'Enter SQL' , e altere o comando anterior para o seguinte: select CodPessoa, Nome, Sexo from Pessoa Execute o comando. Agora apenas as colunas 'codpessoa', 'nome' e 'sexo' so retornadas, nessa ordem. Note que a ordem das colunas no precisa ser a mesma ordem presente na definio da tabela. De fato, na maioria das aplicaes, a ordem das colunas na tabela no tem importncia. Voc tambm pode mudar o cabealho das colunas retornadas, criando um alias de coluna. Execute o seguinte comando: select CodPessoa Cdigo, Nome, Sexo from Pessoa O resultado ser o mesmo do comando anterior, mas a coluna 'codpessoa' aparece como Cdigo.

Usando Condies
Os comandos que j usamos no tm a clusula WHERE. Nesse caso, todas as linhas da tabela so retornadas. Se o WHERE estiver presente, ele especifica uma condio que seleciona as linhas e apenas as que satisfazem essa condio sero mostradas. Por exemplo, se quisermos as pessoas que moram em 'Goinia', podemos consultar as linhas cuja coluna 'cidade' tem o valor 'Goinia': select Nome from Pessoa where Cidade = 'Goinia' Note que o resultado ir mostrar somente as pessoa que moram em 'Goinia'. As linhas que aparecem so apenas as que satisfazem a consulta. Existem vrios tipos de condies de pesquisa, como veremos.

Manipulando Expresses
Um comando SELECT pode retornar nas colunas de resultado uma coluna da tabela, ou um valor calculado. Por exemplo, a tabela Produto contm os produtos cadastrados na empresa, com sua quantidate em estoque. Se quisermos ver como fica a quantidade em estoque aps um acrescimo de 10% do que j existe, pode ser feito o seguinte: select codproduto, quantEstoque, (quantEstoque * 1.1) from produto Ou seja, a terceira coluna mostra o resultado de (quantEstoque * 1.1) para cada linha. Voc pode tambm usar vrios operadores em expresses com colunas numricas: adio (+), subtrao (-), multiplicao (*) e diviso (/). A idia de mostrar o comando select somente para voc ter um conhecimento bsico com este comando, mas podem ser realizados vrias consultas complexas com ele.

Vises [views]
Uma viso [view] uma forma alternativa de olhar os dados contidos em uma ou mais tabelas. Para definir uma viso, usa-se um comando SELECT que faz uma consulta sobre as tabelas. A viso aparece depois como se fosse uma tabela. Vises tem as seguintes vantagens: Uma viso pode restringir quais as colunas da tabela que podem ser acessadas (para leitura ou para modificao), o que til no caso de controle de acesso, como veremos mais tarde. Uma consulta SELECT que usada muito freqentemente pode ser criada como viso. Com isso, a cada vez que ela necessria, basta selecionar dados da viso. Vises podem conter valores calculados ou valores de resumo, o que simplifica a operao. Uma viso pode ser usada para exportar dados para outras aplicaes.

Vamos criar uma viso no nosso banco de dados, onde iremos mostrar quais os vendas dos produtos em um determinado perodo . Como vimos, as vises so criadas atravs do comando SELECT. Mas para realizar esta consulta iremos precisar utilizar as tabelas MovimentacaoProduto e Produto. Para iso, podemos usar um comando select do SQL envolvendo vrias tabelas, chamado de juno de tabelas.

Exemplo: Consultando Vrias Tabelas


Um comando SELECT tambm pode fazer uma consulta que traz dados de duas ou mais tabelas . Esse um processo chamado de juno[join]. As tabelas tm uma coluna em comum que usado para fazer as junes.Para realizar a consulta onde iremos mostrar os produtos vendidos e qual a sua quantidade disponvel em estoque, temos que executar o comando SELECT abaixo: select distinct prod.CodProduto, Nome, Quantidade QuantVendida,DataMov Data from MovimentacaoProduto mov , Produto prod where mov.CodProduto = prod.CodProduto Note que na clusula FROM , os nomes das tabelas esto seguidos de nomes mais curtos, que so os apelidos [alias] utilizados para as tabelas dentro do SQL. Esses apelidos afetam apenas o comando atual. A lista de campos do SELECT seleciona a coluna 'codProduto' da tabela 'Produto', neste caso, foi necessrio informar a tabela e a coluna, porque a coluna codproduto pode ser encontrada na tabela 'MovimentacaoProduto' e 'Produto'. Com relao a juno de tabelas, acrescentamos todas as tabelas que queremos utilizar na clusula FROM , temos tambm que colocar uma condio na clusula WHERE que ir fazer a juno das duas tabelas. A condio que utilizamos foi 'mov.codproduto = prod.codproduto'. Ela diz qual a ligao entre elas. Se no for especificada , a consulta vai funcionar, mas vai retornar o produto cartesiano das duas tabelas, ou seja, todas as combinaes possveis com o registro de uma e o registro da outra tabela. Se no utilizar o comando distinct a resposta ter o nome dos produtos repetidos vrias vezes,este comando mostra somente as linhas que so diferentes. Esta consulta ir mostrar o resultado independente da data. Porque o importante o usurio escolher qual o perodo que ele deseja visualizar, e isso ser feito pelo programa.

Criando uma viso


A viso que iremos criar ser gerada atravs do comando SELECT feito no exemplo anterior. E iremos mostrar como chamar esta viso a partir do Delphi. Para criar um viso temos que utilizar o comando CREATE VIEW, execute o comando abaixo: Create View VendaProduto as select distinct prod.CodProduto, Nome,Quantidade QuantVendida, DataMov Data from MovimentacaoProduto mov , Produto prod where mov.CodProduto = prod.CodProduto Como desejamos obter os dados por perodo e pretendemos colocar este parmetro no Delphi , por isso acrescentamos a coluna 'Data' no comando SELECT. Podemos testar o resultado desta viso no SQL Explorer, basta executar o comando abaixo: select * from VendaProduto O resultado ter as colunas 'CodProduto', 'Nome', 'QuantVendida' e 'Data' , mostrando os dados relacionados entre as tabelas. Para excluir uma viso , pode ser usado o comando DROP VIEW: drop view VendaProduto

Chamando uma Viso no Delphi


Abra o projeto 'ALMOXARIFADO' e crie um novo formulrio. Altere o nome do formulrio para 'formVendaProduto' , altere a propriedade Caption para 'Produtos Vendidos'. O componente utilizado para visualizar o resultado de uma viso o componente Query (), pois neste componente podemos obter dados baseados em um comando SQL. Abra o DataModule 'DMAlmoxarifado' e acrescente um componente Query . Altere a propriedade Name para qryProdutoVenda e a propriedade DatabaseName para DBAlmoxarifado. Como podemos realizar consultas utilizando vises (sendo mais utilizadas para este caso) , e a propriedade SQL utilizada para colocar o comando SQL que desejamos executar , vamos acrescentar o comando abaixo nesta propriedade: Select * from VendaProduto where Data =:ParamData

Order by Nome Na clusula WHERE criamos o parmetro chamado 'ParamData'. O que define o parmetro so os dois-pontos antes do nome. O parmetro foi criado para o usurio informar o perodo que deseja realizar a consulta. A coluna 'Data' uma das colunas que a viso VendaProduto retorna. Temos que usar este nome porque a coluna 'DataMov' na viso foi alterada para 'Data'. As consultas em vises podem ser feitas seguindo o mesmo critrio para tabelas, desde que utilize somente as colunas de retorno. Foi acrescentada o clusula ORDER BY para que os valores de retorno fiquem ordenados pelo nome do produto. Selecione a propriedade Params do componente QryVendaProduto e abra seu editor. O editor de parmetros o local que define o tipo de dado do parmetro , primeiro deve-se escolher o parmetro como mostra a figura abaixo: Clique no parmetro 'ParamData' em seguida posicione no object inspector e altere a propriedade Value em Type escolha "Date", que ser o tipo de dados do campo, ao confirmar o Object Inspector ir ficar como a figura abaixo: Observao: Se voc est usando o SQL Server, a propriedade DataType a ser informada ser 'ftDateTime'. Vamos retornar ao formulrio 'formVendaProduto' e acrescentar os componentes abaixo : (DataSource) : Name: dsProdutoVenda DataSet: DMAlmoxarifado.qryVendaProduto (DBGrid) DataSource: dsProdutoVenda (Panel) Caption: '' Name: mskData EditMask: !99/99/0000;1;_ (Button) Name : btnProcurar Caption: Procurar (Label) Caption: Data: Note que na propriedade DataSet do componente dsProdutoVenda , o seu contedo DMAlmoxarifado.qryVendaProduto, porque este componente foi criado no Data Module 'DMAlmoxarifado'. Crie um procedimento para o evento OnClick do btnCalcular , nele iremos executar a consulta, acrescente o cdigo abaixo: try with DMAlmoxarifado.qryVendaProduto do begin close; Params[0].asdate := strtodate(mskData.text); Open; end; except On EConvertError do begin showmessage('Data Invlida!'); mskData.setFocus; end; end; Se o usurio informar uma data errada ou se no informar nenhum valor para data e clicar no boto 'Calcular' , ir aparecer uma mensagem de erro em ingls dizendo que a data invlida, para mostrar nossa prpria mensagem de erro , fizemos o tratamento de erro com o comando Except e a classe testada EConvertError. A propriedade Params do componente Query um vetor de objetos do tipo TParam, com ndices comeando de zero. Cada um dos objetos tem propriedades com AsString, AsInteger, ... que permitem alterar o valor do parmetro.

Para testar este exemplo falta acrescentar no menu principal a chamada deste formulrio. Como todos os nossos formulrios esto sendo criados dinamicamente iremos fazer o mesmo para este formulrio. Primeiro precisamos retirar este formulrio da lista "Auto-Create", em seguida no evento OnClick do item de menu Consulta|Venda por Produto . Faa o seguinte: if not FormularioAberto(FormVendaProduto) then FormVendaProduto:=TFormVendaProduto.Create(Application); FormVendaProduto.Show; Retorne ao formulrio 'formVendaProduto' e crie um procedimento para o evento OnClose. Acrescente o cdigo abaixo: procedure TformVendaProduto.FormClose(Sender: TObject;var Action:TCloseAction); begin Action := caFree end; Execute e teste o formulrio , informe uma data que foi efetuado alguma venda e pressione o boto 'Procurar'. Notas: Uma viso tambm pode ser acessada com um componente Table, como se fosse uma tabela. Os nomes das vises no banco de dados aparecem na lista de nomes da propriedade TableName, como se fossem tabelas.

Comandos de atualizao
O comando SELECT usado para consultar uma ou mais tabelas ou vises, produzindo como resultado um conjunto de linhas e colunas. Mas para modificar os dados no banco de dados, existem os comandos INSERT, UPDATE e DELETE.

Inserindo Linhas
O comando INSERT insere linhas em uma tabela ou viso, mas o comando SELECT que gerou a viso no pode ter mais de uma tabela na clasula FROM. A forma mais simples do comando INSERT insere uma linha . Para inserir uma linha na tabela Fornecedor, execute o seguinte: insert into Fornecedor values (7, 'Fornecedor 7 ') Nesse caso, so informados os valores de todas as colunas da tabela, na ordem em que elas foram definidas na tabela. Mas possvel tambm inserir dados parciais de apenas algumas colunas. Para testar, iremos incluir uma linha na tabela pessoa, execute os comando abaixo: insert into Pessoa (CodPessoa, Sexo,Nome) values (8, 'F', 'Oitava Pessoa') Nesse caso, os nomes das colunas que sero inseridas so especificados entre parnteses aps o nome da tabela. A ordem no precisa ser a mesma das colunas na tabela. Mas a ordem dos valores em VALUES corresponde ordem dos nomes de colunas informados. Quando uma coluna omitida da lista, o que acontece : Se a coluna tem um valor default (como Cidade e DataCadastro), o valor default inserido Caso contrrio, se a coluna permite valores NULL, ser inserido um NULL. Caso a coluna no tenha default e tenha sido criada como NOT NULL, a execuo do comando ser cancelada com uma mensagem de erro. Digite agora SELECT * from Pessoa. Voc ver que a pessoa 8 est com a cidade "Goinia", j que o valor no foi informado para a coluna Cidade e o default "Goinia".

Atualizando Linhas
Para atualizar linhas de uma tabela ou viso baseados em condies, use o comando UPDATE. Na alterao de vises caso a clausula FROM do comando SELECT que gerou a viso possui mais de uma tabela, a atualizao pode ser feita somente nas colunas especficas de uma das tabelas a cada comando utilizado. No nosso caso, vamos assumir que quando a cidade for 'Goinia' iremos colocar o Estado sendo 'Go'. No banco de dados Curso, execute: update Pessoa set Estado = 'Go' where Cidade = 'Goinia' As condies que podem aparecem no WHERE so idnticas s condies do comando SELECT. Ao usar o comando UPDATE, caso alguma restrio de integridade tenha sido violada, a atualizao inteira cancelada, no apenas a linha que provocou o erro.

Um comando de atualizao tambm pode usar uma sub-consulta (um SELECT dentro de parnteses) para buscar dados em outras tabelas antes fazer a atualizao.

Excluindo Linhas
O comando DELETE exclui permanentemente uma ou mais linhas de uma tabela ou viso, baseado em alguma condio.Na excluso de linhas de uma viso o comando SELECT que a gerou no pode ter mais de uma tabela na clasula FROM. Por exemplo, para excluir a Pessoa n 8 (CodPessoa =8), execute o seguinte comando no banco de dados Curso do SQL Server: delete from Pessoa where CodPessoa = 8 Note que a excluso no pode ser desfeita.

6 - Programao Cliente / Servidor


Usando os componentes do Delphi Usando transaes Salvando movimentaes de produtos Usando o componente Query

Usando os componentes do Delphi


Os mesmos componentes do Delphi que podem ser usados em ambiente desktop, Database, Table, Query e DataSource, podem ser usados tambm num ambiente Client/Server. Mas nesse caso existem alguns detalhes que importante analisar.

O componente Table
Todas as operaes num banco de dados cliente/servidor, como incluses, alteraes e excluses de registros, devem ser feitas atravs de comandos SQL. Mas o componente Table esconde esse fato do programador. Voc pode usar os mtodos normais do Delphi para navegar pela tabela (First, Last, Prior, Next e outros), pesquisar valores (FindKey e FindNearest), e modificar os dados (Insert, Delete, Edit, Post e Cancel). Ele automaticamente gera e executa os comandos SQL necessrios. Por exemplo, suponha uma tabela chamada 'Professor' com os campos: CodProfessor, Nome, Telefone, onde a chave primria 'CodProfessor'. Imagine as seguintes situaes: 1) O programa abre a tabela. O Delphi gera um comando SELECT para buscar os registros, como: select CodProfessor, Nome, Telefone from Professor 2) O usurio clica no boto para iniciar a incluso de um registro (o programa chama o mtodo Insert). Ele informa o cdigo 12 para o professor, o nome "Joo da Silva" e o telefone "222-3333". Depois ele clica no boto do DBNavigator (que chama o mtodo Post). O SQL gerado : insert into Professor (CodProfessor, Nome, Telefone) values (12, 'Joo da Silva', '222-3333') 3) O usurio comea a modificar esse mesmo registro que j foi salvo (o programa chama o mtodo Edit). Ele altera o telefone para 222-4444 e salva o registro (o programa chama o mtodo Post). O SQL gerado : update Professor set Telefone = '222-4444' where CodProfessor = 12 4) O usurio exclui esse mesmo registro anterior (o programa chama o mtodo Delete). O SQL executado : delete from Professor where CodProfessor = 12 O componente Table busca toda a informao do esquema da tabela (ou seja, a estrutura da tabela, com os tipos de dados de cada campo) quando ele ativado. Isso permite que o acesso aos dados seja mais rpido.

Filtrando Linhas
Voc pode tambm (e geralmente deve) filtrar registros da tabela, em vez de retornar todos os dados. Isso feito com a propriedade Filter do componente. Essa propriedade gera uma clusula WHERE no comando SELECT de consulta. Ela s tem efeito se a propriedade Filtered estiver com o valor True. altamente recomendvel usar Filter para evitar uma consulta que retorna todos os registros da tabela. No caso de tabelas grandes, trazer todos os registros consume recursos desnecessrios do SQL Server. Voc pode restringir os registros por algum critrio, dependendo da aplicao. Por exemplo, voc pode mostrar as letras do alfabeto e trazer apenas os nomes que comeam com a letra escolhida. No exemplo da tabela acima, se Filtered = True, e Filter contm Nome = 'A*', por exemplo, o Delphi gera um comando de consulta da forma: select CodProfessor, Nome, Telefone from Professor where Nome like 'A%' Note que a sintaxe do Filter nesse caso diferente da sintaxe do LIKE.

Usando o componente Table


Abra o projeto ALMOXARIFADO.DPR . Crie um fomulrio. Nele iremos usar as duas formas de acesso a uma tabela: com o componente Table e com o componente Query. Coloque no formulrio um componente PageControl. Modifique a propriedade Align para alBottom e crie duas pginas. Para criar um pgina clique com o boto direito do Mouse e escolha a opo 'New Page'. Na primeira pgina, coloque o Caption= "Table", na outra Caption= "Query". Vamos alterar algumas propriedades do fornulrio. Altere o nome para 'formMovProduto' e o caption para 'Movimentao Produto'. Salve o formulrio como 'DMOVPRODUTO.PAS'. No Data Module 'DMAlmoxarifado' acrescente um componente Table, com Name=tblMovProduto, DatabaseName=DBAlmoxarifado e TableName=MovimentacaoProduto (ou "dbo.MovimentacaoProduto", como aparece na lista) . necessrio relacionar a tabela PRODUTO com a tabela MOVIMENTACAOPRODUTO , pois, iremos chamar o formulrio 'formMovProduto' a partir do 'formProduto', ao cadastrar uma movimentao, iremos realizar a sada ou entrada para o produto que estiver posicionado na tabela Produto'. Como a tabela MOVIMENTACAOPRODUTO a tabela subordinada e a tabela mestre PRODUTO , vamos associar a propriedade MasterSource do componente tblMovProduto ao componente dsGenerico do fomulrio 'formProduto', seu contedo ser MasterSource = formProduto.dsGenerico. Na propriedade MasterField iremos colocar as colunas que permitem o relacionamento, como o nosso banco de dados Client/Server, as colunas que fazem parte do relacionamento no precisa ter ndices criados, como ocorre no Paradox, mas se tiver seu relacionamento pode se tornar mais rpido. Ento na propriedade MasterField associe a coluna CodProduto da tabela PRODUTO a coluna CodProduto da tabela MOVIMENTACAOPRODUTO. Retorne ao formulrio 'formMovProduto' e acrescente um DataSource, com Name=dsMovProdutoTable e DataSet= DMAlmoxarifado.tblMovProduto. Acrescente no formulrio um componente label altere a propriedade Caption para 'Produto' , coloque tambm um componente DBEdit , altere sua propriedade DataSource para 'formProduto.dsGenerico' , na propriedade DataField coloque a coluna 'Nome', altere a propriedade ReadOnly para 'True'. Falta acrescentar os controles de dados ao formulrio 'formMovProduto', ento vamos retornar ao formulrio DMAlmoxarifado e criar objetos de campos para o componente tblMovProduto, em seguida selecione todos os objetos de campo exceto 'codproduto' , depois arraste os objetos de campo para o formulrio 'formMovProduto'. Ele ter a aparncia como a figura abaixo: Como a coluna 'TipoMov' tem dois valores vamos substituir o componente 'DBEdit' pelo componente 'DBRadioGroup' . Retire o componente DBEdit que esta mostrando o 'TipoMov' e o label cujo caption 'TipoMov'. Acrescente o componente 'DBRadioGroup' (). Na propriedade Items acrescente 'Entrada' na linha0 e 'Sada' na prxima linha. Altere tambm a propriedade Values para 'E' na linha0 e 'S' na prxima linha. Coloque a propriedade Caption vazia.Na propriedade DataSource altere para 'dsMovProdutoTable' e DataField especifique a coluna 'tipomov'. Na pgina "Table", coloque os seguintes controles: 3 componentes Buttons e 1 DBNavigator , altere as propriedades de acordo com a tabela abaixo: Button1 Caption Incluir Name btnIncluir Button2 Caption Salvar Name btnSalvar

Button3 Caption Name DBNavigator1 DataSource

Cancelar btnCancelar dsMovProdutoTable nbInsert = False nbDelete = False nbEdit = False nbPost = False nbCancel = False nbRefresh = False

VisibleButtons

Dependendo do estado da tabela , iremos desabilitar ou habilitar os botes de inclui, salvar e cancelar. Crie um procedimento para o evento OnStateChange do componente dsMovProdutoTable e coloque o cdigo abaixo: procedure TformMovProduto.dsMovProdutoTableStateChange(Sender:TObject); begin btnIncluir.Enabled := dsMovProdutoTable.State = dsBrowse; btnSalvar.Enabled := dsMovProdutoTable.State = dsInsert; btnCancelar.Enabled := btnSalvar.Enabled; end; No evento OnClick do boto btnIncluir acrescente o cdigo a seguir : procedure TformMovProduto.btnIncluirClick(Sender: TObject); begin DmAlmoxarifado.tblMovproduto.insert end; No envento OnClick do boto btnCancelar coloque o cdigo abaixo: procedure TformMovProduto.btnCancelarClick(Sender: TObject); begin DmAlmoxarifado.tblMovproduto.cancel end;

Usando Transaes
Muitas vezes necessrio garantir a consistncia do banco de dados mesmo em caso de falha. Para cada atualizao individual de registro, o Delphi garante que ela ser completada imediatamente, mas muitas vezes vrias atualizaes precisam ser feitas em conjunto para manter os dados consistentes. Se h uma falha no meio do processo, os dados esto invlidos. Uma transao um grupo de aes que devem ser todas executadas com sucesso, ou todas so canceladas. Por exemplo, no nosso sistema necessrio controlar a quantidade disponvel em estoque dos produtos. Suponhamos que foi realizada uma venda, necessrio subtrair a quantidade vendida da quantidade existente em estoque. Isso envolve atualizaes em duas tabelas: primeiro salvar o registro includo na tabela 'MOVIMENTACAOPRODUTO' e depois subtrair a quantidade vendida pela quantidade existente em estoque na tabela 'PRODUTO' . Se apenas a primeira atualizao completada iremos vender um produto e no iremos atualizar o estoque, com isso o estoque no ir bater com as quantidades vendidas deste produto, gerando inconsistncia no banco de dados. Voc inicia uma transao com o mtodo StartTransaction do componente Database, e finaliza com Commit, por exemplo: dbAlmoxarifado.StartTransaction; {atualiza os registros} ... dbAlmoxarifado.Commit; Dentro da transao, qualquer alterao no banco de dados (incluso, alterao ou excluso de registros) ser registrada num log. Se houver uma falha no meio da transao, todas as alteraes sero canceladas. Quando voc chama o mtodo Commit, todas as alteraes sero confirmadas e se tornam permanentes. Se em vez de confirmar, voc quiser cancelar a transao no meio, pode usar o mtodo Rollback, que cancela todas as atualizaes feitas: dbAlmoxarifado.Rollback; Lembre-se que uma transao num componente Database, por exemplo, 'dbAlmoxarifado', s afeta os componentes Table ou Query que estejam ligados ao componente 'dbAlmoxarifado', ou seja , que tenham a propriedade DatabaseName igual a ' dbAlmoxarifado'.

Para saber se uma transao est em curso atualmente, consulte a propriedade InTransaction do componente Database (True indica que h uma transao ocorrendo). Os mtodos de transao tambm equivalem a comandos SQL, que podem ser usados em procedimentos: Mtodo Comando ANSI-SQL StartTransaction BEGIN TRANSACTION Commit COMMIT TRANSACTION Rollback ROLLBACK TRANSACTION

Nveis de Isolamento
Se vrios usurios abrem transaes simultaneamente no mesmo banco de dados, uma transao pode interferir com a outra de vrias formas. Por exemplo, um usurio A inicia uma transao no tempo t1 e por alguma razo executa um rollback no tempo t3. Enquanto isso uma transao do usurio B l os dados que foram modificados (ou novos registros includos) no tempo t2: A ------ t1:StartTrans., depois altera dados ----------------- t3:Rollback----B-------------------------------------------- t2:l dados alterados ---------------------------Se 'A' modifica um registro, mas ele ainda no foi confirmado [commited] no banco de dados, ento 'B' leu dados invlidos, que depois foram cancelados. Esse problema tem duas variantes: quando 'B' l registros modificados por A chamado de leitura suja [dirty read]. Quando 'B' l um registro includo por 'A', que depois desaparece, chamdo de valor fantasma [phantom value]. Esses problemas acontecem porque um programa pode ler dados que ainda no foram confirmados por uma transao. Outro problema pode acontecer se uma transao precisa ler dados e modificar esses dados. No intervalo entre a leitura e a modificao, outro usurio altera esses dados, por exemplo: C ---- t1:Start.-----t2:l registro X-------t4:altera registro Y----t5:Commit D ----------------------------------------------t3: altera X-------------------------------------No caso, primeiro 'C' l os dados de um registro X do banco de dados. Logo aps, 'D' altera esses dados. E depois 'C' usa os dados lidos para alterar outro registro Y, mas eles no so mais vlidos. Esse problema chamado de leitura no-repetvel [non-repeatable read]. Para evitar esses problemas, existem vrios nveis de isolamento de transaes. Voc escolhe o nvel mais adequado para garantir a consistncia do banco de dados. Esses nveis podem ser definidos com um comando SQL ou com a propriedade TransIsolation do componente Database: Nome SQL Ao READ (TransIsolation=tiDirtyRead). Nesse nvel podem acontecer leituras sujas [dirty reads], valores UNCOM fantasmas e leituras no-repetveis. MITED READ (TransIsolation=tiReadCommited). Nesse nvel um usurio s consegue ler um valor que j foi COMMIT commited [confirmado] por uma transao. Mas pode acontecer o problema de leituras no-repetveis. ED REPEAT ABLE (TransIsolation=tiRepeatableRead). Nesse nvel no acontece nenhum dos problemas mencionados READ SERIALI O mesmo que REPEATABLE READ ZABLE Os nveis de isolamento so implementados pelo SGBD com bloqueios de registro. Se uma transao est em modo READ UNCOMMITED, o nvel mais baixo de isolamento, no ocorre bloqueio automtico de registros. Se uma transao roda no nvel READ COMMITED, ento qualquer registro modificado pela transao ser mantido bloqueado at a transao terminar com Commit ou Rollback. Esse bloqueio evita que outros usurios leiam valores ainda no confirmados. Se o nvel de isolamento for REPEATABLE READ ou SERIALIZABLE (que para o Delphi so o mesmo), ento vale o que foi dito para READ COMMITED e alm disso qualquer registro lido dentro da transao ser bloqueado at a transao terminar. Esse bloqueio evita que os registros usados pela transao sejam modificados depois que ela leu. Usar um nvel mais alto de bloqueio tem a vantagem de manter a consistncia do banco de dados, mas tem o potencial de diminuir a possiblidade de acesso simultneo aos dados.

Otimizando Transaes
Como uma transao pode colocar bloqueios em vrios registros, dependendo do nvel de isolamento, voc deve criar transaes que terminam no menor tempo possvel. Se voc deixa uma transao aberta por muito tempo, ela vai "segurar" outros usurios que dependem dos registros usados por ela.

Principalmente, no faa uma transao que depende da vontade do usurio para executar o Commit ou Rollback, ou seja, no deixe a transao aberta, esperando a resposta de uma mensagem ou evento.

Oracle e Transaes
Uma transao um mecanismo usado para manter a consistncia do banco de dados. Aps iniciar uma transao, todos os dados so gravados "condicionalmente", com a opo de confirmar a transao (COMMIT) ou cancelar toda a transao (ROLLBACK). Normalmente o Delphi usa AUTOCOMMIT - ele faz um COMMIT no banco de dados automaticamente aps cada modificao. Para modificar isso, use o mtodo StartTransaction do componente Database, antes de fazer as modificaes. Aps terminar as modificaes de dados, use o mtodo Commit, que confirma tudo o que foi gravado desde o StartTransaction. Se quiser cancelar a transao, use o mtodo Rollback, que cancela tudo o que foi gravado desde o StartTransaction. Em alguns bancos de dados, se um usurio (A) inicia uma transao e comea a alterar ou incluir registros, outro usurio (B) pode ver essas alteraes ou incluses, mesmo que a transao no tenha sido confirmada. Esse problema chamado de leitura suja [dirty read] ou de valor fantasma [phantom value]. Esse problema no ocorre no Oracle. Em muitos casos, se uma usurio (A) l dados dentro de uma transao, ele precisa que esses dados sejam consistentes at o trmino da transao. Se os dados forem alterados depois, isso cria o problema de leitura no repetvel [non-repeatable read]. O Delphi suporta trs nveis de isolamento de transaes, definidos pela propriedade TransIsolation do componente Database. O suporte a esses nveis depende do banco de dados. Eles so: tiDirtyRead, tiReadCommited, e tiRepeatableRead. O Oracle at a verso 7.2 suporta dois tipos de transaes: READ/WRITE e READ ONLY. O tipo de transao pode ser definido tambm com o comando SQL SET TRANSACTION do Oracle: Uma transao READ/WRITE pode modificar os dados, mas pode haver o problema de leituras norepetveis. Esse nvel corresponde a TransIsolation=tiReadCommited no Delphi. Se voc fizer TransIsolation=tiDirtyRead num banco de dados Oracle, isso no tem nenhum efeito e o Delphi funciona como se estivesse tiReadCommited. Uma transao READ ONLY somente leitura - ela no pode modificar os dados. Ela pode ser finalizada com Commit ou Rollback indiferentemente. Em compensao, o Oracle garante que no ocorre o problema de leituras no-repetveis. Os dados lidos pela transao so bloqueados e no podem ser modificados por outros usurios at que ela termine. Esse nvel equivale a TransIsolation=tiRepeatableRead no Delphi. O Oracle a partir da verso 7.3 suporta um outro tipo de transao. Com o comando SET TRANSACTION ISOLATION LEVEL, voc pode definir a transao como READ COMMITED (default) ou SERIALIZABLE. No modo SERIALIZABLE, uma transao (A) no pode modificar dados que foram alterados por outra transao (B), enquanto a transao A no terminar. Se ela tentar modificar esses dados, recebe um erro de execuo. Esse nvel ainda no suportado pelo Delphi/BDE, mas voc pode enviar um comando diretamente ao Oracle, usando um componente Query com: SET TRANSACTION ISOLATION LEVEL SERIALIZABLE ou SET TRANSACTION ISOLATION LEVEL READ COMMITED

Salvando Movimentaes de Produtos


Para salvar a movimentao de um determinado produto necessrio ter o cuidado de no registrar a compra ou venda de um produto sem atualizar o seu estoque. Para que isto no ocorra utilizaremos transaes. Se, durante a alterao do produto, for feita alguma leitura por outro usurio podemos ter problemas , por exemplo: Vamos supor que um usurio A deseja comprar um determinado Produto , mas a quantidade em estoque deste produto 5, e ele deseja comprar 5 unidades. Suponhamos que no momento em que a venda est sendo realizada, um usurio B deseja comprar 3 unidades do mesmo produto . Se for realizada a venda para o usurio A, este produto ficar em falta no estoque. Se o usurioB conseguir obter a quantidade em estoque do produto antes que o usuarioA confirme as alteraes, para ele este produto tem 5 Unidades , o que no verdade. Para evitar o problema citado iremos alterar a propriedade TransIsolation para 'tiReadCommited'' antes de inicializar a transao e depois vamos retornar ao seu valor default que 'tiDirtyRead'. No evento OnClick do boto btnSalvar coloque o cdigo abaixo: with DmAlmoxarifado do begin

dbAlmoxarifado.TransIsolation := tiReadComitted; try dbAlmoxarifado.StartTransaction; // Inicializa Transao tblMovProduto.post // Salva o registro da tabela MovimentacaoProduto tblProduto.edit; // Colocar tabela produto em modo de edio if tblMovProdutoTipoMov.value = 'E' then tblProdutoQuantEstoque.value := tblProdutoQuantEstoque.value + tblMovProdutoQuantidade.value else begin if tblProdutoQuantEstoque.value < tblMovProdutoQuantidade.value then begin showmessage('Quantidade Invlida!'); dbAlmoxarifado.RollBack; // Cancela Transao abort; end; tblProdutoQuantEstoque.value := tblProdutoQuantEstoque.value - tblMovProdutoQuantidade.value; end; tblProduto.post; dbAlmoxarifado.Commit; // Confirma a Transao Except on EDBEngineError do begin dbAlmoxarifado.Rollback; // Cancela Transao abort; end; end; dbAlmoxarifado.TransIsolation := tiDirtyRead end; Se o tipo da movimentao for entrada iremos acrescentar a quantidade que foi comprada ao estoque, caso seja sada iremos retirar a quantidade vendida ao estoque.Todas estas alteraes sero feitas somente se o componente tblMovProduto estiver no modo de insero. No iremos permitir alterao de um determinado movimento . Caso tenha feito o cadastro errado necessrio excluir e incluir novamente. Para que a alterao no seja permitida altere a propriedade AutoEdit para False no componente dsMovProdutoTable do formulrio 'formMovProduto'. Observao: Usamos o nvel de transao tiReadCommited porque para usarmos tiRepeatAbleRead teramos que processar uma query com o comando 'SET TRANSACTION ISOLATION LEVEL SERIALIZABLE'. No SQL Server o nvel de isolamenteo tiRepeatAbleRead poderia ser usado sem nenhum esforo a mais. Como estamos utilizando a classe 'EDBEngineError' , ento, temos que acrescentar a unit 'dbTables' na unit 'DMovProduto'. Com o tratamento de excees qualquer erro que ocorrer durante a transaes , iremos cancelar a transao. Notas: Para excluir ou estornar uma determinada movimentao de um produto segue a mesma idia utilizada para salvar. Notas: Quando utilizamos banco de dados client/server as operaes utilizadas durante a alterao ou excluso de uma movimentao podem ser feitas utilizando triggers(seria a melhor opo).

Chamando o Cadastro de Movimentaes do Produto a partir do Cadastro de Produtos


Vamos abrir o formulrio 'formProduto' e acrescentar um componente Button. Altere a propriedade Name para btnMovProduto, Caption para 'Movimentaes' e no evento OnClick , acrescente o cdigo abaixo: procedure TformProduto.btnMovProdutoClick(Sender: TObject); begin inherited; if DMAlmoxarifado.tblProduto.RecordCount = 0 then begin showMessage('No possvel chamar movimentaes! necessrio cadastrar um produto antes.');

end;

exit;

if not FormularioAberto(FormMovProduto) then FormMovProduto:=TFormMovProduto.Create(Application); FormMovProduto.Show; end; Como a funo FormularioAberto est na unit 'Menu' , e no foi definido que outras units podem utiliz-la, vamos declar-la na seco interface desta unit, conforme o cdigo abaixo: interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, Menus; type ... end; function FormularioAberto(F:TForm):boolean; var formPrincipal: TformPrincipal; No uses da seo implementation deste formulrio acrescente a Unit 'DMOVPRODUTO'. Retorne ao formulrio 'formMovProduto'. Como ele ser criado dinamicamente necessrio retir-lo da lista de "Auto-Create". E no evento OnClose devemos destru-lo, portanto, crie um procedimento para este evento e acrescente o cdigo abaixo: procedure TformMovProduto.FormClose(Sender: TObject;var Action:TCloseAction); begin Action := caFree end; Finalmente para testar necessrio abrir o componente tblMovProduto no formulrio 'formMovProduto' e em seguida colocar a tabela em modo de inserco, ento no evento OnCreate deste formulrio acrescente: procedure TformMovProduto.FormCreate(Sender: TObject); begin with DMAlmoxarifado.tblMovProduto do begin Open ; Insert; end; end; Execute o programa e verifique o que acontece. No cadastro de produtos escolha um produto e clique no boto 'Movimentaes'. Faa algumas movimentaes e certifique se a tabela produto est sendo atualizada.

Controlando o bloqueio de registros


Os datasets tm uma propriedade chamada UpdateMode que controla como eles atualizam ou excluem registros existentes na tabela. Para ver o efeito dessa propriedade, inicie o SQL Monitor. Clique em Options|Trace Options, marque as trs primeiras opes e desmarque as restantes. Execute o programa e faa alteraes na tabela "Produto". Verifique que quando voc edita um registro, alterando o campo Nome e clica em Salvar, o Delphi executa o seguinte comando (note a ltima linha que tem "SQL Execute": UPDATE dbo.Produto SET Nome=:1 WHERE CodProduto=:2 AND Nome=:3 AND QuantEstoque =:4 AND QuantMinima IS NULL and CodFornecedor =:5

Os parmetros :1, :2, etc. so substitudos no momento da execuo. Para saber seus valores, verifique as linhas "SQL Data In" logo acima. O importante a notar que o Delphi usa todos os campos na condio WHERE para localizar os registros. Isso quer dizer que, se voc comear a editar o registro e algum outro usurio alterar qualquer campo do registro (por exemplo, Preo) antes de voc tentar gravar, a clusula WHERE no consegue mais achar aquele registro. O Delphi vai mostrar uma mensagem de exceo "Could not update because another user changed the record" (No pude salvar porque outro usurio alterou o registro). Nesse caso, prevalecem as alteraes do primeiro usurio. Esse o comportamento default e corresponde propriedade UpdateMode do componente Table com o valor 'upWhereAll', que indica que Delphi vai montar uma clusula WHERE com todos os campos. Voc pode escolher outros valores que mudam esse comportamento. Se voc mudar para 'upWhereKeyOnly', o Delphi usa apenas a chave primria da tabela (no caso, CodProduto) para localizar valores. Nesse caso, ele sempre consegue encontrar o registro (a no ser que a chave seja alterada, o que improvvel). Se voc mudar para 'upWhereChanged', o Delphi usa a chave primria e os campos que foram alterados (por exemplo, Nome, no teste acima). Para testar, altere UpdateMode para 'upWhereKeyOnly', execute o programa e faa o mesmo teste anterior. Voc ver que o comando de atualizao executado : UPDATE dbo.Produto SET Nome=:1 WHERE CodProduto=:2 Ou seja, o Delphi usou apenas 'CodProduto' para localizar o registro. Faa o mesmo teste com 'upWhereChanged'.

Usando o componente Query


Vamos usar o mesmo exemplo do componente Table , mas com um componente Query. Clique na pgina "Query" do componente PageControl.O componente Query permite consultar dados, mas nem sempre permite alterar esses dados. Existem vrias formas de poder alterar os registros retornados pela consulta. A forma mais fcil usar a propriedade RequestLive=True, mas tem certas desvantagens, como veremos. Como iremos utilizar um component Query , vamos coloc-lo no data module . Altere Name= qryMovProduto, DatabaseName= DBAlmoxarifado e RequestLive=True, para permitir alterao. Na propriedade SQL, digite: select * from MovimentacaoProduto where codProduto =:codProduto order by DataMov Ao incluir uma movimentao iremos atribuir o cdigo do produto a coluna 'codproduto' da tabela 'MovimentacaoProduto', no evento OnNewRecord crie o procedimento abaixo: procedure TdmAlmoxarifado.qryMovprodutoNewRecord(DataSet: TDataSet); begin qryMovProduto.fieldbyname('codproduto').asinteger := tblProduto.fieldbyname('codproduto').asinteger end; Essa consulta ter um parmetro 'codProduto', que ser alterado dinamicamente. O contedo deste parmetro ser o valor da coluna 'CodProduto' na tabela produto. No componente Query no temos a propriedade MasterSource que faz o relacionamento, mas podemos utilizar a propriedade DataSource deste componente, altere esta propriedade para 'formProduto.DSGenerico'. O parmetro colocado no componente Query deve ter o mesmo nome da coluna que ir definir o relacionamento com a tabela Produto. O parmetro ser substitudo a cada mudana realizada na tabela produto. Retorne ao formulrio 'formMovProduto' , na pgina 'Query', crie um componente DataSource, com Name=dsMovProdutoQuery e DataSet= DMAlmoxarifado.qryMovProduto. Copie os mesmos componentes da pgina "Table" (ou crie outros iguais) e altere a propriedade DataSource desses componentes para 'dsMovProdutoQuery'. Altere tambm a propriedade Name dos componentes Button para: Button1.Name = btnIncluirQuery Button2.Name = btnSalvarQuery Button3.Name = tnCancelarQuery Retire todos os procedimentos associados ao evento OnClick dos botes e crie novos procedimentos com os cdigos a seguir: No procedimento btnIncluirQueryClick faa a modificao abaixo: procedure TformMovProduto.btnIncluirQueryClick(Sender: TObject); begin

end;

DMAlmoxarifado.qryMovProduto.insert

necessrio fazer modificaes no procedimento btnCancelarQueryClick , coloque de acordo com o cdigo abaixo: procedure TformMovProduto.btnCancelarQueryClick(Sender: TObject); begin DMAlmoxarifado.qryMovProduto.Cancel end; A modificao maior ser no procedimento btnSalvarClick, pois, iremos criar um procedimento que ir servir para os dois botes, ento necessrio substituir o tblMovProduto , os objetos de campos tblMovProdutoQuantidade e tblMovProdutoTipoMov. Substitua o objeto de campo tblMovProdutoQuantidade.value por (Sender as TDataSet).fieldbyname('quantidade').asinteger, substitua tblMovProdutoTipoMov.value por (Sender as TDataSet).fieldbyname('TipoMov').asString e o componente tblMovProduto substitua por (sender as TDataSet). Crie o procedimento abaixo: Procedure SalvaDados(Sender: TDataSet); Begin with DmAlmoxarifado do begin dbAlmoxarifado.TransIsolation := tiReadComitted; try dbAlmoxarifado.StartTransaction; // Inicializa a transao (sender as TDataSet).post; // Salva o registro da tabela MovimentacaoProduto tblProduto.edit; // Colocar tabela produto em modo de edio if (sender as TDataSet).fieldbyname('TipoMov').asstring = 'E' then tblProdutoQuantEstoque.value := tblProdutoQuantEstoque.value + (sender as TDataSet).fieldbyname('Quantidade').asinteger else begin if tblProdutoQuantEstoque.value < (sender as TDataSet).fieldbyname('Quantidade').asinteger then begin showmessage('Quantidade Invlida!'); dbAlmoxarifado.RollBack; // Cancela Transao abort; end; tblProdutoQuantEstoque.value := tblProdutoQuantEstoque.value (sender as TDataSet).fieldbyname('Quantidade').asinteger; end; tblProduto.post; dbAlmoxarifado.Commit; // Confirma a Transao Except on EDBEngineError do begin dbAlmoxarifado.Rollback; // Cancela Transao abort; end; end; dbAlmoxarifado.TransIsolation := tiDirtyRead end; end;

Notas: No recomendvel usar a propriedade Filter do componente Query, porque ela filtra os dados localmente, e no no servidor, o que mais adequado. Crie um procedimento para o evento OnClick do Button 'btnIncluirQuery' e acrescente o cdigo abaixo: Procedure TformMovproduto.btnSalvarQueryClick(Sender: TObject); begin SalvaDados(DMALmoxarifado.qryMovProduto) end; Altere o procedimento do evento OnClick do Button 'btnSalvar' , deixe-o de acordo com o cdigo a seguir: Procedure TformMovproduto.btnSalvarClick(Sender: TObject); begin SalvaDados(DMALmoxarifado.tblMovProduto) end; Antes de executar o programa necessrio abrir o componente Query, no formulrio altere o procedimento do evento onCreate, acrescentando os cdigos abaixo: with DMAlmoxarifado.qryMovProduto do begin Open ; Insert; end; Execute o programa, clique na pgina "Query" e teste. Como RequestLive=True, voc tambm pode alterar os dados. Na prtica, RequestLive no funciona em todas as situaes e, mesmo quando funciona, tem alguns problemas. Se voc usa RequestLive, o Delphi faz duas consultas ao banco de dados: uma com a condio de pesquisa especificada, que vai retornar um subconjunto da tabela, e outra, buscando todos os registros da tabela.

7 - Exemplo Cadastro de Pessoas


Procedimentos Armazenados Componente UpdateSQL

Procedimentos Armazenados
Um procedimento armazenado [stored procedure] um conjunto de comandos SQL que so compilados e armazenados no servidor. Ele pode ser chamado a partir de um comando SQL qualquer. A vantagem de usar procedimentos armazenados que eles podem encapsular rotinas de uso freqente no prprio servidor, e estaro disponveis para todas as aplicaes. Parte da lgica do sistema pode ser armazenada no prprio banco de dados, em vez de ser codificada vrias vezes em cada aplicao. Eles tambm aumentam o desempenho de vrias operaes pois so executados no servidor e pr-compilados, ao contrrio de comandos SQL que devem ser interpretados no momento da execuo.

Criando procedimentos
Para criar um procedimento, use o comando CREATE PROCEDURE. Infelizmente no existe uma sintaxe padro ANSI para procedimentos. A sintaxe para criar procedimentos, os tipos de parmetros que eles podem receber, e os comandos especiais para uso dentro de procedimentos, so especficos de cada SGBD. Faremos um pequeno procedimento de exemplo, que retornar todas as pessoas que tenham nome parecido com um parmetro informado. No tentaremos ver toda a linguagem de procedimentos, por tratar-se de um assunto extenso. Para uma referncia mais completa dos comandos para uso com procedimentos consulte os livros da linha Oracle Press (autorizados da Oracle) ou SQL Server Books Online, no comando CREATE PROCEDURE. Para que o Oracle possa retornar linhas em cima de um select (que o nosso procedimento ter, necessrio que um cursor seja declarado para poder conter o objeto de resposta. Tal cursor servir como uma tabela "intermediria"de armazenamento no Oracle. Veremos que para fazer a mesma coisa no SQL Server, no h necessidade de se declarar cursores.

Para podermos usarmos um cursor, necessrio declar-lo primeiro (assim como variveis de estrutura nas linguaguens de programao). Tal declarao dever ser feita dentro de um pacote no Oracle, que pode ser entendido como um local de armazenamento de declaraes. Abra o Alias "OracleCurso" e digite o seguinte comando: CREATE Package PacoteCursor AS type TipoCursor is ref Cursor; end; Aqui, PacoteCursor o nome que este pacote ter no servidor Oracle. 'Type' inicia a declarao de um novo tipo (ou uma nova estrutura), 'TipoCursor' o nome da nova estrutura, sendo escolhida aleatoriamente neste caso. 'is ref Cursor' indica que este novo type um objeto que faz referncia a um cursor. Uma vez criado o pacote que contm a declarao do cursor, podemos agora realmente criar a procedure dentro do Oracle. Repare que nada disto seria necessrio se voc estivesse utilizando o SQL Server. Execute o seguinte comando: CREATE procedure BuscaPessoa (nomebusca in varchar2, umcursor in out PacoteCursor.Tipocursor) as begin open umcursor for select * from Pessoa where nome like '%'||nomebusca||'%'; end; Aqui executamos o comando de criao da procedure, que recebe um parmetro de entrada(que ser passado pelo Delphi), e um parmetro de entrada e sada (in out), que caracteriza um Cursor. Note que ao declarar o segundo parmetro, colocamos que ele do tipo PacoteCursor.Tipocursor. Isto faz com que o Oracle automaticamente detecte que a declarao de Tipocursor est dentro do pacote PacoteCursor. No corpo da procedure, temos de incio uma chamada de abertura ao cursor, o que far com que ele possa ser executado e que possar retornar resultados. O resultado retornado o conjunto de todas as linhas da tabela Pessoa que tenham o parmetro nomebusca de entrada em qualquer parte do campo nome. O smbolo "||" indica concatenao de strings e "%" indica qualquer string (inclusive a string nula). Obs: Para criar a mesma procedure no SQL Server, voc dever executar o seguinte comando: CREATE procedure BuscaPessoa @nomebusca varchar(50) as select * from Pessoa where nome like '%'+@nomebusca+'%' Note que a diferena entre os SGBDs que o SQL Server no precisa do cursor para retornar o resultado da procedure. No SQL Server, os parmetros so especificados com uma '@' antes do nomes, indicando parmetro local de procedure. O smbolo de concatenao agora o comum '+', e no mais '||'.

Chamando um procedimento
Abra o projeto ALMOXARIFADO.DPR e dentro dele crie um formulrio. Altere a propriedade Name para 'formPessoa' e o Caption para 'Cadastro de Pessoas'.Coloque neste formulrio um componente Label altere o Caption para 'Pessoas'. Acrescente tambm um componente Edit , um Button altere as propriedades de acordo com a tabela abaixo: Edit Name editPessoa Text '' Button Caption Procurar Name btnProcurar Salve o formulrio como 'DPessoa.pas'. Como iremos criar o formulrio dinmicamente vamos retir-lo da lista de "Auto-create" e no procedimento do evento onClose coloque o comando a seguir: Action := caFree; Neste formulrio iremos utilizar o procedimento armazenado 'BuscaPessoa'. Para chamar um procedimento armazenado no Delphi, usa-se um componente StoredProc. Esse componente um dataset, o que significa que, se o procedimento retorna um conjunto de registros, ele pode ser usado para manipular esse conjunto de registros. Vamos us-lo para executar o procedimento BuscaPessoa, que retorna um conjunto de registros como resultado. Para executar o procedimento, basta chamar o mtodo ExecProc desse componente. Vamos abrir o data module 'DMAlmoxarifado' e em seguida acrescentar o componente StoredProc.Altere as propriedades:

Name: stpBuscaPessoa DatabaseName: DBAlmoxarifado StoredProcName: BUSCAPESSOA Quando voc seleciona um nome na propriedade StoredProcName, o Delphi automaticamente busca informao sobre os parmetros do procedimento. Voc pode ver essa informao na propriedade Params. Ao abrir essa propriedade, voc ver algo como: E caso esteja usando o SQL Server, voc ver: Repare que no Oracle o parmetro que se refere ao cursor no aparece. Voc deve adicionar este parmetro manualmente, ou o Delphi acusar nmero incompatvel de parmetros. Para isto, clique com o boto direito do mouse na janela dos parmetros, e escolha a opo "Add". Na propriedade name, coloque o nome do parmetro do cursor, que no nosso caso "umcursor". Na propriedade "ParamType", escolha "PtInputOutPut" e na propriedade datatype escolha "ftCursor". No SQL Server, o parmetro @nome de entrada , ento vamos alterar a propriedade ParamType para ptInput. O parmetro Result devolve o resultado de um procedimento armazenado , caso no procedimento tenha sido utilizado o comando RESULT no procedimento. Retorne ao formulrio 'Cadastro de Pessoas'. Para que voc possa acessar os componentes do mdulo 'DMAlmoxarifado' nesse formulrio, deve acrescentar uma clasula uses ao formulrio, fazendo referncia unidade 'MAlmoxarifado'. Para executar o procedimento, foi acrescentado o componente Edit ,onde iremos informar o nome a ser procurado e no boto 'Procurar' , iremos chamar o procedimento. Para mostrar o resultado vamos acrescentar um componente DBLookupListBox() e um componente DataSource. No DataSource altere seu nome para dsPessoa e a propriedade DataSet coloque 'DMAlmoxarifado.strBuscaPessoa' . Para o DBLookupListBox vamos coloque a propriedade ListSource = 'dsPessoa' , ListField = 'Nome' (campo que ser mostrado) e KeyField = 'CodPessoa' . No evento OnClick do btnprocurar faa: procedure TformPessoa.btnprocurarClick(Sender: TObject); begin with DMAlmoxarifado.strBuscaPessoa do begin close; ParamByName('NomeBusca').asstring := editPessoa.text; open; end; end; Este procedimento retorna dados como resultado utilizamos o mtodo Open (poderia utilizar a propriedade Active = true). Para isso associamos a um componente DataSource. Desta forma podemos mostrar o resultado em controles de dados, no nosso caso estamos utilizando o DBLooupComboBox. Caso no retorna resultado ao invs de abrir o componente, temos que execut-lo, utilizando o mtodo ExecProc. Se voc estiver usando o SQL Server, voc deve apenas alterar o nome do parmetro. Ao invs de 'NomeBusca', coloque '@nomebusca'. Iremos chamar este formulrio, atravs do formulrio principal. Abra o formulrio principal e acrescente uma referncia da unit DPessoa. E no evento OnClick do item de menu Cadastro|Pessoas. Faa o seguinte: if not FormularioAberto(FormPessoa) then FormPessoa :=TFormPessoa.Create(Application); FormPessoa.Show; Execute o formulrio e teste. Para testar informe o sobre nome de uma pessoa que esteja cadastrada. Para visualizar os dados temos que acrescentar alguns componentes de controle, para facilitar vamos criar objetos de campos no componente strBuscaPessoa (neste caso podemos criar , porque o procedimento retorna registros como resultado) que est no data module DMAlmoxarifado. Em seguida arraste os objetos para o formulrio 'formPessoa', organize o formulrio como abaixo: Acrescente um componente DBNavigator () e altere a propriedade DataSource para dsPessoa.Execute o programa . Observe que s possvel movimentar de uma pessoa para outra, no permitindo fazer alteraes.

Componente UpdateSQL
Uma forma de ter consultas alterveis usar um componente chamado UpdateSQL (). Esse componente permite definir comandos SQL de alterao que sero executados para modificar os dados retornados pelo componente Query ou Stored Proc. Para usar esse componente, voc deve trabalhar em modo cached updates. Primeiro vamos ver o que vem a ser o esse modo cached updates.

O modo "cached updates"


Normalmente, a cada operao de alterao, incluso, ou excluso que voc faz em um dataset, o Delphi grava imediatamente o resultado daquela operao. Nem sempre essa a melhor opo. Voc pode ter um controle maior do processo de alterao com o modo cached updates [atualizaes em cache]. Nesse modo, todas as operaes feitas no dataset so registradas apenas em memria, num cache de atualizaes, mas no so enviadas para o servidor de banco de dados (ou para as tabelas locais). Depois, quando o programa desejar gravar os dados fisicamente, ele usa o mtodo ApplyUpdates para salvar as alteraes. Vamos ver como funciona isso com a tabela 'tblProduto' no Data Module 'DMAlmoxarifado' . Ento vamos abrir este Data Module. Para ativar o modo cached updates, altere a propriedade CachedUpdates desse componente para True. Mas o formulrio que utiliza o componente tblProduto para cadastro o 'formProduto', ento vamos abrir este formulrio. Coloque um boto a mais no formulrio, com Caption="Salvar" e chame-o de 'btnSalvar'. Crie um procedimento para o evento Onclick com o seguinte: procedure TformProduto.btnSalvarClick(Sender: TObject); begin inherited; DMAlmoxarifado.tblProduto.ApplyUpdates; DMAlmoxarifado.tblProduto.CommitUpdates; end; O mtodo ApplyUpdates salva todas as modificaes que esto no cache, gravando fisicamente no banco de dados. O mtodo CommitUpdates limpa o contedo do cache, depois que os dados foram gravados. necessrio chamar os dois em seqncia. Uma outra forma de salvar as alteraes, que discutiremos mais tarde, usar o mtodo ApplyUpdates do componente Database: DMAlmoxarifado.dbAlmoxarifado.ApplyUpdates([tblProduto]); Execute o programa. Altere alguns produtos , incluindo ou excluindo alguns dados. Note que quando voc estiver modificando a tabela, o componente no est gravando fisicamente os dados. Se voc fechar o formulrio sem clicar no boto "Salvar", nada ser gravado e, ao executar novamente, voc ver os mesmos dados que antes. Podemos tambm pedir confirmao ao fechar o formulrio, para ter certeza de que o usurio vai salvar as alteraes. Podemos verificar se existem modificaes pendentes (isto , no cache, mas no gravadas), lendo a propriedade UpdatesPending. Para isso, crie um procedimento no evento OnCloseQuery do formulrio: procedure TformProduto.FormCloseQuery(Sender: TObject; var CanClose: Boolean); var resposta :integer; begin inherited; if (DMAlmoxarifado.tblProduto.Active) and (DMAlmoxarifado.tblProduto.UpdatesPending) then begin resposta := Application.MessageBox('Salvar as alteraes pendentes?','Confirmao',MB_IconQuestion + MB_YesNoCancel); case resposta of idYes: DMAlmoxarifado.dbAlmoxarifado.ApplyUpdates([DMAlmoxarifado.tblProduto]); idNo: DMAlmoxarifado.tblProduto.CancelUpdates; idCancel: CanClose := False; end; end;

end; No procedimento, o mtodo ApplyUpdates do componente database chama indiretamente os mtodos ApplyUpdates e CommitUpdates do componente 'tblProduto', salvando as modificaes. O mtodo CancelUpdates limpa o cache de modificaes, cancelando tudo que foi alterado. E finalmente, 'CanClose' um parmetro do procedimento que, se alterado para false, faz com que ele no feche o formulrio. Execute o programa, altere ou inclua algum registro, e tente fechar o formulrio sem clicar em "Salvar". O procedimento acima vai mostrar a mensagem e voc deve responder de acordo.

Consultas alterveis com UpdateSQL


Para que o cadastro de pessoas permita alteraes , iremos utilizar um componente UpdateSQL . Como vimos ele utiliza o modo 'Cached Updates'. Mas o componente que contm os dados das pessoas o componente strBuscaPessoa, portanto, iremos colocar este componente para utilizar 'Cached Updates'. Abra o Data Module 'DMAlmoxarifado' , clique no componente strBuscaPessoa. Altere a propridade CachedUpdate para True. Acrescente um componente UpdateSQL() e altere apenas seu nome para 'updPessoa' . Clique no componente strBuscaPessoa . Na propriedade UpdateObject selecione 'updPessoa'. Desta forma estamos dizendo que quem vai permitir alteraes neste componente e o componente updPessoa. O componente UpdateSQL permite gerar um comando SQL para cada tipo de operao no dataset: incluso (comando INSERT), alterao de dados (comando UPDATE) ou excluso (comando DELETE). Depois de gerar os comandos, eles sero executados quando ocorrem as operaes correspondentes no componente dataset associado a ele. Para gerar os comandos SQL, clique duas vezes no componente 'updPessoa'. Voc ver uma tela como a seguinte: Nas listas Key Fields e Update Fields, aparecem todos os campos da tabela usada, no caso, Pessoa. Os campos selecionados em Key Fields sero usados para localizar um registro no momento da alterao (UPDATE) ou excluso (DELETE). Os campos selecionados em Update Fields tero seus valores modificados (UPDATE) ou includos (INSERT). Em TableName informe a tabela 'Pessoa'. Na primeira lista, selecione apenas o campo "CodPessoa", como indicado acima. Na segunda lista, mantenha todos os campos selecionados. Clique no boto "Generate SQL". Voc ver agora a pgina "SQL", com os comandos SQL gerados. Se voc clicar em (.) Modify, ver o comando usado para alterao: update Pessoa set CodPessoa = :CodPessoa, Nome = :Nome, Sexo = :Sexo, Rua = :Rua, Bairro = :Bairro, Cidade = :Cidade, Estado = :Estado, CEP = :CEP, CPF = :CPF, DataCadastro = :DataCadastro, Nota = :Nota, fone = :fone, fax = :fax where CodPessoa = :OLD_CodPessoa Onde :NomeDoCampo um parmetro que ser substitudo pelo valor digitado naquele campo (o novo valor) e :OLD_NomeDoCampo ser substitudo pelo valor anterior do campo (o valor antes da modificao). Durante a execuo, se voc selecionar o registro com CodPessoa = 1 e alterar CodPessoa para 25 e Nome para 'Pessoa25', o comando realmente executado, com parmetros substitudos, ser: update Pessoa set CodPessoa = 25, Nome = 'Pessoa25', ...

where CodPessoa = 1 Note que os campos que aparecem no WHERE (apenas CodPessoa) so os mesmos selecionados na lista Key Fields e os campos que aparecem no SET so os que foram selecionados em Update Fields. Voc pode tambm editar manualmente o SQL para modificar seu funcionamento. Por exemplo, se voc no quiser permitir alterar o cdigo do produto, remova-o da lista do SET. Se voc clicar no boto (.) Insert na pgina SQL, ver o seguinte comando: insert into pessoa (CodPessoa, Nome, Sexo, Rua, Bairro, Cidade, Estado, CEP, CPF, DataCadastro, Nota, fone, fax) values (:CodPessoa, :Nome, :Sexo, :Rua, :Bairro, :Cidade, :Estado, :CEP, :CPF, :DataCadastro, :Nota, :fone, :fax) Nesse caso, todos os valores de campos (da lista Update Fields) so inseridos na tabela. E se voc clicar em (.) Delete, ver: delete from pessoa where CodPessoa = :OLD_CodPessoa Nesse caso, os campos de Key Fields (CodPessoa) so usados para localizar o registro a ser excludo. Clique no boto 'Ok' da janela do UpdateSQL. Abra o formulrio 'formPessoa' e coloque um novo boto "Salvar" , com o nome 'btnSalvar'. Nesse boto, coloque o cdigo abaixo: procedure TformPessoa.btnSalvarClick(Sender: TObject); begin with DMAlmoxarifado do dbAlmoxarifado.ApplyUpdates([strBuscapessoa]); end; Isso permite salvar as alteraes, j que a consulta est em modo cached updates e elas no so salvas automaticamente. Execute o programa e note que agora voc pode alterar o resultado do procedimento.

Simulando modo "normal" em cached updates


O componente UpdateSQL pode ser usado tanto com o componente StoredProc (como usamos) quanto com um componente Query,Table. Ele mais til no primeiro e no segundo caso. No segundo caso porque permite transformar uma consulta no-atualizvel (que no usa RequestLive=True) em uma consulta altervel. Mas esse componente s funciona em modo cached updates, que nem sempre o que se deseja. s vezes necessrio gravar as alteraes imediatamente, uma vez para cada registro, mas poder usufruir dos recursos do UpdateSQL. Para isso, podemos simular o modo "normal" (atualizaes salvas instantaneamente) mesmo com o modo cached updates. A forma de fazer isso forar a gravao das alteraes logo depois de um Post (incluso ou alterao) e de um Delete (excluso) na consulta. No data module 'DMAlmoxarifado' clique no componente strBuscaPessoa, na pgina de eventos, e no evento AfterPost. Digite um nome de procedimento: 'SalvarDados' e tecle [Enter]. Isso vai criar um procedimento com aquele nome. No procedimento faa: procedure TdmAlmoxarifado.SalvarDados(DataSet: TDataSet); begin dbAlmoxarifado.ApplyUpdates([DataSet as TDBDataSet]); end; O parmetro 'DataSet' diz qual o dataset (Table, Query, storedProc etc.) que chamou esse evento. Isso permite tratar de forma genrica qualquer componente, no s 'strBuscaPessoa'. O procedimento chama o mtodo ApplyUpdates e fora a gravar fisicamente a(s) alterao(es) que est(o) em cache naquele 'DataSet'. Clique no mesmo componente, 'strBuscaPessoa'. Na pgina de eventos, no evento AfterDelete, selecione o mesmo nome de procedimento (SalvarDados). Assim as excluses de registro tambm sero executadas imediatamente. Execute o programa e veja a mudana de funcionamento. Note que agora o boto "Salvar" no mais necessrio.

8 - Utilizando o ORACLE
Conectando ao ORACLE Opes de um alias ORACLE Oracle e nomes do banco de dados Oracle e Transaes Oracle e Procedimentos Armazenados

Conectando ao Oracle
Para estabelecer uma conexo com o banco de dados Oracle a partir do Delphi, preciso ter o software cliente do Oracle instalado no computador. Voc precisa tambm utilizar o SQL*Net (Oracle 7.x) ou Net8 (Oracle 8.x) para criar um database alias ou "string de conexo" com o banco de dados. O Delphi 4 j vem configurado para acessar o Oracle8, mas ele suporta tambm o Oracle 7.x, o Personal Oracle 7.x, ou o Oracle Lite (novo nome do Personal Oracle) verso 8.0.4 ou posterior. Dependendo da verso do software cliente que voc tem instalada, voc talvez precise mudar parmetros de configurao no BDE Administrator do Delphi. Execute o BDE Administrator, clique na pgina "Configuration" e abra System\Drivers\Native\ORACLE. Alguns parmetros que voc define para o driver ORACLE so apenas valores default, que podem ser alterados quando voc define um alias. Outros so opes globais. As mais importantes so:

DLL32
DLL32 - define qual das DLLs do BDE ser o driver usado: SQLORA32.DLL: use esse valor para o Oracle 7.x SQLORA8.DLL: use esse valor para o Oracle a partir da verso 8

VENDOR INIT
VENDOR INIT - define o nome de um arquivo DLL que instalado pelo software do Oracle: ORA7x.DLL ou ORANT711.DLL: use o nome da DLL adequado dependendo da verso do Oracle. Verifique o nome da DLL no diretrio de instalao do cliente Oracle, subdiretrio BIN (por exemplo, em C:\ORAWIN95\BIN ou C:\ORANT\BIN). Esse diretrio deve estar no PATH do computador (AUTOEXEC.BAT no Windows 9x ou Painel de Controle, Sistema, Ambiente no Windows NT). OCI.DLL: use esse valor para o Oracle8. O diretrio que contm esse arquivo deve estar no PATH. MTXOCI.DLL: use para o Oracle8 apenas se aparecer disponvel na lista e se esse arquivo existe no seu computador. Assim por exemplo, se voc tiver instalado o cliente do Oracle 7.3, use: DLL32 = SQLORA32.DLL VENDOR INIT = ORA73.DLL Se voc estiver usando o software cliente do Oracle8 (que tambm pode acessar servidores 7.x), use: DLL32 = SQLORA8.DLL VENDOR INIT = OCI.DLL Aps alterar os valores (se necessrio), teste a conexo. Clique na pgina "Databases", crie um novo alias do tipo ORACLE e veja se voc consegue conectar-se a ele. Se no funcionar, tente alterar os parmetros do driver novamente.

Opes de um alias ORACLE


Quando voc cria um alias do tipo ORACLE, ou quando voc usa um componente Database e seleciona o "Driver name" como ORACLE, vrias opes podem ser definidas. Algumas se aplicam exclusivamente ao Oracle ou tm um funcionamento diferente no Oracle:

SERVER NAME
A "string de conexo" ou alias do SQL*Net ou Net8. Esse o mesmo nome usado, por exemplo, para se conectar usando o SQL*Plus do Oracle.

USER NAME
Nome de usurio no Oracle. Isso apenas um default, que geralmente alterado para um nome diferente a cada conexo.

LIST SYNONYMS
LIST SYNONYMS = {NONE | PRIVATE | ALL} Um sinnimo do Oracle um outro nome criado para uma tabela j existente, de forma a facilitar o acesso por outros usurios. Um sinnimo pode ser definido com um owner [proprietrio] especfico, ou como sinnimo pblico, acessvel a todos usurios. O Delphi pode sempre acessar um sinnimo como se fosse uma tabela real. A opo LIST SYNONYMS define quais os sinnimos que ficam disponveis para visualizar em uma lista de tabelas do banco de dados (por exemplo, que aparece na lista da propriedade TableName): NONE: nenhum sinnimo aparece em listas de tabelas. S aparecem tabelas "reais" e vises [views]. PRIVATE: aparecem os sinnimos cujo proprietrio o usurio conectado, mas no aparecem os sinnimos pblicos. ALL: aparecem todos os sinnimos, incluindo os pblicos.

NET PROTOCOL
NET PROTOCOL = {TNS | NETBIOS | TCP/IP | NAMED PIPES | APPC | ASYNC | 3270 | IPX/SPX } Use sempre TNS, que o protocolo suportado pelas verses mais recentes do Oracle, usando SQL*Net (7.x) ou Net8 (8.x). Se voc usar outro valor, consulte a documentao do Oracle para definir o SERVER NAME.

OBJECT MODE
OBJECT MODE = {TRUE | FALSE} Use FALSE para o Oracle 7.x e verses do Oracle8 que no tenham instalado a Objects Option. Use TRUE para o Oracle8 com Objects Option. Com essa opo, o Delphi passa a suportar a programao baseada em objetos do Oracle8 e os novos tipos de dados do Oracle8, como OBJECT, VARRAY e nested table.

ENABLE INTEGERS
ENABLE INTEGERS = {FALSE | TRUE} Quando FALSE, qualquer campo numrico do Oracle (NUMBER) interpretado pelo BDE como sendo um campo de ponto flutuante (tipo "Float"). Se voc mudar para TRUE, os campos NUMBER(x), com x < 5 so interpretados pelo Delphi como do tipo "Smallint" (inteiro pequeno, de 16 bits) e com 5 <= x < 10 so interpretados como do tipo "Integer" (inteiro grande, de 32 bits).

ENABLE BCD
ENABLE BCD = {FALSE | TRUE} Quando FALSE, campos NUMBER so tratados como do tipo "Float" (Double), o que pode provocar erros de arredondamento. Quando TRUE, campos NUMBER so tratados como do tipo Currency no Delphi/BDE, permitindo operaes mais exatas.

ROWSET SIZE
ROWSET SIZE = n (um nmero inteiro) O Delphi busca n linhas de cada vez do servidor para o cliente. Aumentar esse nmero pode melhorar o desempenho.

Oracle e nomes do banco de dados


Normalmente nomes de tabelas e campos no Oracle so case-insensitive, ou seja, ignoram a diferena entre maisculas e minsculas. Se voc criar uma tabela no Oracle com: create table

MinhaTabela (Codigo number, Nome varchar2(50)) , o Oracle converte o nome da tabela para todas letras maisculas (MINHATABELA), assim como qualquer campo declarado. Voc pode fazer consultas ou alteraes na tabela sem se preocupar com isso, ou seja: select codigo, NOME from minhaTAbela Agora, se voc criar o nome da tabela ou campo entre aspas, e esse nome no estiver todo em maisculas, o Oracle assume que ele ser case-sensitive, ou seja, qualquer consulta naquela tabela tem que informar o nome entre aspas, e exatamente como foi escrito originalmente: create table "MinhaTabela" ("Codigo" number, "Nome" varchar2(50)) ao tentar usar a tabela: select codigo, NOME from minhaTAbela Erro ORA-00942: table or view does not exist. select "Codigo", "Nome" from "MinhaTabela" funciona O utilitrio Data Pump do Delphi 4 (antes chamado Data Migration Wizard), ao converter tabelas para o Oracle, cria todos os nomes de tabelas e campos entre aspas e isso pode causar o problema acima. A nica forma de resolv-lo alterar todos os nomes de tabelas e campos (no ltimo passo do Data Pump) para todas letras maisculas (MINHATABELA, CODIGO, NOME etc.).

Oracle e Procedimentos Armazenados


O Delphi pode chamar qualquer procedimento armazenado [stored procedure] ou funo armazenada [stored function] do Oracle. Por exemplo, crie no banco de dados do Oracle o seguinte procedimento armazenado: create or replace procedure Aumenta_Preco(aumento in number, tot_aumento out number) as begin select sum(PrecoVenda * (aumento-1)) into tot_aumento from Produto; update Produto set PrecoVenda = PrecoVenda * aumento; end; O procedimento recebe um parmetro de entrada (in), chamado aumento, do tipo number. Esse parmetro ser um multiplicador usado para alterar os preos dos produtos na tabela Produto. Por exemplo, aumento = 1.1 usado para aumentar em 10% os preos dos produtos. O procedimento calcula qual o total de aumento na tabela Produto e coloca no parmetro de sada (out), chamado tot_aumento. Depois ele atualiza a tabela Produto, multiplicando o campo PrecoVenda pelo aumento. Um procedimento armazenado pode ter vrios (ou nenhum) parmetros de entrada (in), aos quais voc atribui valores antes de chamar o procedimento. Ele pode ter tambm vrios (ou nenhum) parmetros de sada (out), cujos valores voc pode ler no Delphi aps a execuo do procedimento. Um parmetro tambm pode ser in out (usado tanto para entrada quanto para sada). Para usar esse procedimento no Delphi, crie um componente StoredProc no formulrio. Conecte-o a um componente Database que esteja acessando o banco de dados do Oracle. Selecione em StoredProcName o nome do procedimento. Depois, na propriedade Params, voc deve ver a lista de parmetros, que trazida automaticamente. Os parmetros tm ambos DataType = ftFloat, indicando o tipo de dados a ser passado. A propriedade ParamType de cada um diz a direo do parmetro (in, out ou in out). O parmetro de entrada AUMENTO fica com ParamType = ftInput e o parmetro de sada, TOT_AUMENTO, fica com ParamType = ftOutput. No necessrio mudar nada. Antes de executar o procedimento, atribua um valor ao parmetro aumento. Para executar o procedimento, use o mtodo ExecProc do componente. Aps executar o procedimento, leia o valor do parmetro tot_aumento, por exemplo: with stpAumentaPreco do begin ParamByName('AUMENTO').AsFloat := aumento; ExecProc; tot_aumento := ParamByName('TOT_AUMENTO').AsFloat; end;

Nota: um procedimento que faz parte de um package [pacote] do Oracle no aparece na lista de procedimentos, na propriedade StoredProcName. Isso no significa que voc no possa us-lo no Delphi. Apenas digite nome_do_proprietrio.nome_do_package.nome_do_procedimento manualmente na propriedade StoredProcName e o Delphi vai ler as informaes dos parmetros. Um procedimento tambm pode fazer uma pesquisa em uma ou mais tabelas e retornar um cursor como resultado dessa pesquisa. Para poder usar procedimentos que retornam cursores, voc deve primeiro definir um pacote [package] no Oracle contendo a definio de um tipo de dados cursor. Isso s precisa ser feito uma vez. No SQL*Plus, crie o seguinte pacote: create package Pkg_cursor -- pode ser qualquer nome em vez de pkg_cursor as type TipoCursor is ref Cursor; -- idem acima end; Se o SQL*Plus disser 'Package created', o pacote foi criado sem erros. Se ele disser 'Package created with compilation errors', digite SHOW ERRORS PACKAGE Pkg_cursor para saber que erro ocorreu e depois tente criar o pacote de novo. Para isso, use create or replace package Agora voc pode criar um procedimento que retorna um cursor. Esse procedimento deve ter um parmetro in out (de entrada e sada), declarado com o tipo PKG_CURSOR.TipoCursor. Crie por exemplo o procedimento abaixo: create or replace procedure Busca_Pessoa(nome_busca in varchar2, ret in out Pkg_cursor.TipoCursor) as begin open ret for select * from Cliente where Nome like ('%' || nome_busca || '%') end; (Se houver erros na criao do procedimento, digite SHOW ERRORS PROCEDURE Busca_Pessoa para saber quais foram e corrig-los). Esse procedimento faz um SELECT na tabela Cliente, usando o parmetro nome_busca como uma string de pesquisa. Ele abre um cursor, ret, nesse SELECT. Como o cursor um parmetro de entrada/sada, o Delphi pode usar o cursor criado como se fosse uma tabela, lendo os dados retornados. Nesse caso, faa como antes, usando um componente StoredProc, e associando-o ao nome do procedimento. Na propriedade Params, selecione o parmetro ret e altere a sua propriedade DataType para ftCursor (sem isso, o Delphi no sabe que est lidando com um cursor). Antes de executar o procedimento, defina o valor do parmetro nome_busca. Mas para executar o procedimento, no use ExecProc. Use o mtodo Open do componente ou faa a propriedade Active = True. Os dados do cursor podem ser mostrados em qualquer controle de banco de dados. Coloque um componente DataSource no formulrio, ligado ao StoredProc, e um componente DBGrid, ligado ao DataSource. Voc dever ver o resultado da execuo do procedimento (o contedo do cursor ret). Voc pode tambm executar dinamicamente como: stpBuscaPessoa.ParamByName('NOME_BUSCA').AsString := nomeBusca; stpBuscaPessoa.Open; Resumindo: procedimento sem cursor (mais comum) --- use ExecProc para executar procedimento com cursor --- use Open (Active := True) para executar Para chamar uma stored function do Oracle, o processo o mesmo, com um detalhe a mais: o Delphi cria um parmetro chamado "Result", que representa o valor retornado da funo. Aps executar a funo, voc pode pegar o valor desse parmetro. create function Total_Produto(cod_produto in number) return number as total_aux number begin select sum(p.PrecoVenda * it.Quantidade) into total_aux

from Produto p, Item it where p.CodProduto = it.CodProduto and p.CodProduto = cod_produto; return total_aux; end; A funo recebe como parmetro um cdigo de produto, cod_produto , pesquisa a tabela Item e retorna como resultado a soma de todos os itens vendidos para aquele produto. Voc pode colocar um componente StoredProc no formulrio, com Name=stpTotalProduto e colocar o nome da funo na propriedade StoredProcName = nome_do_ proprietrio. Total_Produto Para executar a funo, informe o valor do parmetro COD_PRODUTO e chame o mtodo ExecProc. Aps executar, pegue o valor do parmetro Result, que contm o valor retornado pela funo, por exemplo: with stpTotalProduto do begin ParamByName('COD_PRODUTO').AsInteger := codProduto; ExecProc; result := ParamByName('Result').AsFloat; end;

Padro de uso de procedimentos (recomendado)


Para facilitar a utilizao de um procedimento/funo armazenado, siga sempre o seguinte padro de programao: 1. coloque um componente StoredProc no Data Module; coloque seu nome como stpNomeDoProcedimento e associe ao procedimento armazenado correspondente. 2. defina um procedimento Pascal na parte public da classe do Data Module, com o mesmo NomeDoProcedimento e os mesmos parmetros do procedimento Oracle. Se um parmetro for de entrada (in), declare o parmetro por valor, ou usando const. Se for de sada (out) ou de entrada/sada (in out), declare o parmetro por referncia (var antes do nome dele). 3. no corpo do procedimento Pascal, passe os parmetros para o procedimento Oracle, usando o componente StoredProc e execute o procedimento (ExecProc ou Open). Aps executar, pegue o valor dos parmetros de sada do Oracle e atribua para os parmetros var do seu procedimento Pascal. A vantagem de usar esse padro que o procedimento Oracle pode ser chamado em qualquer lugar do programa, como se fosse um procedimento Pascal normal. Seguindo esse padro, os trs procedimentos criados aqui seriam declarados como: unit DataModule1; { Nesse exemplo genrico, 'MeuDataModule' o nome do mdulo de dados e 'DataModule1.pas' o nome do arquivo de unidade. } type TMeuDataModule = class(TDataModule) ... stpAumentaPreco: TStoredProc; stpBuscaPessoa: TStoredProc; stpTotalProduto: TStoredProc; private ... public procedure AumentaPreco(aumento: double; var tot_aumento: double); procedure BuscaPessoa(nomeBusca: string); function TotalProduto(codProduto: integer): double; ... end Em algum outro formulrio/unidade, a funo TotalProduto, por exemplo, pode ser chamada: unit MeuFormulario; uses ..., DataModule1;

... umaVariavelQualquer := MeuDataModule.TotalProduto(umCodigoDeProduto);

9 - Componentes MIDAS
Criando aplicaes multi-tiered Criando uma aplicao multi-tiered com banco de dados COM / DCOM Criando um servidor de objetos

Criando aplicaes multi-tiered


Uma aplicao cliente/servidor pode ser two-tier [duas camadas] ou multi-tier [mais de duas camadas]. Numa aplicao two-tier, as duas "camadas" de software so: a camada de interface com o usurio, que faz a entrada e apresentao dos dados e a camada de gerenciamento de dados, representada por um software gerenciador de bancos de dados (SGBD), que faz a consulta e modificao dos dados em seu armazenamento fsico. Uma aplicao multi-tier ou distribuda aquela onde vrias partes so distribudas para serem executadas em diferentes computadores e se comunicam entre si atravs da rede local ou da Internet. Geralmente as partes ou "camadas" da aplicao so trs: uma parte se encarrega da interface visual do programa. Outra, apenas mantm a consistncia lgica dos dados, atravs das chamadas regras de negcio. Outra, ainda, trabalha diretamente com os dados, lendo e gravando registros (essa geralmente implementada por um SGBD). Mas numa aplicao mais complexa, pode haver mais divises. No Delphi, voc pode criar uma aplicao de banco de dados distribuda onde os dados so acessados indiretamente atravs de um servidor de aplicaes, e so acessados remotamente em outro computador com um programa cliente. O cliente (primeira camada) l e grava dados atravs do servidor de aplicaes (a camada do meio ou middletier), que por sua vez, l e grava esses dados em tabelas locais ou se comunica com um SGBD cliente/servidor (a terceira camada). Teoricamente, voc poderia criar at seu prprio SGBD, mas isso muito mais complexo. Veremos como criar aplicaes com essa arquitetura em trs camadas.

Vantagens e desvantagens
Nesse modelo de aplicao, o servidor de aplicaes coordena os pedidos e atualizaes de dados vindos de mltiplos clientes. Ele pode tambm conter toda a validao de dados (regras de negcios), que assim ficam guardadas num local central, em vez de serem feitas em cada cliente. Voc tambm pode criar aplicaes clientes menores, que ocupam menos memria e so mais fceis de instalar (porque no precisam do BDE). Alm disso, distribuindo a aplicao, voc est aproveitando o poder de processamento de vrios computadores ao mesmo tempo. Note no entanto que esse tipo de aplicao obviamente mais difcil de criar e de testar, pois voc precisa execut-la em mais de um computador. Mas, na fase de testes, voc pode executar o cliente e o servidor de aplicaes no mesmo computador.

Componentes usados
Os componentes abaixo fazem parte da paleta de componentes MIDAS: (ClientDataSet): Componente descendente de TDataSet, que portanto suporta todas as propriedades, eventos e mtodos comuns a todos os datasets. Substitui o Table na aplicao cliente. (DCOMConnection): Utilizado para estabelecer comunicao remota entre o cliente e o servidor, utilizando o DCOM. (CorbaConnection): Utiliza o CORBA (Common Object Request Broker Architeture ) para estabelecer conexo entre o servidor e o cliente. (SocketConnection): Utiliza Windows sockets para administrar a conexo com o de servidor. (OLEnterpriseConnection): Utiliza o protocolo OLEnterprise (criado pela Inprise) para estabelecer conexo entre o servidor e o cliente. (Provider): No servidor de aplicaes substitui o componente DataSource.

(SimpleObjectBroker): Mantem uma lista de servidores com os quais o cliente pode se conectar. Permite distribuir a carga entre os servidores. (RemoteServer): Componente que utiliza o DCOM para estabelecer conexo entre o servidor e o cliente. Como temos o componente TDCOMConnection , o RemoteServer tornou-se obsoleto. (MidasConnection): Utilizado tambm nas aplicaes Multi Tier para administrar a conexo entre o cliente e o servidor. Tambm obsoleto. Na aplicao cliente, todos os controles de dados padro do Delphi podem ser usados normalmente, como grids, DBEdits, DBLookupCombo etc. A diferena que em vez de usar um componente Table ou Query, voc deve usar o componente (ClientDataSet). A aplicao cliente tambm pode utilizar um objeto DCOMConnection, CorbaConnection, OLEnterpriseConnection, que sero o elo de ligao com o servidor de aplicaes. Esses componentes, de certa forma, tomam o lugar do componente Database. No servidor de aplicaes, um mdulo de dados remoto [remote data module] faz a interface com os clientes. Ele pode conter qualquer componente que normalmente colocado num mdulo de dados, como componentes Database, datasets diversos (Table, Query, StoredProc) e normalmente tem um ou mais componentes (Provider) para cada dataset. O componente Provider: - Recebe os pedidos de leitura do cliente, l os dados de um dataset e envia os dados atravs da rede para o cliente. - Recebe os dados alterados do cliente, aplica as atualizaes ao banco de dados, registra alteraes que no puderam ser aplicadas e envia cdigos de erro ao cliente informando o que no pde ser feito. O seguinte diagrama ilustra como so feitas as conexes: Do lado esquerdo, os componentes usados na aplicao cliente so o RemoteServer, que faz a ligao com o servidor, os componentes ClientDataSet, onde cada um faz a ligao com um componente Provider do servidor e componentes DataSource alm de controles de dados (no mostrados). No lugar do RemoteServer podemos utilizar o DCOMConnection, CORBAConnection, OLEnterpriseConnection. Do lado direito (servidor de aplicaes), so usados componentes de dados como Table e Database, alm de componentes Provider para cada tabela acessada.

Software necessrio
Se voc utilizar o Componente RemoteServer ou DCOMConnection ,o software que faz a comunicao entre o cliente e o servidor entre mquinas diferentes chamado DCOM (distributed component object model) e ser visto em detalhes mais tarde. Por enquanto, note que o DCOM est disponvel com o Windows NT 4.0 e Windows 98. Para us-lo no Windows 95 preciso instal-lo separadamente. Por enquanto faremos testes executando tanto o servidor quanto o cliente no mesmo computador. Nesse caso, iremos utilizar o DCOM. Os programas utilizam o software DCOM da Microsoft, que parte integrante do Windows NT. Alm da leitura e gravao de dados, um objeto DCOM pode ter mtodos definidos pelo programador para fazer qualquer outra coisa, como veremos.

Criando uma aplicao multi-tiered com Banco de Dados


Criando o servidor de aplicaes
Voc deve geralmente criar o servidor de aplicaes primeiro, e depois criar os clientes. Isso vai permitir testar mais facilmente a comunicao entre eles. O servidor de aplicaes um programa Delphi comum, exceto que geralmente ele no tem interface visvel com o usurio (no precisa ter, mas pode ter). Crie um novo projeto no Delphi. Por enquanto ignore o formulrio principal do projeto, apenas altere sua propriedade WindowState para 'wsMinimized', para que ele no aparea ao executar o programa. Salve os arquivos desse projeto como FSERVIDOR.PAS e SERVIDOR.DPR. Como veremos, importante salvar o projeto (e dar um nome a ele), antes de execut-lo. Vamos criar um mdulo de dados remoto para conter os componentes de dados. Um mdulo de dados remoto funciona como um mdulo de dados comum, exceto que ele um objeto DCOM, e isso permite chamar remotamente rotinas nesse mdulo e transferir remotamente os dados. Para cri-lo, clique em File|New, na pgina 'MultiTier' selecione "Remote Data Module" e clique Ok. O Delphi vai pedir o nome da classe do mdulo. Digite "DadosProduto" e clique Ok, ignorando a opo "Instancing" e 'Threading Model' por enquanto. Salve o arquivo de unidade do mdulo como MSERVIDOR.PAS.

Nesse mdulo de dados, vamos acessar as tabelas do Oracle. Selecione a pgina "Data Access" na paleta de componentes. Coloque um componente Database, altere a propriedade Name para 'dbProduto' e clique duas vezes neste componente. Em "Name" coloque 'dbProduto' e em DatabaseName, na opo "Driver Name", selecione Oracle e desmarque a opo "Login prompt". Clique no boto "Defaults" e altere os parmetros: SERVER NAME=nome do seu servidor Oracle USER NAME=nome de um usurio vlido LANGDRIVER='DBWINUS0' PASSWORD= Informar a senha do servidor Coloque um componente Table no formulrio e conecte-o tabela PRODUTO, alterando as propriedades: Name: tblProduto DatabaseName: DBProduto TableName: PRODUTO Vamos colocar agora um componente Provider () , da pgina Data Access no formulrio e conect-lo a essa tabela. Altere as suas propriedades: Name: provProduto DataSet: tblProduto Ative o componente 'tblProduto' (Active=True) para que ele possa ser usado pelos clientes. Alm disso, voc deve exportar o componente Provider nesse mdulo de dados. Para fazer isso, clique em 'provProduto' com o boto direito e na opo Export provProduto from data module. A exportao acrescenta alguns mtodos ao mdulo de dados, como a funo "Get_provProduto". Execute o programa. Isso vai compilar o projeto e alm disso vai registrar o servidor no Windows. O servidor deve ser registrado primeiro, antes que possa ser usado pelos clientes. Voc pode finalizar o programa. O servidor ser executado pelo DCOM quando for necessrio. Notas: No Windows 95 e 98 necessrio iniciar o programa servidor manualmente, enquanto no Windows NT iniciado automaticamente quando necessrio.

Criando a aplicao cliente


Para criar a aplicao cliente, salve este grupo que estamos utilizando . Clique em View|Project Manager, e depois clique com o boto direito no grupo (Project Group1) , escolha a opo 'Save Project Group' e salve com o nome 'MultitieredDCOM'. Neste grupo iremos criar um novo aplicativo que ser a aplicao cliente. Clique com o boto direito e escolha a opo 'Add New Project' e escolha o cone 'Application ' . Vamos fazer simplesmente um formulrio que acessa a tabela Produto remotamente. sempre recomendvel usar um mdulo de dados, mas para uma aplicao pequena, vamos dispens-lo. No formulrio principal, coloque um componente (DCOMConnection) da pgina "Data Access" e altere seu nome para 'DCOMServidor'. Ele que faz a interface com o servidor. Como o servidor pode ser acessado por qualquer mquina, na propriedade ComputerName coloque o nome do computador . Se for executado no mesmo computador, deixe essa propriedade vazia. Clique na propriedade ServerName e no boto . Voc ver o nome de todas as classes de servidor registradas no Windows. No nosso caso, selecione 'Servidor.DadosProduto'. Note que esse o nome do programa (Servidor) seguido do nome da classe do mdulo remoto. A propriedade Connected desse componente determina se ele est conectado ao servidor ou no. Quando False, o servidor no est sendo executado. Altere seu contedo para True. Isso vai automaticamente iniciar o programa servidor no outro computador. Para acessar a tabela no cliente, coloque um componente (ClientDataSet) no formulrio. Altere seu nome para 'cdsProduto'. Esse componente pode ser usado em qualquer lugar que um dataset comum. Altere sua propriedade RemoteServer, selecionado 'DCOMServidor'. Voc tambm deve escolher a qual provedor [provider] do servidor ele vai ser conectado. Clique na propriedade ProviderName e no boto . Deve aparecer uma lista de um elemento, "provProduto". Selecione esse elemento. Altere a propriedade Active para true. Agora o componente 'cdsProduto', no cliente, est indiretamente conectado ao componente 'tblProduto', no servidor: Coloque agora um componente DataSource no formulrio. Altere Name para 'dsProduto' e DataSet para 'cdsProduto'. Coloque um componente DBNavigator e altere seu DataSource para 'dsProduto' . Acrescente tambm um componente DBGrid colocando sua propriedade DataSource para 'dsProduto'. Agora a execuo do programa como uma aplicao de banco de dados comum, com uma diferena: o componente ClientDataSet no envia os dados imediatamente para o servidor. Ele mantm apenas um registro de quais alteraes (incluses, modificaes ou excluses) foram feitas nos dados. preciso chamar o mtodo

ApplyUpdates desse componente para forar a atualizao dos dados. Isso semelhante ao modo cached updates que foi discutido anteriormente. Para isso, crie um procedimento para o evento AfterPost do componente cdsProduto e faa o seguinte: cdsProduto.ApplyUpdates(0); O parmetro de ApplyUpdates o nmero mximo permitido de erros de atualizao. Caso seja zero, todas as atualizaes devem ser gravadas corretamente ou todas sero canceladas. Salve a unit como 'ClienteDCOM' e o projeto coloque 'ClienteDCOMP' .Agora execute o programa e verifique o seu funcionamento. Nota: se voc colocar um componente Database num mdulo de dados remoto, altere sua p

COM/DCOM
Como vimos, voc pode criar classes de objetos em Delphi. Uma classe bem-construda pode ser reutilizada facilmente em vrios programas. Geralmente para criar e usar objetos de uma classe, voc s precisa saber quais so os mtodos dessa classe, e no como ela implementada internamente. Voc pode tambm criar uma biblioteca de classes, com vrias classes que funcionam juntas, e usar essa biblioteca em outros programas. Mas um problema que vrias linguagens na plataforma Windows permitem tambm a criao e utilizao de classes. Normalmente dois softwares feitos em linguagens diferentes no podem se comunicar entre si, portanto uma biblioteca feita em C++, por exemplo, no seria utilizvel a partir do Delphi.

Component Object Model (COM)


O Component Object Model [modelo de objetos componentes] da Microsoft, um mecanismo criado para permitir que objetos, na forma de cdigo compilado, possam interoperar entre si e fazer parte de um mesmo projeto de software, independentemente da linguagem em que eles foram criados. Isso significa que qualquer linguagem que suporte esse modelo pode criar objetos que cooperam com objetos em outras linguagens. Assim, um programa em Delphi pode usar as funes de um objeto COM criado em C++, ou Visual Basic, ou outras linguagens. E vice-versa: voc pode criar um objeto COM em Delphi que pode ser utilizado por outras linguagens. COM parte integrante do Windows 95 e NT. Uma classe de objeto COM possui uma ou mais interfaces. Uma interface um conjunto de declaraes de mtodos (funes e procedimentos) e propriedades. Quando um programa se comunica com um objeto COM, ele precisa especificar qual a interface que vai utilizar (no qual a classe a ser utilizada). Ele pode obter uma referncia a esta interface, e chamar os mtodos definidos na interface. Uma classe que implementa uma ou mais interfaces chamada CoClass (component object class). Uma biblioteca de tipos [type library] um arquivo contendo declaraes de interfaces para uma ou mais classes. Um ambiente de programao, como o Delphi, pode consultar uma biblioteca de tipo para saber quais as propriedades e mtodos vlidos para uma determinada interface. Sem uma biblioteca de tipo, ainda possvel usar o objeto COM, mas s possvel verificar o tipo de dados correto dos parmetros e propriedades em tempo de execuo. Por isso recomendvel que o criador do objeto crie tambm uma biblioteca de tipos. Um servidor de objetos um componente de software que implementa uma ou mais interfaces COM (em uma ou mais classes) e permite a outros programas usar essas interfaces externamente. Um servidor de objetos pode ser um programa executvel (.EXE) independente ou pode ser uma biblioteca (.DLL). No primeiro caso, ele pode ser uma aplicao completa, com interface grfica, executada separadamente pelo usurio. A segunda opo (.DLL) cria um servidor intra-processo [in-process], e geralmente mais vantajosa. A DLL servidora carregada para a memria apenas quando os seus clientes comeam a utilizar algum objeto dela e geralmente compartilhada por todos os clientes. Alm disso, a velocidade da chamada de mtodos bem maior, porque os valores no precisam ser passados entre dois processos (.EXEs) diferentes. Nota: as funes fornecidas pelo COM antes eram conhecidas pelo nome de OLE (Object Linking and Embedding - vinculao e incorporao de objetos). Hoje OLE apenas uma parte do modelo COM, que permite objetos visuais serem inseridos de um programa em outro. Nota: no recomendvel usar o tipo Variant exceto para manipular objetos COM. Ele pode ser fonte de erros no programa difceis de detectar, j que ele evita a verificao normal de tipos do Delphi. Alm disso, acessar variveis desse tipo mais ineficiente.

O tipo Variant
O tipo Variant em Delphi muito usado com objetos COM. Uma varivel desse tipo pode conter dados de tipos diferentes durante a execuo, por exemplo: var qualquerCoisa: Variant; begin qualquerCoisa := 2.5; // um nmero real

qualquerCoisa := 'Texto'; // uma string Quando voc no tem acesso a uma biblioteca de tipos, essa s vezes a nica forma de passar parmetros a um mtodo de um objeto COM. Se o Delphi no sabe o tipo dos parmetros, ele assume que so todos do tipo Variant. Por exemplo: //o tipo do parmetro de Mostrar string ou inteiro? //o Delphi no tem como verificar em tempo de projeto obj.Mostrar('Texto'); obj.Mostrar(23); Esse tipo de varivel tambm pode conter referncia a uma interface de um objeto COM qualquer, o que permite chamar mtodos desse objeto. Veremos essa caracterstica com mais detalhes.

Distributed COM (DCOM)


O modelo COM permite a comunicao entre objetos no mesmo computador. J o modelo de objetos componentes distribudo (DCOM) permite a comunicao entre objetos em diferentes computadores, atravs de uma rede. Essa comunicao, depois de feita a conexo, totalmente transparente para os objetos envolvidos. Um objeto pode chamar os mtodos de outro como se eles estivessem sendo executados no mesmo computador e receber valores de retorno de funes normalmente. O DCOM usado pelo prprio Delphi, para implementar aplicaes multi-tier, com os componentes ClientDataSet, DCOMConnection e Provider vistos anteriormente. Como o DCOM uma tecnologia nova, no est disponvel no Windows 95. O software do DCOM includo com o Windows NT 4.0, no Windows 98 e no Windows 2000 (futura verso do NT). Para us-lo no Windows 95 preciso instal-lo separadamente. Voc pode obter o DCOM para Windows 95 (DCOM95) na Internet em: http://www.microsoft.com/com Clique no link "DCOM95" e siga as instrues da pgina para download e instalao.

ActiveX
ActiveX um nome comum para um conjunto de tecnologias de criao de componentes desenvolvidas sobre a base do COM/DCOM, algumas delas direcionadas para a Internet. Por enquanto note que a pgina "ActiveX" do repositrio no Delphi permite criar vrios tipos de componentes, sejam objetos COM ou ActiveX.

Criando um servidor de objetos


Vamos criar um servidor de objetos simples, com uma classe de objeto que vai simplesmente fazer um clculo e retornar um resultado. Para criar um .EXE servidor de objetos, simplesmente use File|New Application. No caso, vamos criar uma .DLL servidora de objetos. Clique em File|New, depois clique na pgina "ActiveX" do repositrio, escolha "ActiveX Library" e clique Ok. Uma DLL servidora tem que ser do tipo "ActiveX Library", no pode ser uma DLL comum. O Delphi vai criar apenas o arquivo de projeto de uma DLL (com library Project1 no cabealho). Agora salve o projeto com o nome de SERVOBJ.DPR. Lembre-se que esse ser o nome da DLL compilada (SERVOBJ.DLL). Agora vamos criar um objeto nessa biblioteca. Clique em File|New novamente, na pgina "ActiveX" e escolha "Automation Object". Isso vai mostrar o "Automation Object Wizard": Em Class name, digite o nome da classe que ser criada. No caso, use "Calculo". Note que esse no inclui um "T". A opo Instancing determina como podem ser criados os objetos dessa classe pelos programas clientes. Se voc deixar "Multiple Instance", o default, vrios objetos podem ser criados. "Single instance" determina que apenas um objeto dessa classe pode ser criado. J "Internal" determina que clientes no podem criar, mas apenas utilizar objetos j existentes. Mantenha o default em Instancing e clique Ok. O Delphi vai fazer vrias coisas no projeto: Cria uma classe de objetos COM (ou CoClass) chamada "Calculo", que corresponde a uma classe no Delphi chamada "TCalculo". Cria uma interface para essa classe, chamada "ICalculo". Cria uma biblioteca de tipos, que define essa classe e interface. A biblioteca tem o nome do projeto e guardada num arquivo chamado SERVOBJ.TLB. Essa biblioteca mostrada imediatamente na tela. Cria uma unidade auxiliar para trabalhar com a biblioteca de tipos, chamada "ServObj_Tlb.PAS". Essa unidade gerada automaticamente a partir do contedo da biblioteca de tipo. Cria uma unidade com a declarao da classe 'TCalculo', que aparece no editor (inicialmente com o nome provisrio de 'Unit1').

A unidade recm-criada ainda no foi salva. Salve o projeto inteiro e d o nome a essa unidade de 'MCALCULO.PAS'. O editor de biblioteca de tipo [type library editor] aparece na tela com o arquivo 'Servobj.tlb' aberto. Se voc fechar essa janela, pode abri-la de da mesma forma que se abre um formulrio, posicione em 'Servobj_tbl' e pressione a tecla F12. Cada classe ou interface criada tem um identificador gerado para ela, chamado de GUID (globally unique identifier - identificador globalmente nico), um nmero grande que deve ser diferente para cada classe instalada no computador. Normalmente o Delphi gera os GUIDs automaticamente, e voc no precisa se preocupar com eles. Para poder acrescentar novos mtodos ou propriedades ao nosso objeto, devemos acrescent-los na interface ICalculo nesse editor, voc pode utilizar a pgina "Text" para escrever o cdigo diretamente, desde que saba a sintaxe correta. Clique em "ICalculo" do lado esquerdo do editor em seguida clique com o direito do Mouse e escolha a opo "New" e "Method". Depois clique na pgina "Parameters" e informe os parametros para este mtodo como a seguir: Somar ser um mtodo que retorna um valor do tipo Integer , por isso foi necessrio alterar a opo "Return Type". Crie outro mtodo como o nome 'Converter' e os parmetros como a figura abaixo: Nem todos os tipos de parmetros que podem ser declarados no Delphi podem ser usados em um mtodo de um objeto COM. No caso 'WideString' uma verso diferente do tipo string que deve ser usada em mtodos COM. Como o parmetro 's' ser passado por referncia,ento necessrio que o contedo da opo "modifier" seja 'var'. Nota: o tipo WideString, usado em mtodos COM suporta caracteres de outros alfabetos, como chins, japons, russo etc. Agora clique no boto "Refresh Implementation" para atualizar o cdigo. Agora note o contedo da unidade 'MCALCULO.PAS' no editor de texto do Delphi. Ela contm a declarao da classe 'TCalculo', com o seguinte: type TCalculo = class(TAutoObject, ICalculo) protected function Somar(x, y: Integer): Integer; safecall; procedure Converter(var s: WideString); safecall; end; Essa classe derivada de 'TAutoObject', a classe bsica para todos os objetos de automao OLE. Na declarao tambm est indicado que ela implementa a interface ICalculo, ou seja, que contm todos os mtodos declarados nessa interface, que est declarada na unidade SERVOBJ_TLB.PAS. Os mtodos em questo so a funo 'Somar' e o procedimento 'Converter'. Se voc der uma olhada na parte implementation da unidade, ver que o corpo desses mtodos j foi criado, embora vazio. Vamos complet-los: function TCalculo.Somar(x, y: Integer): Integer; begin result := x + y; end; procedure TCalculo.Converter(var s: WideString); begin // converte 's' para maisculas s := AnsiUpperCase(s); end; Nota: a funo 'AnsiUpperCase' converte uma string para maisculas, considerando caracteres acentuados corretamente. Para usar a funo AnsiUpperCase, inclua o nome 'SysUtils' na clusula uses dessa unidade (ao lado de ComSrv). Agora compile o projeto (como uma DLL, voc no vai execut-lo) com Project|Compile. Alm disso, voc deve registrar esse servidor de objetos no Windows. A operao de registro vai guardar informaes sobre esse servidor no Registro [registry] do Windows, para que outros programas possam saber da existncia dele. Para registrar o servidor no boto "Register Type Library" do editor de biblioteca de tipo. Salve o projeto novamente.

Utilizando o servidor
Vamos fazer um pequeno programa exemplo que chama o servidor. Caso no esteja posicionado no projeto 'ServObj.dpr' abra-o . Salve seu grupo com o nome 'ServObj' e crie um novo aplicativo neste projeto. No formulrio altere a propriedade Caption para 'Usando servidor COM', coloque os seguintes componentes: Coloque os seguintes nomes nos componentes: boto "Criar objeto" btnCriar boto "Destruir objeto" btnDestruir primeiro Edit editNum1 segundo Edit editNum2 boto "Somar" btnSomar terceiro Edit editSoma quarto Edit editTexto boto "Converter" btnConverter O boto "Criar objeto" ser responsvel por criar um novo objeto, usando o servidor de objetos. Antes de criar um procedimento para ele, abra o arquivo de unidade, e declare uma varivel global do tipo Variant. Tambm acrescente uma clusula uses: implementation {$R *.DFM} uses ComObj; var Objeto: Variant; A unidade 'ComObj' foi utilizada por causa da funo CreateOleObject, que ser usada para criar o objeto. A varivel 'Objeto' foi declarada para fazer referncia ao objeto criado. No procedimento de evento do boto btnCriar, escreva: procedure TForm1.btnCriarClick(Sender: TObject); begin Objeto := CreateOleObject('ServObj.Calculo'); end; O parmetro de CreateOleObject o nome qualificado da classe de objeto a ser criada, composto do nome do servidor, 'ServObj', mais o nome da CoClass, 'Calculo'. O boto "Destruir objeto" s precisa atribuir o valor especial Unassigned varivel de objeto. Isso libera o objeto da memria e tambm o servidor de objetos, se no houver outros objetos criados. Esse valor o mesmo com o qual inicializada uma varivel Variant, no incio da execuo: procedure TForm1.btnDestruirClick(Sender: TObject); begin Objeto := Unassigned; end; Agora os botes "Somar" e "Converter" vo simplesmente chamar os mtodos correspondentes, atravs da varivel 'Objeto': procedure TForm1.btnSomarClick(Sender: TObject); begin editSoma.Text := Objeto.Somar(editNum1.Text, editNum2.Text); end; procedure TForm1.btnConverterClick(Sender: TObject); var s: widestring; begin s := editTexto.Text; Objeto.Converter(s); editTexto.Text := s; end; Note que ao chamar o mtodo 'Somar', no foi feita converso de strings para inteiros. O tipo Variant faz essas converses dinamicamente. J no mtodo 'Converter', que recebe um parmetro por referncia, uma varivel foi declarada com exatamente o tipo esperado (WideString) pelo procedimento.

Execute o programa. Clique primeiro em 'Criar objeto', antes de clicar num dos botes que chamam os mtodos. Se voc fizer o contrrio, ver uma mensagem de erro de execuo. Na verdade, no preciso destruir o objeto sempre, ele ser destrudo ao terminar a execuo. Salve o projeto com os nomes de USACOM.PAS e USACOMP.DPR.

Importando uma biblioteca de tipo


O que acontece se voc digita um nome errado de mtodo, por exemplo: Objeto.Coverter(s); //falta o 'n' Note que o Delphi no consegue verificar o erro durante a compilao, mas s ao tentar executar o programa. Isso porque ele no est usando a biblioteca de tipos. Outros servidores de objeto poderiam ser chamados a partir do Delphi. Por exemplo, como o Microsoft Word um servidor de objetos, voc poderia usar a funcionalidade do MS Word dentro do seu programa. Por exemplo, o cdigo abaixo executa o Word, abre um arquivo chamado "C:\teste.doc", imprime esse arquivo e fecha o Word: var app, doc: Variant; begin app := CreateOleObject('Word.Application'); doc := app.Documents.Open('c:\teste.doc'); doc.Insert(Cabecalho); app.PrintOut; app.Quit(False); end; Todos os comandos do Visual Basic for Applications (VBA) do Word, podem ser chamados dessa forma. Nesse caso, para que o Delphi possa saber os mtodos e propriedades dos objetos indicados, voc pode importar a biblioteca de tipos do programa. Isso cria uma unidade que declara as interfaces e classes necessrias. Para fazer isso, use o menu Project|Import Type Library e escolha qual a biblioteca que quer importar, por exemplo "Microsoft Office 8.0 Type Library".

10 - Pginas Web
Conceitos e protocolos da Internet Aplicaes cliente Criando aplicaes de servidor Pginas Web dinmicas Incluindo produtos dinamicamente

Conceitos e protocolos da Internet


A Internet um meio de comunicao que permite transmitir dados entre computadores do mundo inteiro. Ela usa tecnologias abertas (padres) que permitem diferentes sistemas interoperarem entre si. Os protocolos da Internet definem como implementado o transporte de dados entre computadores. Os computadores conectados Internet (sejam estaes ou servidores) so chamados geralmente de hosts. A pilha de protocolos TCP/IP o software bsico para comunicao via Internet e abrange alguns protocolos principais: IP Internet Protocol: protocolo que fornece a base para todos os outros. Para se comunicar via IP, cada computador tem um endereo IP nico, por exemplo 200.201.202.203. Muitas vezes um computador tambm tem um nome de host [hostname], como ftp.borland.com, que traduzido dinamicamente para um endereo. TCP Transmission Control Protocol: permite transmitir pacotes de dados entre computadores, e garante a entrega desses pacotes apesar de erros que possam ocorrer no caminho. Automaticamente retransmite os pacotes se necessrio. UDP User Datagram Protocol: transmite os dados, mas no garante a entrega dos pacotes. A aplicao decide se vai retransmitir os dados ou no. Muitos computadores tm hostnames amigveis, que so compostos da forma: nomeComputador.nomeDomnio, onde o nomeDomnio registrado para uma empresa em particular. Por exemplo, ftp.borland.com e

www.borland.com so dois computadores (ftp e www) no mesmo domnio (borland.com). Esses nomes so traduzidos dinamicamente para seus endereos IP correspondentes, atravs de um banco de dados distribudo chamado DNS (Domain Name System). Um programa que usa os protocolos TCP ou UPD para se comunicar com outro computador deve usar uma porta no computador de destino. Uma porta apenas um nmero lgico que alocado para cada programa. Um programa pode escutar numa porta, ou seja, ele pode ficar esperando conexes de clientes que se comunicam com aquela porta. Nesse caso, o programa chamado de servio ou programa servidor. Por exemplo, um browser (programa que mostra pginas Web) se conecta a um host, que tem um programa chamado servio HTTP (ou servidor Web) sendo executado. O browser usa o protocolo TCP e se conecta porta 80 do host (nmero padro).

Protocolos de alto nvel


Outros protocolos trabalham em um nvel mais alto do que TCP/IP e UDP/IP e so usados para vrias aplicaes teis (obs. P.=Protocol): FTP File Transfer P.: usado para transferncia de arquivos. SMTP Simple Mail Transfer P.: usado para transferncia de e-mail entre diferentes servidores. Permite enviar email a qualquer local, e roteia o correio at chegar ao local de destino. POP Post Office P.: usado por um cliente (geralmente um PC) para se conectar a um servidor de correio e consultar uma caixa postal, trazendo as mensagens atuais. HTTP Hypertext Transfer P.: usado para transferir pginas Web (arquivos HTML) de um servidor para um browser (programa de acesso Web), como o Netscape Navigator ou o Internet Explorer. A rede de servidores HTTP chamada de World Wide Web. NNTP Network News Transfer P.: usado para manter e atualizar grupos de discusso ou grupos de notcia [newsgroups], uma espcie de frum aberto onde as pessoas do suas opinies e recebem respostas, ou anunciam novidades sobre um determinado assunto. A rede de servidores NNTP chamada de Usenet. Um servio um programa que executa num computador, atendendo a conexes feitas para um desses protocolos. Por exemplo, um servio HTTP (ou software servidor Web) um programa que escuta na porta 80 e, ao receber uma conexo, com um pedido de busca de arquivo, l o arquivo e o manda de volta. A biblioteca Windows Sockets ou Winsock foi criada para facilitar o desenvolvimento de aplicaes Windows com acesso Internet. Essa biblioteca (DLL) trabalha com o conceito de socket [soquete], que uma conexo estabelecida entre dois programas para transmitir dados.

URLs
Num browser, o usurio pode digitar diretamente um endereo que corresponde a um item de informao. Esse endereo chamado URL (Uniform Resource Locator). Alguns exemplos de URLs so: file://D|/Doc/Web/teste.html http://www.borland.com/delphi/support/index.html ftp://ncsa.uiuc.edu/mosaic/windows/mw32.zip news:comp.lang.delphi Uma URL se divide em vrias partes, algumas opcionais: - O protocolo usado para acessar o arquivo: file, http, ftp, news, etc. O nome de protocolo sempre seguido de ":" (dois-pontos). Esse nome obrigatrio, embora os browsers assumam http como o protocolo default. - O nome de host (para http ou ftp): nos exemplos: www.borland.com ou ncsa.uiuc.edu. - O diretrio ou caminho para localizar o arquivo, como "/delphi/support/", "/mosaic/windows", "D:/Doc/Web/"; - O nome de arquivo, como "teste.html", "mw32.zip", "default.htm" ou "comp.lang.delphi". Nem sempre corresponde a um arquivo fsico realmente.

Intranets
Uma intranet uma rede local ou uma rede em pequena escala que usa os protocolos da Internet e na qual so executadas aplicaes que utilizam esses protocolos. Numa intranet, os computadores podem ser acessados tambm com as funes de rede comuns. Por exemplo, se um servidor HTTP est sendo executado numa Intranet (no Windows NT ou 95, por exemplo), voc pode "publicar" pginas Web nele simplesmente copiando arquivos. Uma URL para este servidor pode ser montada usando apenas o nome do computador na rede, por exemplo: http://meuservidor/teste.htm. Uma intranet pode estar ou no conectada Internet. Se no estiver, algumas tarefas de configurao e utilizao so mais simples. Por exemplo, no preciso ter a resoluo de nomes do DNS nem registrar um nome de domnio ou faixa de enderereos IP.

Aplicaes cliente
Uma aplicao cliente Internet usa algum dos protocolos da Internet para se conectar a um servio em um computador remoto. Alguns tipos de aplicao cliente so: - Clientes de e-mail: software usado para ler e enviar correio eletrnico (e-mail) via Internet. Usa os protocolos POP e SMTP. - Cliente FTP: software que transfere arquivos de/para um servidor FTP. - Leitor de notcias [newsreader]: software que acessa um servidor de newsgroups e permite ler ou enviar mensagens para os newsgroups desejados. - Browser Web: (tambm chamado navegador Web): busca pginas Web da Internet, compostas de arquivos HTML, imagens etc. e mostra o contedo dessas pginas numa interface grfica de usurio. Usa o protocolo HTTP para transferir os arquivos do servidor. Os browsers mais usados hoje, Netscape Navigator e Microsoft Internet Explorer, tambm funcionam como cliente de FTP e tm programas associados de e-mail e leitores de notcias.

Componentes para aplicaes cliente


Voc pode criar esses tipos de aplicaes em Delphi, usando os componentes da pgina "Internet". Todos os componentes dessa pgina so no-visuais, exceto o HTML. Os componentes usados em aplicaes clientes so: ClientSocket e TCP: permite fazer uma conexo genrica com qualquer servidor atravs de um socket, usando o protocolo TCP para se conectar a um servidor remoto, permitindo ler e enviar dados atravs da conexo. Veremos como usar o ClientSocket mais tarde. Use um ou outro desses componentes. Eles oferecem a mesma funcionalidade, mas a forma de usar diferente. UDP: permite se conectar com um servidor, usando o protocolo UDP, que no garante a entrega dos dados. Permite ler e enviar dados atravs da conexo.

Criando aplicaes de servidor


Existem vrios tipos de aplicaes de servidor (ou servios): - Servidor Web: servio HTTP, que atende a pedidos e retorna ao cliente pginas Web. Para obter essas pginas, ele l arquivos HTML armazenados em disco, ou chama um programa CGI (ver abaixo) que gera o texto da pgina dinamicamente. - Servio SMTP: recebe uma mensagem de e-mail e verifica se ele prprio o destinatrio da mensagem. Se for, guarda a mensagem em disco. Se no, envia-a para outro servidor SMTP at que ela chegue ao destino. - Servio POP: l as mensagens que foram guardadas em disco pelo servio SMTP e retorna essas mensagens ao cliente quando ele as requisitar. Nota: para um exemplo mais completo de Web browser, veja o programa exemplo WEBBROWS.DPR no diretrio DEMOS\COOLSTUF do Delphi.Voc pode criar uma aplicao que se comunica em baixo nvel, usando o protocolo TCP (ver na prxima sep), mas isso um pouco complexo. Ou voc pode criar um servio como um dos acima, mas geralmente no vale a pena.

Pginas Web dinmicas


A maioria das aplicaes de servidor hoje esto relacionadas Web e funcionam em conjunto com o software servidor Web. Quando um usurio consulta uma pgina Web, ela pode ser esttica (um arquivo HTML criado anteriormente e guardado em disco) ou pode ser uma pgina dinmica (gerada por um programa executvel). O programa deve gerar dados em formato/linguagem HTML. No primeiro caso, o prprio servidor Web l os arquivos da pgina e os repassa para o cliente. No segundo caso, ele executa outro programa para gerar dinamicamente o contedo da pgina. Por exemplo, O AltaVista (http://www.altavista.digital.com) usa um programa para pesquisar dinamicamente no seu banco de dados de documentos. Existem vrias formas de criar uma aplicao que gera pginas dinmicas: - CGI: (Common Gateway Interface - interface de gateway comum): permite que o servidor Web execute um programa. Na plataforma Windows 32 bits, esse deve ser um programa .EXE de modo texto (ou aplicao de console) que l dados a partir da entrada padro e grava dados na sada padro. Os dados "escritos" na sada padro sero mostrados como contedo da pgina Web dinmica. CGI suportado por todos os softwares de servidor Web existentes.

- Win-CGI: variao do CGI criada especificamente para programas Windows e suportada por alguns servidores Web. Nesse caso, o programa no precisa ser uma aplicao de console, mas pode ser um programa grfico normal. - ISAPI/NSAPI (Internet Server API/Netscape Server API): interfaces criadas pela Microsoft e Netscape, respectivamente, para seus softwares de servidor Web. Nesse caso, em vez de um programa executvel, as pginas dinmicas so geradas por uma DLL, que carregada dinamicamente pelo servidor. Isso tem vrias vantagens em relao a CGI e Win-CGI: menos ocupao de memria e maior velocidade. Como a DLL pode ficar na memria, mesmo depois que atendeu a um pedido de pgina, da prxima vez que ela for chamada, a consulta ser mais rpida. No Delphi, voc pode criar os trs tipos de aplicaes. Uma aplicao CGI um programa de console, o que significa que ele no pode ter formulrios (mas pode ter data modules). J uma aplicao Win-CGI pode ter formulrios, embora isso no seja realmente til, pois ela executada no servidor e no no computador cliente. Voc deve escolher o tipo de aplicao ao criar um novo projeto. Uma DLL ISAPI/NSAPI criada em Delphi pode ser usada com os dois tipos de API: tanto pode ser usada no Microsoft Internet Information Server quanto nos servidores Web da Netscape.

URLs e aplicaes de servidor


Uma aplicao de servidor chamada atravs de uma URL, um endereo que pode ser informado de vrios modos: Pode ser digitada pelo usurio diretamente no browser. Pode estar associada a um "link" no qual o usurio clicou. No cdigo HTML, seria algo como <A HREF=url ....>Clique aqui</A> Pode ser chamada por um "formulrio" HTML, com a sintaxe: <FORM ACTION=url... > Em qualquer caso, essa URL se compe de vrias partes. Por exemplo, considere a seguinte URL e suas divises: http://www.servidor.com/progs/pesquisa.exe?nome=fulano&escopo=3 http o protocolo usado (sempre HTTP) www.servidor.com o nome do servidor (hostname) onde est executando o servidor Web. /progs/ o diretrio onde o programa est localizado pesquisa.exe o nome do programa CGI que ser executado nome=fulano o primeiro parmetro passado para o programa. O nome do parmetro 'nome' e o valor 'fulano' escopo=3 o segundo parmetro, com nome 'escopo' e valor '3'. Note que os parmetros so separados do nome do programa com '?' e so separados entre si pelo caractere '&'. O nome do parmetro pode ser digitado diretamente ou, no caso de chamar a URL a partir de um formulrio HTML, corresponde aos nomes dos controles no formulrio HTML. Outro exemplo de URL, para uma aplicao ISAPI/NSAPI : http://teste/scripts/produtos.dll/consultar?Codigo=3 teste o nome do servidor Web. Se o servidor est numa Intranet, no preciso especificar o domnio (.com) /scripts/ o subdiretrio no servidor. produtos.dll o nome da DLL servidora, que gera pginas Web dinmicas /consultar a informao de caminho que ser passada como um parmetro a mais para a DLL Codigo=3 um parmetro adicional. A informao de caminho [path info] usada para selecionar diferentes aes dentro do mesmo executvel ou DLL. Apesar de parecer um subdiretrio na URL, ela tratada apenas como um parmetro a mais. Uma aplicao pode tomar aes diferentes, dependendo da informao de caminho que foi passada. Num servidor Web, geralmente um diretrio especfico (chamado de raiz da Web) criado para armazenar pginas Web e aplicaes que geram pginas dinmicas e os subdiretrios especificados numa URL so sempre relativos a esse diretrio. Por exemplo, suponhamos que o servidor Web com o nome TESTE exista na sua Intranet e est configurado para usar o diretrio C:\PROJETOS\HTML como raiz da Web. Quando o usurio digita a URL acima, contendo o caminho /scripts/produtos.dll, o servidor na verdade vai procurar o arquivo: C:\PROJETOS\HTML\SCRIPTS\PRODUTOS.DLL. Componentes para aplicaes de servidor Os seguintes componentes no-visuais so usados para criar aplicaes de servidor: ServerSocket e TCP: permite criar um servio, que escuta em uma determinada porta, e permite aos clientes se conectarem atravs de sockets, usando o protocolo TCP. Permite aos clientes lerem e escreverem dados atravs da conexo. Veremos como usar o ServerSocket mais tarde. Use um ou outro desses componentes. Eles oferecem a mesma funcionalidade, mas a forma de usar diferente.

WebDispatcher: usado em aplicaes CGI, Win-CGI ou ISAPI/NSAPI. Geralmente no necessrio us-lo. Em vez dele, a aplicao pode usar um WebModule, como veremos. S poderia ser til em aplicaes que usam um DataModule normal. PageProducer: componente que produz pginas Web dinmicas a partir de um arquivo HTML modelo. Esse arquivo-modelo contm marcadores [tags] que devem ser substitudos dinamicamente pela aplicao. QueryTableProducer: recebe parmetros passados pelo browser e passa esses parmetros a um componente Query, produzindo como resultado uma "tabela" HTML. Esse componente formata cada campo da consulta em uma coluna da tabela. DataSetTableProducer: produz uma "tabela" HTML, lendo os dados de um componente DataSet qualquer. WebModule: no um componente da paleta, mas um tipo especial de mdulo de dados (DataModule) que est presente em todas as aplicaes de servidor. Um WebModule criado automaticamente quando voc cria uma aplicao de servidor.

Passos para criao da aplicao


Os princpios para criar uma aplicao de servidor so os mesmos para qualquer um dos trs tipos de aplicao. Basicamente voc deve seguir os seguintes passos: 1) Criar uma nova aplicao de servidor no Delphi e escolher o tipo desejado: CGI, Win-CGI, ou ISAPI/NSAPI. O Delphi cria um WebModule, no qual voc pode colocar componentes no-visuais. 2) Coloque nesse mdulo os componentes que sero usados, como componentes para acessar bancos de dados e tabelas e/ou componentes que ajudam a gerar pginas, como PageProducer, QueryTableProducer , DataSetTableProducer etc. 3) No mdulo, defina uma ou mais aes. Uma ao ser executada quando a aplicao for chamada por uma URL. Para cada ao, voc deve definir a informao de caminho (path info) que vai diferenci-la das outras. 4) Para cada ao, crie um procedimento para o evento OnAction e escreva o cdigo que gera o contedo da pgina. 5) Salve e compile o projeto (no execute). 6) Copie o arquivo .EXE ou .DLL compilado para um diretrio no seu servidor Web. 7) Para testar a aplicao, abra um browser e digite uma URL que vai executar a aplicao. Faremos os exemplos usando aplicaes CGI, que podem ser executadas em qualquer servidor Web. No entanto, se voc tiver acesso a um dos servidores da Microsoft ou Netscape, recomendvel usar um deles para o teste. Nos exemplos, vamos assumir os seguintes nomes: INSTRUTOR Ser o nome de computador e hostname do servidor Web de teste. O mesmo nome usado para acess-lo com as funes de rede do Windows e tambm em URLs, como http://curso. Troque pelo nome do seu servidor para fazer os testes em sua intranet/Internet. C:\Web Ser o nome do diretrio onde so colocadas as pginas Web dentro do servidor INSTRUTOR. Todos os caminhos especificados em URLs so relativos a esse diretrio. Para testar na sua Intranet, troque pelo diretrio apropriado (por exemplo, C:\INETPUB\WWWROOT no servidor Microsoft IIS).

Consultando tabelas dinamicamente


As aplicaes Web mais teis so aquelas que fazem consultas a bancos de dados, e retornam pginas com o resultado de uma pesquisa nesses bancos de dados. Vamos criar como exemplo uma aplicao que l e mostra a tabela de produtos. Clique em File|New, escolha "Web server application" e clique Ok. Voc ver uma caixa de dilogo com trs opes: Nas opes de tipo de aplicao, escolha "CGI" e clique Ok. Ao fazer isso, voc ver um novo WebModule chamado WebModule1, que tem a mesma aparncia de um mdulo de dados. Como essa uma aplicao de console, ela no tem e no pode ter formulrios. Se voc olhar no arquivo de projeto, com Project|View Source, ver uma linha contendo: {$APPTYPE CONSOLE} Essa linha foi criada para definir que o Delphi vai gerar uma aplicao de console. Antes de mais nada, salve o projeto. D os nomes de MWPROD.PAS para a unidade do WebModule e WPROD.DPR para o projeto. Lembre-se que o nome do projeto tambm ser o nome da aplicao compilada (MWPROD.EXE). Nota: alguns servidores Web exigem que programas executveis sejam colocados num diretrio especfico, como "/cgi-bin". Nesse mdulo, coloque um componente Database para connectar com o Oracle. Clique duas vezes no componente Database. Em "Name" coloque dbProduto, na opo "Driver Name", selecione Oracle. Clique no boto "Defaults" e altere os parmetros:

SERVER NAME=nome do seu servidor Oracle USER NAME=um login vlido LANGDRIVER=DBWINUS0 PASSWORD=senha do usurio Desmarque a opo 'Login Prompt' e altere a propriedade Name para 'DbProduto'. Acrescente tambm um componente Table para acessar a tabela de produtos. Altere o nome para 'tblProduto', DatabaseName para ,'dbProduto' e TableName para o nome da tabela, 'PRODUTO.DB'. Ative a tabela. Coloque agora no mdulo um componente DataSetTableProducer, da pgina Internet. Esse componente produz dados tabulares, em formato HTML, a partir de uma tabela do banco de dados (ou um dataset qualquer). Altere o seu nome para 'dstpProduto'. Na propriedade DataSet, selecione 'tblProduto'. Na propriedade Header, voc pode editar um contedo HTML que aparece antes da tabela propriamente dita. Essa propriedade uma lista de strings. Abra o editor para essa propriedade e digite o seguinte: <H1>Lista de Produtos</H1> Depois clique duas vezes sobre o componente para ver o resultado. Voc ver uma janela como a seguinte: A lista de colunas na parte superior direita (com "Field Name", "Field Type") deve estar inicialmente mostrar todos as colunas existente na tabela 'Produto' . Caso deseja remover alguma coluna clique no boto "" Delete na parte superior direita. Isso vai remover a coluna que esta posicionada. O boto "" Add , permite adicionar uma coluna . Se adicionar , clique no item "THTMLColumn1" e altere a propriedade FieldName, selecionando o campo que deseja da tabela. Para cada coluna, voc pode tambm alterar a propriedade Align para determinar o alinhamento dessa coluna, ou Title.Align para alterar o alinhamento do cabealho de coluna. Feche essa janela. Agora o que precisamos definir uma ao que vai mostrar o contedo da tabela. Para isso, clique com o boto direito no mdulo de dados e escolha 'Action Editor'. Crie uma nova ao com o boto Add. Voc ver um novo item na lista, chamado 'WebActionitem1'. Clique nesse item. No Object Inspector , altere a propriedade Name para 'wacLista' e a propriedade Default para true. O que distingue uma ao da outra a propriedade PathInfo. No caso vamos deix-la vazia, porque s teremos uma ao. Se houver mais de uma, o mdulo decide qual ser executada baseado na informao de caminho da URL. Por exemplo, uma URL da forma http://curso/wprod.exe/testar executaria a ao cujo PathInfo = '/testar'. Ainda no editor de aes, com a ao 'wacDefault' selecionada, clique na pgina de eventos do Object Inspector. No evento OnAction, crie um procedimento de evento. Ele ser chamado quando a ao est sendo executada. Digite o seguinte: procedure TWebModule1.WebModule1wacListaAction (Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean); begin Response.Content := dstpProduto.Content; end; Essa ao simplesmente coloca na propriedade Content de 'Response' o resultado da funo-mtodo Content do componente 'dstpProduto'. A funo Content gera o cdigo HTML listando os campos da tabela. Crie um procedimento para o evento OnCreate do DataModule , nele iremos abrir o componente tblProduto : procedure TWebModule1.WebModule1Create(Sender: TObject); begin tblproduto.open end; Compile essa aplicao e copie para o diretrio apropriado no seu servidor Web. Para execut-la, digite a URL: http://instrutor/wprod.exe Voc dever ver a lista de produtos.

Outros componentes
Outro componente que no foi usado o QueryTableProducer, que permite facilmente passar os parmetros que foram informados na URL para um componente Query, que executa essa consulta. Para us-lo em sua aplicao, coloque um componente Query no Web module. A propriedade Query do componente QueryTableProducer deve referenciar esse componente Query. Os parmetros do comando SQL devem corresponder exatamente aos parmetros que sero passados na URL, por exemplo: URL: http://instrutor/pesquisarNome.exe?nomeBusca =abc&tipo=2 SQL: select Nome, PrecoVenda

from Produto where Nome like :nomeBusca and Tipo = :tipo Numa das aes do seu mdulo, faa algo como: Response.Content := QueryTableProducer1.Content;

Incluindo produto dinamicamente


Para incluir um produto na tabela 'Produto' utilizando browser , necessrio criar uma aplicao cliente que ser uma pgina em HTML esta ir permitir informar o cdigo e o nome do produto. A inteno deste exemplo no explicar a linguagem HTML , mas, como passar os parmetros utilizados numa pgina HTML para o servidor de aplicao, e este ir incluir esta informao no banco de dados.

Criando uma aplicao cliente


Na aplicao cliente iremos criar uma pgina em HTML , utilizando o bloco de notas. Ento vamos abrir o bloco de notas e acrescentar os comandos abaixo: <html> <head> <title>Cadastro de Produtos </title> </head> <form method="GET" action="//instrutor/Wprod.exe/Incluir?Nome=Nome&codigo=codigo"> <p>Cdigo: <input type="text" name="Codigo" size="5"></p> <p>Nome:<input type="text" name="Nome" size="40"></p> <p><input type="submit" value="OK" name="BtnOk"></p> </form> </body> </html> O cdigo <html> indica que os comandos abaixo fazem parte da linguagem HTML. Nesta pgina que ser gerada iremos colocar dois componentes para entrada de dados que sero mostrados atravs do comando: <p>Cdigo: <input type="text" name="Codigo" size="5"></p> <p>Nome:<input type="text" name="Nome" size="40"></p> Em 'input type' quando o seu contedo 'text' indica que voc esta criando um componente parecido com o 'Edit' do Delphi. Em 'name' foi informado o nome deste componente , e este nome ser usado no Delphi para obter o nome e o cdigo do produto, em 'size' voc especificar o tamanho deste componente. Abaixo destes cdigos criado um boto 'Submit' , que desta forma estamos dizendo que quando for precionada a tecla <Enter> esse boto ir executar o mtodo 'GET'. No comando abaixo ser mostrado como este mtodo foi criado. <form method="GET" action="//instrutor/Wprod.exe/Incluir?Nome=Nome&codigo=codigo"> E em 'action' definimos a ao deste mtodo que chamar o servidor de aplicao WProd.exe e a ao que ser executada a Incluir(que ser criada no prximo item). Salve o arquivo como 'IncluirProduto.htm'. <body>

Criando uma aplicao servidor


Vamos acrescentar uma ao para incluir produtos no servidor de aplicao que criamos anteriormente , o 'WProd'. Abra o projeto 'WProd.dpr'.

No mdulo 'MWProd' iremos criar uma ao 'Incluir'. Para isso clique com o boto direito neste mdulo e escolha 'Action Editor' . Crie um nova aco com o boto Add New (). Altere seu nome para 'WacInluir' e na propriedade PathInfo informe 'Incluir'. Agora crie um procedimento para o evento OnAction: procedure TWebModule1.WebModule1wacIncluirAction(Sender: TObject;Request: TWebRequest; Response: TWebResponse; var Handled: Boolean); var nome : string; codigo :integer; begin nome := Request.QueryFields.Values['Nome']; codigo := strtoint(Request.QueryFields.values['codigo']); tblProduto.insert; tblproduto.fieldbyname('nome').asstring := nome; tblproduto.fiedlbyname('codProduto').asinteger := codigo; tblproduto.post; response.content := '<HTML> Produto Cadastrado </HTML>' end; Esse procedimento de evento recebe alguns parmetros: 'Request' um objeto da classe 'TWebRequest', que contm vrias informaes sobre a requisio da pgina, incluindo os parmetros fornecidos. O parmetro 'Response' um objeto da classe 'TWebResponse', atravs do qual o programa retorna os dados. A propriedade QueryFields de 'Request' uma lista de strings contendo os parmetros enviados, na forma nome=valor. Na aplicao cliente chamamos a URL como ' <form method="GET" action="//instrutor/Wprod.exe/Incluir?Nome=Nome&codigo=codigo">', essa propriedade vai conter o valor do componente Nome e Codigo. A propriedade Values de uma lista de strings procura um nome ( no caso 'Nome' ou 'Codigo') e retorna o valor correspondente, que est depois do '='. Isso permite saber os parmetros passados. Na pgina iremos mostrar que o produto foi cadastrado, caso ocorra algum erro ele retorna a mensagem de erro. Para gravar os dados na tabela produto iremos usar os mtodos do componente Table que permite essa incluso, mas poderia ter utilizado um componente 'Query'. Salve o programa e compile, em seguido copie o arquivo Wprod para o diretrio apropriado no seu servidor Web. Para testar no explorer abra o arquivo 'IncluirProduto.htm'.

11 - Classes e Objetos
Criando uma classe simples Listas de objetos Exemplo: conta bancria Resumo

Criando uma classe simples


Uma classe, como vimos, define a estrutura de vrios objetos semelhantes. Cada objeto uma instncia da classe, que tem essa mesma estrutura. Por exemplo, se quisermos representar um ponto na tela, podemos criar uma classe contendo as coordenadas x e y: type TPonto = class x, y: integer; end; Vamos usar essa classe dentro de um projeto para manter uma lista de pontos e desenhar (ligar esses pontos) quando for necessrio.

Exemplo: desenho de pontos


Crie um novo projeto e coloque dois botes no formulrio, com as propriedades: 1 ponto: Caption: "Ligar pontos" e Name: btnLigar 2 ponto: Caption: "Limpar lista" e Name: btnLimpar

Na unidade do formulrio, declare a classe 'TPonto', logo aps a implementation, da forma como foi colocado acima. Acrescente tambm as seguintes declaraes de variveis: var Lista: array of TPonto; N: integer; A varivel 'Lista' um vetor de pontos, que seu tamanho ser criado em tempo de execuo. Cada elemento desse vetor uma referncia a um objeto da classe TPonto. A varivel 'N' ser usada para controlar quantos elementos esto sendo usados em 'Lista'. Como iremos criar o tamanho do vetor em tempo de execuo , ento no evento OnCreate do fomulrio acrescente o cdigo abaixo: procedure TForm1.FormCreate(Sender: TObject); begin setlength(lista, 30) end; Com o comando setlength definimos que o tamanho do vetor ser de 30 posies. No evento OnMouseDown do formulrio, vamos pegar as coordenadas do ponto que foi clicado e criar um objeto TPonto para guard-las: procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); var p: TPonto; begin p := TPonto.Create; //cria o objeto p.x := X; p.y := Y; Lista[N] := p; //guarda na lista N := N + 1; Canvas.Pixels[X,Y] := clBlue;//pinta um ponto azul end; Ao clicar no boto "Ligar pontos", vamos percorrer a lista de pontos e desenhar linhas entre eles: procedure TForm1.btnLigarClick(Sender: TObject); var k: integer; p: TPonto; begin if N = 0 then exit; // nada para ligar //definir a pos. de desenho no primeiro ponto Canvas.MoveTo(Lista[0].x, Lista[0].y); for k := 1 to N-1 do begin p := Lista[k]; Canvas.LineTo(p.x, p.y);//liga o anterior com p end; end; A ltima linha antes do end poderia ser escrita tambm: Canvas.LineTo(Lista[k].x, Lista[k].y);

Liberar memria
Agora falta limpar a memria usada pelos pontos quando o usurio clica no boto "Limpar lista". Para isso, vamos percorrer o vetor de pontos e chamar o mtodo Free em cada um deles: procedure TForm1.btnLimparClick(Sender: TObject); var k: integer; begin for k := 0 to N-1 do begin Lista[k].Free; //destri o objeto Lista[k] := nil; //indica que no tem mais ref. end; N := 0; end;

Note que quando voc fizer 'Lista[k].Free', o elemento na posio 'k' do vetor passa a conter uma referncia invlida a um objeto que j foi liberado. Por segurana, colocamos o valor nil nesse elemento. Salve os arquivos desse projeto como PONTOS.PAS e PONTOSP.DPR.

Listas de objetos
No exemplo anterior (PONTOSP.DPR), usamos um vetor para armazenar os objetos, de acordo com o comando SetLength definimos que seu tamanho ser 30 , mas este tamanho pode ser aumentado de acordo com a necessidade.Mas necessrio usar a varivel N para saber quantos pontos foram adicionados. Em vez de usar vetor, iremos utilizar a classe TList, para ter uma idia do seu funcionamento. Um objeto TList funciona como um vetor dinmico. Quando voc acrescenta um objeto a ele, ele aumenta de tamanho. Voc pode tambm retirar objetos dinamicamente. A propriedade Count diz a quantidade de objetos existentes dentro do TList. Inicialmente ela ser 0 (lista vazia). Vamos alterar o projeto anterior (PONTOSP.DPR) para usar um TList em vez de um vetor.

Exemplo: usando um objeto TList


Na unidade principal do projeto (PONTOS.PAS), logo aps a palavra implementation, existem algumas declaraes de variveis. Apague as declaraes das variveis 'Lista' e 'N'. Troque pelo seguinte: var Lista: TList; O objeto TList j tem a propriedade Count, por isso no preciso uma varivel separada s para guardar a contagem de elementos. Essa declarao de varivel no cria um objeto TList. Ela s diz que a varivel 'Lista' est apta a referenciar um objeto TList. Altere o procedimento de evento OnCreate do formulrio, para inicializar essa varivel, criando um objeto TList: procedure TForm1.FormCreate(Sender: TObject); begin Lista := TList.Create; end; No evento OnMouseDown do formulrio, faa as seguintes alteraes: begin p := TPonto.Create; //cria o objeto p.x := X; p.y := Y; Lista.Add(p); //guarda na lista Canvas.Pixels[X,Y] := clBlue;//pinta um ponto azul end; O mtodo Add adiciona um objeto lista e aumenta o valor de Lista.Count. Agora voc deve alterar tambm o procedimento do 'btnLigar': begin if Lista.Count = 0 then exit; p := TPonto(lista.items[0]); Canvas.MoveTo(p.x, p.y); for k := 1 to Lista.Count - 1 do begin p := TPonto(Lista.Items[k]); Canvas.LineTo(p.x, p.y); end; end; Note que 'Lista.Count' agora faz o papel de N. A propriedade Items um vetor que retorna um item guardado na lista, dado a posio. Mas essa propriedade retorna um item do tipo pointer (ponteiro), que pode ser vrias coisas em Object Pascal: uma referncia de objeto, um PChar, ou outros tipos de ponteiro. Resumindo, 'Lista', um objeto TList, no sabe o tipo de objeto que foi guardado nele e nem se importa com isso. Mas quando voc vai pegar um objeto de volta, deve informar o seu tipo, o que feito com a sintaxe TPonto(Lista.Items[k]) ou, em geral, NomeDaClasse(valor). Com isso, voc est interpretando valor como sendo uma referncia de objeto daquela classe.

Agora falta liberar a memria dos objetos TPonto e, alm disso, retir-los do TList. Note que so duas aes separadas: se voc executa Free para um objeto, voc vai destruir esse objeto, mas pode ser que na lista ainda haja uma referncia para ele. Abra o procedimento de evento 'btnLimparClick': var k: integer; p: TPonto; begin for k := 0 to Lista.Count - 1 do begin p := TPonto(Lista[k]); p.Free; //destri o objeto end; // limpar a lista Lista.Clear; end; Para limpar toda a lista, usamos o mtodo Clear. Se usssemos apenas o Clear, o objeto TList ficaria vazio, mas os objetos que ele referenciava continuariam existindo e ocupando memria. Salve o projeto novamente. Nota: a propriedade Items pode ser omitida, por exemplo:p := TPonto(Lista[k]);

Outros mtodos do TList


Alm dos mtodos e propriedade que utilizamos, a classe TList tem alguns outros que podem ser teis, como: Insert(pos,novoItem) - Insere um item no meio, na posio 'pos' e desloca os outros para frente. (Diferente de Add, que acrescenta ao final). Delete(pos) - Remove um item, dada a posio Remove(item) - Remove um item, dada uma referncia a ele. IndexOf(item) - Funo que Procura um item na lista e retorna a posio onde ele est, ou -1 se no encontrou Exchange(posPrimeiro, posSegundo) - Troca os dois itens de lugar: o que est em 'posPrimeiro' e o que est em 'posSegundo' Sort(NomeFuncao) - Ordena a lista. O parmetro o nome de uma funo de comparao que ser chamada durante a ordenao. Essa funo deve ser declarada como: function NomeFuncao(Item1,Item2: Pointer): Integer; A funo deve retornar um valor negativo se Item1 < Item2, um valor positivo se Item1 > Item2 e zero se Item1 = Item2.

Exemplo: conta bancria


Uma das utilizaes de classes criar objetos nos programas que se comportam como objetos do mundo real. Cada objeto tem campos que correspondem aos atributos ou propriedades dos objetos reais e tem mtodos que correspondem s operaes ou ao comportamento do objeto real. Por exemplo, se um programa faz o controle de contas de um banco, uma classe TConta pode representar contas bancrias. Cada instncia dessa classe representa uma conta bancria, com seu saldo individual. As operaes que podem ser feitas com uma conta envolvem depositar um certo valor em dinheiro ou retirar um valor. Vamos criar um programa que define e utiliza essa classe.

Definindo a classe
Crie um novo projeto. Esse projeto automaticamente tem um formulrio, que ser ignorado por enquanto. Salve os arquivos do projeto, ainda sem alteraes, como TESTECONTA.PAS e TESTECONTA.DPR. Vamos criar uma nova unidade separada para definir a classe TConta, porque uma unidade separada facilita reutilizar essa classe em outros projetos. Clique em File|New, escolha "Unit" e clique Ok. Salve a nova unidade como CLASSECONTA.PAS. Na parte interface da unidade, declare a classe como abaixo: unit ClasseConta; interface

type TConta = class Saldo: double; procedure Depositar(valor: double); procedure Retirar(valor: double); end; Cada objeto dessa classe tem um campo 'Saldo', que guarda o saldo da conta e mtodos 'Depositar' e 'Retirar' para as operaes de movimentao. Esses mtodos devem ser implementados na parte implementation da unidade: implementation uses SysUtils; //por causa de 'Exception' procedure TConta.Depositar(valor: double); begin //depositar um valor Saldo := Saldo + valor; end; Note que o procedimento deve ser declarado novamente com o nome da classe (TConta.Depositar). Isso porque na mesma unidade poderiam haver vrias classes com procedimentos que tm o mesmo nome. Note tambm que dentro do procedimento, que um mtodo da classe, o campo 'Saldo' acessado sem nome de objeto. Quando esse mtodo for chamado, ser para um objeto especfico, como: conta3.Depositar(200) e nesse caso o campo acessado, 'Saldo' equivale a conta3.Saldo. De forma geral, um mtodo da classe "sabe" qual o objeto no qual ele est executando. Se precisar de um nome para esse objeto, pode usar a varivel Self. Agora no procedimento Retirar, vamos retirar um valor do saldo, mas antes, verificaremos se existe saldo disponvel. Caso contrrio, vamos gerar uma exceo. Uma exceo na verdade um objeto da classe Exception ou de alguma classe derivada. procedure TConta.Retirar(valor: double); begin //retirar um valor //mas antes verificar se o valor excede o saldo if valor > Saldo then raise Exception.Create('Saldo insuficiente'); Saldo := Saldo - valor; end; Quem chamar esse mtodo responsvel por tratar as excees. Se o chamador no tratar, ento o programa ir mostrar a mensagem de exceo 'Saldo insuficiente'.

Usando a classe
Agora volte ao formulrio principal. Vamos colocar vrios controles no formulrio, que vo permitir o gerenciamento de uma lista de nomes de pessoas, com um objeto TConta para cada pessoa. Altere o Caption do formulrio para "Testando Contas". Coloque controles como na figura abaixo. O componente maior um ListBox. Os outros so labels, botes ou Edits. Coloque os seguintes nomes e propriedades para todos os componentes, exceto os Label: primeiro Edit: editNome list box: lstContas, Sorted: True boto "Criar conta": btnCriarConta, Default: True boto "Exc. conta": btnExcluirConta segundo Edit: editValor boto "Depositar": btnDepositar boto "Retirar": btnRetirar terceiro Edit: editSaldo, ReadOnly: True, ParentColor: True Na unidade do formulrio, no incio da seo implementation, declare o seguinte: implementation {$R *.DFM} uses ClasseConta;

var ContaAtual: TConta; Agora crie um procedimento para o boto "Criar conta". Nesse procedimento, vamos acrescentar o nome digitado lista, criar um objeto TConta e associ-lo a esse nome. Para manter uma lista de objetos associados, poderamos usar um objeto TList. Mas nesse caso existe uma forma mais fcil: um componente TListBox tem a propriedade Items, que um objeto TStrings para guardar os nomes adicionados lista. Esse objeto tem uma propriedade Objects, que permite guardar um objeto qualquer para cada nome da lista. No procedimento do btnCriar, faa o seguinte: procedure TForm1.btnCriarContaClick(Sender: TObject); begin ContaAtual := TConta.Create; lstContas.Items.AddObject(editNome.Text,contaAtual); lstContas.ItemIndex := lstContas.Items.Count - 1; editnome.clear; end; O mtodo AddObject de TStrings adiciona um nome lista e tambm um objeto associado. Nesse caso, usamos 'ContaAtual' para guardar o objeto criado. Quando o usurio clicar no listbox, (no evento OnClick), vamos buscar a conta associada ao item selecionado e guardar em 'ContaAtual'. Vamos tambm mostrar o saldo dessa conta: procedure TForm1.lstContasClick(Sender: TObject); begin ContaAtual := lstContas.Items.Objects[lstContas.ItemIndex] as TConta; MostrarSaldo; end; Para pegar de volta o objeto que foi guardado, usamos Items.Objects[posio]. Note a sintaxe "as TConta", porque a propriedade Objects retorna uma referncia do tipo TObject. preciso converter essa referncia para interpret-la como TConta. O procedimento 'MostrarSaldo' ser um mtodo da classe de formulrio, que ser usado em outros lugares tambm. Declare-o primeiro, dentro da classe de formulrio: TForm1 = class(TForm) .... private { Private declarations } procedure MostrarSaldo; public ... Agora implemente o procedimento com os seguintes comandos: procedure TForm1.MostrarSaldo; begin editSaldo.Text := FormatFloat('0.0',ContaAtual.Saldo); end; O boto "Excluir conta" deve destruir o objeto 'ContaAtual' e remover da lista o nome atual. Faa da seguinte forma: procedure TForm1.btnExcluirContaClick(Sender:TObject); begin ContaAtual.Free; ContaAtual := nil; with lstContas do Items.Delete(ItemIndex); end; Usando os mtodos Depositar e Retirar Agora os botes "Depositar" e "Retirar" vo chamar os mtodos correspondentes da classe TConta, no objeto 'ContaAtual'. Implemente os procedimentos de evento como abaixo: procedure TForm1.btnDepositarClick(Sender: TObject); begin ContaAtual.Depositar(StrToFloat(editValor.Text));

MostrarSaldo; end; procedure TForm1.btnRetirarClick(Sender: TObject); begin ContaAtual.Retirar(StrToFloat(editValor.Text)); MostrarSaldo; end; Execute o programa e verifique o que voc pode fazer com as contas. Existe uma falha no programa, que vamos ignorar por enquanto: quando no existe conta atual selecionada (no incio do programa ou quando excluda uma conta), a varivel ContaAtual = nil, e isso provoca erros de acesso memria. A maneira correta de evitar isso desabilitar os botes btnExcluirConta, btnDepositar e btnRetirar sempre que no existir uma conta atual e habilit-los novamente quando for criada.

Escondendo campos
O mtodo Depositar faz uma validao do valor a ser retirado, para evitar saldo negativo. Mas nada impede um programa de alterar o campo 'Saldo' diretamente, que viola a lgica da classe: conta.Saldo := -100; Para evitar esse tipo de problema, o Delphi permite esconder campos ou mtodos de uma classe com vrios nveis de proteo. O ocultamento de campos ou mtodos, tambm chamado encapsulamento, evita que sejam criados programas que possam violar a lgica interna do objeto. Vamos ocultar o campo Saldo de forma que ele no possa ser modificado. Altere a declarao da classe TConta em CLASSECONTA.PAS: type TConta = class public procedure Depositar(valor: double); procedure Retirar(valor: double); function Saldo: double; private FSaldo: double; end; O nome do campo foi mudado para 'FSaldo' (F de field - campo) e o seu nvel de proteo foi mudado para private. Foi criada uma nova funo 'Saldo', que permitir o acesso somente de leitura a esse campo. Existem trs nveis de proteo principais: public Campos e mtodos pblicos. Podem ser usados pela prpria classe e por qualquer parte do programa. protected [Protegido]: Campos e mtodos podem ser usados pela prpria classe e por classes derivadas desta, que recebem o campo ou mtodo por herana. private [Privativo]: Campos e mtodos so completamente escondidos: eles s podem ser usados pela prpria classe onde eles foram definidos. Se o nvel de proteo no especificado, o default public, como antes. Vamos implementar a funo 'Saldo', que simplesmente retorna o valor de 'FSaldo': function TConta.Saldo: double; begin Result := FSaldo; end; A funo 'Saldo' pode ser chamada para ler o saldo, mas no pode ser usada para alter-lo. O comando abaixo geraria erro de sintaxe: conta.Saldo := -100; Vamos substituir a varivel Saldo para FSaldo nos mtodos depositar e retirar. Execute o programa novamente. Note que o formulrio no precisa de nenhuma alterao. Outra vantagem de usar encapsulamento tambm evita a criao de programas que dependem da estrutura interna do objeto. Com isso, a forma de guardar o saldo poderia mudar, sem que os clientes dessa classe (programas que usam a classe) soubessem, desde que a funo Saldo continue funcionando. Por exemplo, a

classe poderia guardar o saldo inicial e calcular dinamicamente o saldo atual (na funo Saldo) a partir da lista de movimentaes. Salve o projeto novamente.

Usando Herana
A classe TConta tem um certo comportamento bem definido. Ela permite fazer depsitos ou retiradas e guarda o saldo. Mas suponhamos que seja necessrio para um programa especfico tirar um extrato da conta. Nesse caso, o objeto conta poderia guardar as movimentaes da conta internamente e ter um mtodo a mais para mostrar o extrato dessa conta. Em vez de alterar a classe original, o que poderia causar problemas em programas que j esto utilizando essa classe, ns vamos usar herana para criar uma nova classe derivada. Nessa classe, vamos modificar o funcionamento dos mtodos Depositar e Retirar. Para isso, vamos definir mtodos com os mesmos nomes, que vo substituir os mtodos herdados. Vamos tambm criar um novo mtodo, MostrarExtrato, que recebe um objeto TStrings (por exemplo, a propriedade Lines de um componente Memo) e mostra o extrato nessa lista de strings. Crie uma nova unidade independente (File|New, escolha "Unit" e Ok). Salve a unidade como CLASSECONTAEX.PAS. Dentro da unidade coloque o seguinte: unit ClasseContaEx; interface // deve usar a classe j na interface, por isso o // uses ser colocado aqui uses ClasseConta, Classes; //contm TList e TStrings do Delphi type TContaEx = class(TConta) public procedure Depositar(valor: double); procedure Retirar(valor: double); procedure MostrarExtrato(lista: TStrings); private Movimentos: TList; end; Note que a classe tem um campo a mais: Movimentos, que um objeto da classe TList. Esse objeto ser usado para guardar as movimentaes (depsitos e retiradas) feitas na conta. Agora, na seo de implementao, vamos criar uma classe auxiliar para guardar cada movimentao. Declare como abaixo: implementation uses SysUtils; //contm TDateTime type TMovimento = class Valor: double; Data: TDateTime; end; A classe TMovimento tem dois campos. O campo 'Valor' diz qual o valor movimentado. Se negativo, foi uma retirada. Se positivo, um depsito. O campo 'Data' registra a data e hora da movimentao.

Redefinindo Mtodos
Agora vamos implementar o novo mtodo Depositar. Ele vai chamar o mtodo herdado da classe TConta, que altera o saldo, e depois registra a movimentao em 'Movimentos': procedure TContaEx.Depositar(valor:double); var mov: TMovimento; begin

inherited; mov := TMovimento.Create; mov.Valor := valor; mov.Data := Now; //funo que retorna data+hora Movimentos.Add(mov); end; Repare que a primeira coisa dentro do mtodo o comando inherited [herdado]. Esse comando chama o mtodo Depositar que foi herdado (TConta.Depositar) e passa o valor como parmetro. Isso garante que ns estamos acrescentando funcionalidade conta, e no substituindo o que j funcionava. O mtodo herdado nesse caso tem que ser chamado para continuar a atualizar o saldo. O mtodo Retirar bem semelhante, s que ele deve inverter o sinal do valor no objeto 'mov': procedure TContaEx.Retirar(valor:double); var mov: TMovimento; begin inherited; mov := TMovimento.Create; mov.Valor := -valor; //note o sinal "-" (menos) mov.Data := Now; Movimentos.Add(mov); end; Agora, o mtodo MostrarExtrato deve percorrer a lista de movimentaes e gerar descries na lista de strings. Ele vai gerar uma lista de strings da forma: 20/10/95 10:10:13 - Depsito: 1234,00 20/10/95 12:22:53 - Retirada: 500,00 22/10/95 09:07:23 - Depsito: 2100,00 Saldo atual: 2834,00 Implemente esse mtodo como abaixo: procedure TContaEx.MostrarExtrato(lista:TStrings); var k: integer; mov: TMovimento; s: string; begin lista.Clear; for k := 0 to Movimentos.Count - 1 do begin mov := TMovimento(Movimentos[k]); s := FormatDateTime('dd/mm/yyyy hh:mm:ss', mov.Data) + ' - '; if mov.Valor >= 0 then s := s + 'Depsito: ' else s := s + 'Retirada: '; s := s + FormatFloat('0.00',mov.Valor); lista.Add(s); end; end;

Construtores e destrutores
A classe ainda no vai funcionar do jeito que foi definida. Da primeira vez que o mtodo Depositar ou Retirar for chamado, a varivel 'Movimentos:TList' ainda no foi inicializada. Ela deve ser inicializada antes de qualquer operao, criando um objeto 'TList' O ideal inicializar essa varivel quando criado o objeto TContaEx e destruir o objeto TList quando for destrudo o objeto TContaEx. Para isso, existem rotinas especiais chamadas de construtores e destrutores. Um construtor um mtodo executado para criar e inicializar o objeto. No possvel criar um objeto sem construtor. A classe TObject tem um construtor chamado Create, que j usamos para criar vrios tipos de objetos. No nosso caso, vamos redefinir o construtor Create para acrescentar aes adicionais.

Um destrutor um mtodo executado para destruir o objeto. Um destrutor que j usamos o mtodo Free, mas, internamente, Free chama um outro mtodo, Destroy para destruir realmente o objeto. Vamos redefinir o Destroy. Na classe TContaEx, acrescente: type TContaEx = class(TConta) public constructor Create; destructor Destroy; override; ... A diretiva override ser vista em detalhes mais tarde. Na implementao da unidade, acrescente: constructor TContaEx.Create; begin inherited; Movimentos := TList.Create; end; Note que o construtor herdado geralmente deve ser chamado. Para isso, usa-se o comando inherited. Agora acrescente o destrutor: destructor TContaEx.Destroy; begin Movimentos.Free; inherited; end; Note que no destrutor, primeiro a classe libera os objetos que criou (Movimentos, no caso), depois que chama o destrutor herdado. Se essa ordem for invertida, quando o inherited retorna, j no existe mais um objeto para destruir e o campo 'Movimentos' pode no ser mais vlido.

Usando a classe herdada


Para testar a classe herdada, retorne ao formulrio principal. Acrescente na clusula uses o nome da unidade ClasseContaEx: uses ClasseConta, ClasseContaEx; Agora troque todas as referncias a TConta por TContaEx, por exemplo: var ContaAtual: TContaEx; procedure TForm1.btnCriarContaClick(...); ... ContaAtual := TContaEx.Create; procedure TForm1.lstContasClick(...); ... ContaAtual := lstContas.Items.Objects[k] as TContaEx; Vamos tambm acrescentar mais componentes ao formulrio, para mostrar o extrato da conta. Acrescente do lado direito um boto, com Caption = "Mostrar extrato" e nome 'btnExtrato'. Tambm coloque um componente Memo, com o nome de 'memoExtrato'. Quando o usurio clica em "Mostrar extrato", o programa mostra em Dica: voc pode usar [Ctrl+R] para substituir texto. Digite 'TConta' no texto de origem, 'TContaEx' no destino, desmarque "Prompt on Replace" e clique em "Replace All". 'memoExtrato' o extrato da conta atual: procedure TForm1.btnExtratoClick(Sender: TObject); begin ContaAtual.MostrarExtrato(memoExtrato.Lines); end;

Agora salve o projeto como ele est.

Mtodos virtuais
Quando voc chama um mtodo em uma varivel de objeto, como por exemplo: contaAtual.Depositar(valorDigitado), o Delphi deve decidir qual mtodo "Depositar" ser chamado. No programa anterior, existem dois mtodos com o mesmo nome: Depositar da classe TConta e Depositar da classe TContaEx. Uma varivel de objeto, como contaAtual, tem um tipo declarado, mas ela pode conter um objeto de outra classe, por exemplo: var conta: TConta; begin conta := TContaEx.Create; conta.Depositar(valorDigitado); end; No cdigo acima, a varivel 'conta' declarada do tipo-classe TConta, mas ela contm um objeto da classe TContaEx. Qual o mtodo "Depositar", de qual classe, ser chamado? Isso depende da declarao do mtodo. A maioria dos mtodos do Delphi so mtodos estticos. Quando um mtodo esttico, o compilador decide estaticamente, em tempo de compilao, qual mtodo chamar, baseado no tipo da varivel declarada. No caso acima, ser chamado o mtodo 'Depositar' da classe 'TConta'. Um mtodo tambm pode ser virtual ou dinmico. Ambos tm o mesmo efeito: quando o Delphi vai chamar o mtodo, ele verifica dinamicamente, em tempo de execuo, qual a classe do objeto, e chama o mtodo adequado daquela classe. A desvantagem dos mtodos virtuais ou dinmicos que a chamada do mtodo leva um pouco de tempo a mais, mas a vantagem que um programa pode usar uma varivel de objeto, e chamar seus mtodos, sem saber o tipo especfico (classe especfica) do objeto em tempo de execuo. Classes derivadas podem redefinir [override] o mtodo e ter certeza de que ele ser chamado no lugar do mtodo herdado de mesmo nome. Como veremos mais tarde, a VCL do Delphi usa bastante mtodos virtuais.

Declarando um mtodo virtual


Um mtodo deve ser declarado como virtual na classe base. As classes derivadas, quando redefinem esse mtodo, devem declar-lo novamente com o mesmo nome e com o mesmo nmero e tipo de parmetros. Alm disso, a diretiva override deve ser usada na classe derivada para indicar que um mtodo est sendo substitudo. Abra o projeto anterior, TESTECONTAP.DPR. Faa um teste para ver o que ocorre com mtodos estticos. No procedimento do boto "Depositar", modifique o seguinte: procedure TForm1.btnDepositarClick(Sender: TObject); var conta: TConta; begin conta := ContaAtual; conta.Depositar(StrToFloat(editValor.Text)); MostrarSaldo; end; Execute o programa, crie uma conta, deposite e retire valores e mostre o extrato da conta. Os depsitos no aparecem no extrato, porque o que est sendo chamado o mtodo esttico Depositar da classe TConta, que no registra o depsito. Abra a unidade 'ClasseConta'. Dentro da declarao de classe, acrescente a diretiva virtual aos mtodos Depositar, Retirar e Saldo: type TConta = class public

procedure Depositar(valor: double); virtual; procedure Retirar(valor: double); virtual; function Saldo: double; virtual; private FSaldo: double; end; Agora abra a unidade ClasseContaEx. Na declarao da classe, acrescente a diretiva override: type TContaEx = class(TConta) public ... procedure Depositar(valor: double); override; procedure Retirar(valor: double); override; ... end; Agora execute o programa novamente. Ele vai mostrar os extratos, porque o Delphi vai determinar dinamicamente qual a classe do objeto, para saber qual mtodo ser chamado. Em geral, se voc cria um mtodo dentro de uma classe, e imagina que ele ser redefinido [overriden], deve declar-lo como virtual ou dynamic e as classes derivadas devem redeclar-lo com override. A nica diferena entre mtodos virtual e dynamic que os primeiros so mais rpidos, mas gastam ligeiramente mais memria para cada classe. Nota: possvel ter dois mtodos com o mesmo nome, mas diferentes nmero/tipos de parmetros. O nome + parmetro indica a assinatura de um mtodo. Ex : procedure Depositar(valor :Double);Overload; procedure Depositar(conta :TConta);Overload;

Resumo
A programao orientada a objeto (POO) uma forma de programao que tenta construir os programas como colees de classes e objetos, relacionados entre si. Uma classe uma estrutura de dados, definida em tempo de projeto, que contm tambm os procedimentos que manipulam esses dados. Um objeto uma instncia de uma classe, criada em tempo de execuo, de acordo com a estrutura da classe e que pode ser manipulada com os mtodos da classe. A idia da POO criar o mnimo possvel de dependncias de uma parte do programa em relao outra. (As "partes" do programa so as classes e seus objetos). Com isso, uma parte pode ser alterada sem afetar as outras. Num programa orientado a objeto bem construdo, o efeito de uma alterao no cdigo menor e mais previsvel. Em outros tipos de programao, uma alterao no cdigo pode causar problemas, bugs, inconsistncias etc. em vrias partes do programa. Alm disso, a programao orientada a objeto facilita bastante a reutilizao de cdigo. Sem POO, possvel reutilizar cdigo, por exemplo, procedimentos e funes isolados, mas isso tem suas limitaes. Na POO, vrios conceitos so importantes: encapsulamento, herana e polimorfismo. Encapsulamento permite esconder membros de uma classe para evitar que outras classes alterem a estrutura interna do objeto. Por exemplo, um "cliente" da classe TConta no precisa saber como ela guarda as movimentaes da conta e no tem acesso para modificar essas informaes (e falsificar um extrato, por exemplo). Com o encapsulamento, voc garante que o objeto pode alterar sua representao interna sem afetar nenhuma outra classe. Ele s no pode alterar a sua interface, isto , o conjunto de mtodos e dados pblicos (por exemplo, Depositar(valor:double) e Retirar(valor:double)). O encapsulamento ento fora uma separao entre interface e implementao, onde a interface geralmente fixa ou muda pouco enquanto a implementao de uma classe tem total liberdade para mudar.

Herana permite criar uma classe derivada a partir de uma classe base. A classe derivada herda a implementao da base, reutilizando o cdigo que j existe, que j foi testado e aprovado. A classe derivada pode modificar ou estender o funcionamento de mtodos j existentes. Uma classe derivada herda tambm a mesma interface da classe base. Isso quer dizer que em qualquer lugar que possa ser usado um objeto da classe TBase, ele pode ser substitudo por um objeto da classe TDerivada (porque ele suporta os mesmos mtodos). Essa possibilidade, de substituir um objeto por um de outra classe derivada, chamado polimorfismo. Com o polimorfismo, um "cliente" de uma classe (procedimento que usa objetos da classe) pode ser escrito sem se preocupar com a subclasse especfica que ele vai manipular. Futuramente, se novas subclasses so criadas, com capacidades adicionais, o cliente no precisa nem mesmo saber que elas existem, mas pode us-las como objetos da classe TBase. claro que ainda possvel criar programas mal-estruturados, confusos, e instveis com POO. Mas se voc entender corretamente os conceitos e aplicar da forma correta os recursos da linguagem, corre um risco menor de acontecer isso.

12 - Criando Componentes
Pacotes da VCL Hierarquia da VCL Componentes e pacotes Desenhando o controle Criando um controle de edio

Pacotes da VCL
Um pacote [package] um tipo de DLL que contm vrias unidades, e classes de componentes. Quando voc compila um programa, o contedo do pacote pode ser incorporado ao executvel, ou pode continuar separado dentro do arquivo de pacote. Toda a VCL do Delphi dividida em pacotes. Em modo de projeto [design-time], os pacotes so carregados para a memria, e os seus componentes ficam disponveis na paleta de componentes. Durante a execuo do programa, o executvel pode carregar pacotes de tempo-de-execuo [runtime packages] ou pode ser compilado incluindo todo o contedo dos pacotes.

Pacotes design-time
Um pacote de tempo de projeto [design-time] usado pelo ambiente do Delphi durante o projeto do programa. Ao iniciar, o Delphi carrega vrios pacotes design-time, e mostra na paleta de componentes o contedo desses pacotes. Cada componente est contido em um pacote e cada pacote contm um ou mais componentes. Se houver alguns componentes que voc no est usando, voc pode remover alguns pacotes da memria. Para fazer isso, clique em Component|Install Packages. Voc ver uma lista dos pacotes instalados. Cada um tem uma descrio, como "Delphi Database Components" e um nome de arquivo, que voc pode ver ao clicar na descrio.

Pacotes de run-time
Ao compilar um projeto em Delphi, normalmente todas as unidades do Delphi utilizadas so includas no programa executvel. Com isso, um programa fica com um tamanho mnimo de 150 Kb, mas chega at 800 Kb ou mais quando usa os componentes de banco de dados. Desse cdigo, s uma pequena parte (cerca de 60 Kb ou pouco mais) corresponde ao que foi escrito pelo programador. Um pacote de run-time uma DLL que contm unidades do Delphi e seus componentes, que pode ser mantida separadamente do executvel principal. Quando voc compila com a opo de pacotes de run-time, o programa executvel fica bem menor, mas em compensao ele depende dos arquivos de pacote externos para funcionar (como VCL40.BPL, por exemplo).

Vale a pena usar pacotes de run-time principalmente se voc tiver um sistema composto de vrios arquivos EXE separados. Quando todos eles estiverem carregados em memria, vo compartilhar o cdigo dos pacotes. Como um pacote uma DLL, o Windows garante que no haver duas cpias do mesmo pacote em memria. Outra vantagem que, com um executvel menor, para fazer "upgrades" no programa, basta copiar um arquivo de menos de 100 Kb, em vez de levar um executvel com todas as unidades embutidas. Para usar pacotes de run-time, clique em Project|Options e na pgina "Packages". Marque a opo "Build with runtime packages" e recompile o programa. Note a diferena no tamanho do arquivo EXE com e sem essa opo. Se voc usar essa opo, precisa saber quais os pacotes que devem ser distribudos com o seu arquivo EXE. Todos os arquivos de pacote esto no diretrio de sistema do Windows: C:\WINDOWS\SYSTEM (95) ou C:\WINNT\SYSTEM32 (NT). Voc deve instal-los (com o InstallShield por exemplo) no mesmo diretrio no computador de destino. O pacote VCL40.dcp contm os componentes e funes mais usados, exceto os componentes de bancos de dados. Os outros pacotes contm os componentes como indicado na tabela abaixo: VCL40 A maioria dos componentes e unidades do Delphi. VCLDB40 Unidades e componentes de banco de dados (pginas Data Access e Data Controls), exceto DBChart (v. TEEDB40) VCLX40 Componente da pgina Win32: CheckListBox; da pgina Samples: ColorGrid; da pgina System: DdeClientConv, DdeServerConv, DdeClientItem, DdeServerItem, MediaPlayer; da pgina Win3.1 (obsoletos): Outline, TabbedNotebook, FileListBox, DirectoryListBox, DriveComboBox, FilterComboBox. VCLDBX40 Componentes de banco de dados obsoletos: da pgina Win3.1: DBLookupCombo, DBLookupL DblookupList; componente Report (do ReportSmith), que normalmente no aparece em nenhuma pgina. DSS40 Componentes da pgina Decision Cube. INET40 Componentes da pgina Internet: todos exceto QueryTableProducer e DataSetTableProducer (v. abaixo). INETDB40 Componentes da pgina Internet: QueryTableProducer e DataSetTableProducer. QRPT40 Componentes do QuickReport, da pgina QReport. TEE40 Componente da pgina Additional: Chart. TEEDB40 Componente da pgina Data Controls: DBChart. IBEVNT40 Componente da pgina Samples: IBEventCtrl.

Estrutura de arquivos de pacote


Para criar um pacote, voc cria um arquivo fonte de pacote (.DPK). Vamos criar alguns pacotes mais tarde, mas por enquanto vamos abrir um arquivo j existente para ver como sua estrutura. Clique no boto na janela do Delphi. Em Listar arquivos do tipo, selecione "Delphi package source (*.dpk)". Selecione o diretrio de instalao do Delphi e, dentro dele, o diretrio LIB. Abra o arquivo DCLUSR40.dpk. Um arquivo de pacote aparece com duas pginas: "Contains", que lista quais as unidades includas nele e "Requires", que lista quais os pacotes dos quais ele depende. Outra forma de ver o contedo do arquivo no editor: clique na janela do pacote com o boto direito e em View Source. Voc ver algo como: package dclusr40; {$R *.RES} {$ALIGN ON} ... {$DESCRIPTION 'Borland User Components'} {$DESIGNONLY} {$IMPLICITBUILD ON} contains ... requires

vcl40; end. No incio do arquivo, aps o cabealho"package dclusr40;" est a representao em texto das opes de compilao do pacote. A opo {$DESCRIPTION...} a descrio do pacote, um texto que aparece quando ele mostrado no Delphi. E {$DESIGNONLY} indica que esse pacote usado s em modo de projeto [design-time]. Normalmente no preciso editar essas opes diretamente, apenas atravs da caixa de dilogo "Options", chamada a partir da tela do pacote, no boto Options. A clusula contains s est presente se houver alguma unidade dentro do pacote (ns vamos acrescentar algumas mais tarde). Ela lista as unidades que sero includas. A clusula requires declara quais so os pacotes dos quais este pacote depende, no caso, apenas VCL40. Esses pacotes devem estar carregados em memria para poder usar DCLUSR40. Para compilar o pacote, basta usar o boto Compile na janela do pacote. Isso vai gerar o arquivo DCLUSR40.bpl. Para instalar um pacote na paleta de componentes, mostrando todos os componentes que fazem parte dele, voc pode clicar no boto Install.

Hierarquia da VCL
Voc pode criar seu prprio tipo de componente e acrescent-lo paleta de componentes do Delphi para usar em qualquer outro projeto. Essa uma forma de reutilizao de cdigo bem maior, mas mais complicado do que outros recursos do Delphi, como modelos de componentes. Para criar um componente, voc deve criar uma nova classe de componente. Essa classe deve ser baseada no mnimo na classe TComponent do Delphi, que ancestral de todos os componentes. Mas pode ser baseada em outras subclasses. importante entender antes a hierarquia da VCL e quais so as classes envolvidas:

A classe TComponent
Componentes no visuais, como TTable, TTimer e TMainMenu, derivam da classe TComponent. Componentes visuais (controles) so derivados de TComponent tambm, mas indiretamente atravs de TControl. Essa classe define propriedades e mtodos que permitem manipular os sub-componentes de um componente, por exemplo: property Name: string; - o nome do componente property Tag: integer; - um nmero identificador As propriedades ComponentCount e Components permitem percorrer a lista de componentes possudos pelo componente (por exemplo, a lista de componentes de um formulrio). Por exemplo, para chamar um procedimento qualquer para todos os componentes possudos pelo componente 'c': for k := 0 to c.ComponentCount -1 do UmProcedimentoQualquer(c.Components[k]); Um componente, digamos 'c', que possui outro componente, digamos 'd', o dono de d. A propriedade Owner de d faz referncia a 'c'. Ou seja, se d = c.Components[k] para algum elemento 'k', ento d.Owner = c. O mtodo FindComponent permite procurar um componente por nome, por exemplo: comp := c.FindComponent('Nome');

A classe TControl
A classe TControl derivada de TComponent. Todos os componentes visuais so derivados de TControl. Eles tm as propriedades Left, Top, Width e Height que dizem as coordenadas do retngulo que o componente ocupa na tela, em pixels. Essas coordenadas (Left e Top) so sempre relativas ao pai [parent] do controle, que um outro controle que o contm visualmente. Um formulrio um controle que no tem pai. Nesse caso, as coordenadas so relativas tela. A propriedade Parent de um controle faz referncia ao pai dele. A classe TControl tambm define vrias propriedades que so herdadas por todos os controles. Algumas delas so pblicas, como Align, Enabled, Visible, Hint. Mas algumas delas esto encapsuladas (com nvel protected), de forma que no aparecem em qualquer controle. Se uma classe derivada quiser, ela pode simplesmente publicar essas propriedades. So elas: Text e Caption (que fazerm exatamente a mesma coisa), ClientHeight, ClientWidth, Color, Cursor, DragCursor, DragMode, Enabled, Font, ParentColor, ParentFont, ParentColor, ParentShowHint, PopupMenu e ShowHint. Existem vrias outras propriedades, principalmente para uso interno da classe TControl. Existem dois tipos de controles, que correspondem a subclasses de TControl. Controles de janela so controles que internamente possuem uma janela do Windows, que tem uma superfcie de desenho prpria. E um controle

de janela geralmente pode conter outros controles. J os controles grficos no so controlados pelo Windows, e usam a janela de seu controle-pai para desenhar o contedo.

A classe TWinControl
Controles de janela so identificados como janelas internamente pelo Windows. Uma janela uma rea retangular da tela que possui uma superfcie de desenho independente e pode conter outras janelas. Todos os controles de janela so derivados de TWinControl. Um controle de janela pode: - Receber o foco de teclado enquanto a aplicao est executando. Por exemplo, TEdit, TDBEdit, TMemo, TButton so controles de janela, porque eles podem receber o foco e processar teclas. - Pode (geralmente) conter outros controles, ou seja, pode ser o pai de outro controle qualquer [parent]. Por exemplo, um controle TPanel ou TTabControl pode conter outros colocados sobre ele. - Tem uma superfcie de desenho, ou Canvas, independente de outros controles. Isso permite que ele fique "por cima" de qualquer outro controle. - Tem uma ala de janela [window handle], um nmero que identifica internamente a janela do controle. A propriedade Handle permite consultar esse identificador. Para usar algumas funes do Windows, possvel passar nomeControle.Handle quando esperando uma ala de janela (parmetro do tipo HWND).

A classe TGraphicControl
Um controle grfico um controle que no tem uma janela do Windows. Ele desenha a si mesmo em cima da janela do controle pai. Um controle grfico no pode receber o foco de teclado, nem conter outros controles. Os controles TLabel e TImage so controles grficos. Voc pode ver que num formulrio, os controles grficos, comoTLabel ficam sempre por baixo de controles de janela. Controles grficos so mais "leves" em termos de utilizao de memria. Controles grficos so aqueles que no so derivados de TWinControl. Um controle grfico poderia ser derivado diretamente de TControl, mas existe uma classe intermediria mais usada: TGraphicControl, que tem algumas facilidades a mais para desenho sobre a janela. A propriedade Canvas de TGraphicControl contm um objeto de desenho, da classe TCanvas, que permite desenhar qualquer coisa sobre a rea do controle. O mtodo Paint, que virtual, deve ser redefinido nas classes derivadas para desenhar o contedo do controle.

Resumo da Hierarquia
TComponent (todos os componentes) +--- TControl (todos os controles) | +--- TGraphicControl (controles grficos) | +--- TWinControl (controles de janela) +--- outros (componentes no-visuais) Quando voc vai criar uma nova classe de componente, deve escolher qual a classe da qual ela ser derivada, dependendo do tipo de componente a ser criado.

As classes TCustomXXX
Se voc usar o Object Browser (View|Browser) com um projeto qualquer, ver que para cada classe de componente VCL (p.ex. TEdit), existe uma classse ancestral logo acima com a palavra "Custom" (p.ex. TCustomEdit). O objetivo dessas classes intermedirias facilitar a criao de componentes semelhantes, mas que no usam todas as propriedades. Por exemplo, se voc quer criar um novo componente de edio, ele pode ser derivado de TEdit diretamente ou de TCustomEdit. No primeiro caso, ele herda todas as propriedades, mtodos e eventos de TEdit. Pode ser que algumas dessas caractersticas no se apliquem ao seu tipo de componente. Nesse caso, voc ainda pode ocultar propriedades, eventos ou mtodos herdados. J se voc derivar uma classe de TCustomEdit, ela herda as propriedades, eventos e mtodos, mas a maioria deles j est oculta. Nesse caso, voc pode mostrar (tornar pblicas) apenas as propriedades, eventos e mtodos que voc deseja que sejam usadas.

Componentes e pacotes
Cada componente da VCL colocado dentro de um pacote de tempo de projeto [design-time package]. Como vimos antes, voc pode escolher quais pacotes so carregados no ambiente do Delphi em determinado momento, o que vai determinar quais so os componentes disponveis.

Quando voc vai criar e instalar um novo componente no Delphi, deve escolher em qual pacote ele ser colocado ou criar um novo. O Delphi vem com um pacote default chamado DCLUSR40.DPK, com a descrio "Borland Users". Voc pode colocar seus componentes nesse pacote, ou criar um outro para facilitar a separao deles. Se voc criar um outro pacote, fica mais fcil distribuir seus componentes para outros usurios do Delphi.

Criando um controle grfico


Vamos criar um controle grfico, que apenas desenha uma elipse no formulrio. Vamos cham-lo de 'Elipse' (o nome da classe ser 'TElipse'). Um controle grfico as vezes mais simples, porque ele no pode receber o foco de teclado (e no tem eventos de teclado). Esse controle ter uma propriedade 'LarguraBorda', que permite alterar a largura da borda da elipse e ter tambm a propriedade 'Color' padro para alterar a cor da elipse.

Usando o New Component Wizard


A forma mais rpida de criar uma classe componente usar o New Component Wizard do Delphi. Para isso, clique em Component|New Component. Preencha as opes como na figura abaixo: Em "Ancestor Type", voc escolhe qual a classe da VCL que ser a ancestral (classe base) da sua classe. Como vamos criar um controle grfico, escolha "TGraphicControl". Em "Class Name", digite "TElipse". Em "Palette Page", voc pode escolher qual a pgina onde o novo componente ser instalado ou digitar um nome para criar uma nova pgina. Em "Unit file name", voc vai determinar onde a unidade do componente ser criada. O nome de arquivo default ELIPSE.PAS. Mude o diretrio para "D:\CursoDelphi". Voc deve tambm acrescentar esse diretrio embaixo ao "Search path" [caminho de busca] do Delphi: acrescente um ponto e vrgula e mais o diretrio. Quando o pacote do componente for compilado, o Delphi vai procurar as unidades nesse diretrio. Clique em "OK" e a nova unidade ELIPSE.PAS aparecer no editor. Salve a unit 'Elipse.pas'. Note que no preciso criar um projeto para criar uma classe de componente, basta uma unidade.

A classe de componente
O Delphi j declara na unidade Elipse uma classe de componente chamada TElipse. Nada impede que voc inclua mais de uma classe de componente na mesma unidade, e o nome da unidade independente do nome das classes. A classe foi declarada pelo Delphi com: type TElipse = class(TGraphicControl) private { Private declarations } protected { Protected declarations } public { Public declarations } published { Published declarations } end; As quatro divises da classe determinam os nveis de proteo dos campos: private, protected, public e published. As trs primeiras, como j vimos, permitem acesso propria classe apenas (private), s classes derivadas apenas (protected) e a todas as classes (public). O nvel published funciona como public, s que propriedades e eventos declarados nesse nvel aparecem tambm no Object Inspector em tempo de projeto, enquanto os do nvel public s podem ser usados em tempo de execuo.

Desenhando o controle
Como estamos criando um controle grfico, devemos desenhar alguma coisa na tela para que o controle tenha alguma aparncia. A classe TGraphicControl declara um mtodo virtual chamado Paint. Esse mtodo no faz

nada, mas chamado internamente sempre que for necessrio redesenhar o contedo do componente. Ele existe apenas para "segurar o lugar", para que voc defina um mtodo com o mesmo nome que ir realmente desenhar o contedo do componente. Declare o mtodo Paint na parte public da classe: public { Public declarations } procedure Paint; override; A diretiva override necessria porque o mtodo virtual na classe ancestral, TGraphicControl. Agora implemente esse mtodo na parte implementation da unidade: procedure TElipse.Paint; begin { 'Canvas' uma propriedade que d acesso a uma rea de desenho grfica } Canvas.Ellipse(0,0,Width,Height); end; Por enquanto iremos fazer somente isto. Vamos compilar e testar esse componente.

Instalando o componente
Para instalar um componente na paleta, voc deve escolher o pacote onde ele ser instalado ou criar um novo. No caso, vamos escolher o pacote "Borland User Component" (DCLUSR40.DPK). Clique em Component| Install Component: Em Unit file name, aparece automaticamente o nome do arquivo de unidade. Em Search Path, j existe o diretrio que foi colocado. Em Package file name, aparece o nome do pacote DCLUSR40.DPK, que j o default, mas se deseja criar um pacote escolha a pgina "New into page" . Clique em Ok. Agora vai aparecer uma mensagem avisando que o pacote ser compilado (built) e depois instalado. Clique em "Yes". Depois uma mensagem vai indicar que o pacote foi recompilado e o componente TElipse foi acrescentado paleta. Agora vai aparecer uma janela com o contedo do pacote: Deixe-a aberta porque ser usada quando voc quiser recompilar o componente. Clique na pgina Samples e voc ver um novo cone de componente genrico, para o componente Elipse. Clique nesse cone e coloque um componente no formulrio. Ele deve desenhar uma elipse na posio onde foi criado. Voc pode tambm criar vrias cpias desse componente.

Acrescentando propriedades
Vamos permitir ao "usurio" desse componente (ou seja, o programador que usa esse componente em um formulrio, que pode ser voc mesmo) que possa alterar a espessura da linha de contorno da elipse com uma nova propriedade chamada LarguraBorda. Uma propriedade, do ponto de vista do usurio do componente, um campo de dados, que ao ser modificado, altera alguma caracterstica visual do componente. Do ponto de vista do criador do componente, uma propriedade apenas simula um campo de dados. O criador pode definir o que acontece quando a propriedade lida ou modificada. Quando o usurio do componente modifica uma propriedade, pode ser que internamente, o Delphi chame um mtodo (procedimento) para alterar essa propriedade. Ou quando a propriedade lida, o Delphi pode chamar um mtodo (funo) para ler (ou calcular) o seu valor. Vamos criar uma propriedade para tornar isso mais claro. Isso envolve declarar vrias coisas na classe: na parte private, um novo campo de dados, um novo procedimento e na parte published a prpria propriedade: type TElipse = class(TGraphicControl) private { Private declarations } FLarguraBorda: integer; //campo usado pela prop. procedure SetLarguraBorda(larg:integer); //procedimento usado pela propriedade ...

published { Published declarations } property LarguraBorda:integer read FLarguraBorda write SetLarguraBorda; end; A declarao da propriedade diz o tipo de dados (integer) e o que ser feito quando ela lida (read FLarguraBorda) e gravada (write SetLarguraBorda). Nesse caso, ao ler a propriedade, o campo FLarguraBorda ser lido. Ao modificar a propriedade, o procedimento SetLarguraBorda ser executado, por exemplo: Elipse1.LarguraBorda := 2; Esse comando na verdade ser executado internamente como: Elipse1.SetLarguraBorda(2); Voc deve implementar o procedimento SetLarguraBorda, na parte implementation: procedure TElipse.SetLarguraBorda(larg:integer); begin if larg <= 0 then raise Exception.Create( 'Largura deve ser positiva'); FLarguraBorda := larg; Repaint; //redesenha o contedo end; O procedimento verifica se o valor que foi passado ('larg') positivo. Caso contrrio, ele gera uma exceo com o comando raise. Isso impede que essa propriedade possa ter valores invlidos. Caso o valor esteja correto, ele guarda o valor de 'larg' no campo FLarguraBorda e chama o mtodo Repaint que fora o redesenho do componente, chamando o mtodo Paint indiretamente. Agora devemos usar o valor que foi guardado em FLarguraBorda para pintar a elipse. Vamos usar tambm a propriedade Color para definir a cor da elipse. Altere o procedimento Paint: procedure TElipse.Paint; begin Canvas.Pen.Width := FLarguraBorda; Canvas.Brush.Color := Color; Canvas.Ellipse(0,0,Width,Height); end; Antes de testar vamos criar tambm um construtor para inicializar essa propriedade.

Redeclarando uma propriedade


O seu componente no vai mostrar as propriedades e eventos herdados de TGraphicControl, porque a maioria deles so declarados como private na classe TGraphicControl. Para que elas apaream, voc pode redeclar-las como published. Na classe de formulrio, faa as seguintes alteraes: published { Published declarations } property LarguraBorda:integer read FLarguraBorda write SetLarguraBorda; property Align; property Color; property Enabled; property Visible; property OnClick; property OnDblClick; property OnDragDrop;

property OnDragOver; property OnEndDrag; property OnMouseDown; property OnMouseMove; property OnMouseUp; property OnStartDrag; end; Note que os eventos so declarados com property. Veremos o porqu mais tarde.

Criando um construtor
Um dos problemas do componente que o tamanho dele ainda no est correto, porque as propriedades Width e Height so inicializadas com zero. Para poder inicializar estas e outras propriedades (inclusive LarguraBorda), vamos criar um construtor na classe de componente. Dentro da classe, na parte public, declare: ... public { Public declarations } constructor Create(aOwner:TComponent); override; ... O construtor deve seguir o mesmo padro da classe TComponent, por isso deve ter um parmetro do tipo TComponent, que ser o dono desse componente. O dono geralmente um formulrio e controla a alocao de memria do componente: se o dono sair da memria, o componente tambm sai. Vamos implementar o construtor (na parte implementation), inicializando as propriedades necessrias: constructor TElipse.Create(aOwner:TComponent); begin inherited; FLarguraBorda := 1; Width := 100; Height := 100; end;

Recompilando o componente
Para recompilar o componente, voc deve clicar na janela do pacote DCLUSR40.DPK. Se ela estiver fechada, abra novamente o arquivo de pacote no diretrio LIB debaixo do diretrio do Delphi. Na janela de pacote, clique no boto "Compile". O componente dever ser atualizado. Clique no cone dele e coloque em um formulrio para testar o seu funcionamento. Note o que acontece quando voc altera a propriedade LarguraBorda. Se voc tentar entrar com um valor negativo, o componente mostra a mensagem de erro. Quando voc aumenta, a linha de contorno fica mais espessa. Quando voc altera a propriedade Color, a cor do componente ser alterada. Para salvar o que foi feito, salve a unidade, ELIPSE.PAS e o arquivo de pacote, DCLUSR40.DPK. No precisa salvar o projeto de teste.

Criando um controle de edio


Agora vamos criar um outro tipo de componente, um controle de edio baseado na classe TMemo, que permite apenas edio de valores numricos, alinhados direita.

Criando a classe de componente


Para alinhar nmeros direita, precisamos criar o componente baseado em TMemo e no em TEdit, porque s o TMemo tem capacidade de alinhar direita.

Vamos usar o New Component Wizard, como antes. Em "Ancestor Type", escolha "TCustomMemo". Em "Class Name", digite "TEditNum". Em "Palette Page", deixe o default, "Samples" e em Unit file name, altere o o diretrio para "C:\CursoDelphi", deixando o nome de arquivo EDITNUM.PAS. Clique no boto 'Install' para instalar o componente. Em seguida ir aparecer uma janela onde necessrio especificar se vai utilizar o arquivo de pacote existente ou se iremos criar outro arquivo de pacote. Clique na pgina "Into new package" e coloque as opes de acordo com a figura abaixo: Clique no boto "OK" , em seguida pergunta se voc deseja isntalar o componente , confirme clicando "OK". Desta forma ele cria a unit 'EditNum.pas', instala o componente e adiciona-o ao arquivo de pacote criado. No feche a janela que aparece o arquivo de pacote , pois, iremos utiliz-la para compilar a unit do componente.

Acrescentando propriedades
Vamos criar uma propriedade numrica chamada 'Valor', que permite ler ou alterar o valor do componente, sem precisar de uma converso de numrico para string ou vice-versa. Para isso, declare alguns itens dentro da classe 'TEditNum': type TEditNum = class(TCustomMemo) private { Private declarations } property Text; procedure SetValor(v: double); function GetValor: double; ... published { Published declarations } property Valor: double read GetValor write SetValor; end; A declarao property Text; dentro da parte private da classe tem por objetivo esconder a propriedade 'Text' herdada, de forma que no seja possvel alterar diretamente o texto, mas apenas com a propriedade 'Valor'. A propriedade Valor foi declarada com read GetValor write SetValor, ou seja, ao ser lida, ela invoca a funo GetValor e ao ser modificada, o procedimento SetValor ser chamado. Vamos implementar esses dois mtodos: procedure TEditNum.SetValor(v: Double); begin Text := FloatToStr(v); end; function TEditNum.GetValor: double; begin try // por segurana Result := StrToFloat(Text); except on EConvertError do Result := 0; end; end; Simplesmente esses mtodos convertem de/para string o valor numrico e gravam/lem o valor da propriedade Text, que foi herdada de TCustomMemo.

Para testar, voc pode instalar o componente no pacote DCLUSR30.DPK e compilar o pacote. (Clique em Component|Install Component, selecione o nome do pacote na lista em "Package file name", clique Ok e confirme a recompilao do pacote). Ao colocar um componente EditNum em um formulrio, voc pode alterar a propriedade Valor e ver o resultado dessa alterao. Note que o Delphi no permite entrar com valores no-numricos. Mas o componente ainda no valida a digitao de teclas.

Inicializando o componente
Vamos criar um construtor nessa classe para inicializar os campos do componente. Declare o construtor como pblico: public { Public declarations } constructor Create(aOwner: TComponent); override; Agora implemente o construtor na parte implementation. Ele vai inicializar o texto, o tamanho do componente e algumas outras propriedades, como Alignment para alinhar o texto esquerda: constructor TEditNum.Create(aOwner: TComponent); begin inherited; // importante! Text := '0'; Alignment := taRightJustify; //alinhar direita Width := 100; // um tamanho default... Height := 25; // ...mais razovel WordWrap := False; //s mostra uma linha de texto WantReturns := False; // no aceita [Enter] end;

Tratando as teclas
Quando o usurio digita uma tecla dentro de um componente, o Windows avisa esse componente, que pode fazer alguma coisa com a tecla, ou simplesmente aceitar a digitao da tecla. Normalmente o componente aceita a tecla e chama um dos eventos de teclado associados a ele (OnKeyDown, OnKeyUp ou OnKeyPress). Mas o que acontece internamente no componente um pouco diferente. A classe ancestral (TCustomMemo) chama o mtodo KeyPress internamente (um mtodo virtual) para processar as teclas. Esse mtodo que faz o processamento default. Ns vamos redefinir esse mtodo (com override) para tratar as teclas antes mesmo de elas chegarem ao procedimento do evento OnKeyPress. Declare o novo mtodo na parte protected da classe (acessvel a esta classe e classes derivadas): protected { Protected declarations } procedure KeyPress(var Tecla: Char); override; Esse mtodo s vai permitir valores numricos, a tecla [Backspace] ou uma vrgula. Implemente o mtodo da seguinte forma: procedure TEditNum.KeyPress(var tecla: Char); begin if not (tecla in ['0'..'9',#8,',']) then tecla := #0; if (tecla = ',') and (pos(',',Text) > 0) then tecla := #0; inherited; end; No primeiro if, as teclas que no estiverem no conjunto ['0'..'9', #8 (backspace), ',' (vrgula)] so eliminadas. No segundo if, feita uma verificao para evitar digitar uma segunda vrgula. Compile novamente o pacote e teste o componente em tempo de execuo. Agora ele edita os valores alinhados direita e no permite entrada de valores no-numricos.

O tipo procedure e variveis de evento


Um evento como um mtodo "ao contrrio": no caso de um mtodo, um formulrio chama o componente. No caso de um evento, o componente que chama um mtodo do formulrio (procedimento de evento) atravs do evento.

Eventos so baseados em variveis do tipo procedure. Uma varivel do tipo procedure (ou function) em Delphi uma varivel que contm uma referncia a um procedimento e permite cham-lo indiretamente. Por exemplo, no cdigo a seguir: var Proc: procedure (x:integer); A varivel 'Proc' pode referenciar um procedimento qualquer, desde que este aceite um parmetro do tipo integer. Por exemplo: procedure ABC(a:integer); begin ... end; procedure DEF(y:integer); begin ... end; Em determinado ponto do programa, valido atribuir um valor varivel 'Proc', que seja o nome de um dos procedimentos acima. E depois o procedimento vai ser chamado a partir dessa varivel, por exemplo: if x = 1 then Proc := ABC else Proc := DEF; ... ... Proc(N); //vai chamar ABC ou DEF, dependendo do // valor de 'Proc' Uma varivel de evento uma varivel dentro de um componente usada exatamente para essa finalidade. Ela contm uma referncia a um procedimento do formulrio e usada para chamar esse procedimento indiretamente. O componente no precisa saber o nome exato do procedimento, mas apenas qual o nome da sua prpria varivel. Um evento na verdade uma propriedade, cujo valor do tipo procedure. Quando voc liga um procedimento a um evento, est atribuindo um valor quela propriedade.

Criando um evento
Vamos criar um evento chamado 'OnErroTecla', que ser executando sempre que o usurio digitar uma tecla no-numrica. O prefixo 'on' [ao] em nomes de evento opcional: ele usado s como um padro do Delphi: (ao acontecer isso, execute isso). Primeiro voc deve declarar o tipo de evento, para facilitar. O tipo de evento um tipo procedure, que declara quais os parmetros do evento. Acrescente a declarao antes da classe do componente: type TErroTecla = procedure(Sender: TObject; Tecla: char) of object; TEditNum = class(TCustomMemo) private ... A declarao of object declara que variveis desse tipo (TErroTecla) podem fazer referncia a um mtodo de uma classe, juntamente com o objeto associado. Para um evento, isso obrigatrio: a varivel de evento referencia tanto o formulrio que trata o evento quanto o mtodo do formulrio que ser chamado. Agora, dentro da classe, acrescente uma declaraes na parte private, para a varivel de evento e outra na parte published, para a propriedade de evento: TEditNum = class(TCustomMemo) private { Private declarations } FOnErroTecla: TErroTecla; ...

published ... property OnErroTecla: TErroTecla read FOnErroTecla write FOnErroTecla; Note que a propriedade l e grava diretamente no campo 'FOnErroTecla'. Dentro da classe de componente vamos usar esse campo para acionar o evento. Retorne ao procedimento KeyPress criado anteriormente e acrescente algumas linhas: begin if not (tecla in ['0'..'9',#8,',']) then begin if assigned(FOnErroTecla) then FOnErroTecla(Self,tecla); tecla := #0; end; ... Note que o procedimento passa dois argumentos: 'Self' o prprio objeto de componente e ser passado para o argumento 'Sender'. Isso segue o padro do Delphi, que permite ao receptor do procedimento verificar qual foi o componente que provocou o evento. O outro parmetro a tecla invlida. Compile novamente o pacote. Quando voc coloca esse componente no formulrio e clica na pgina de eventos, ver um evento 'OnErroTecla'. Para test-lo, crie um procedimento de evento com o seguinte: procedure TForm1.EditNum1ErroTecla(Sender: TObject; Tecla: Char); begin ShowMessage('Tecla invlida:' + tecla); end;

Apndice A - DLLs
Criando e utilizando uma DLL Usando strings de tamnho varivel com DLLs Criando uma unidade de importao A API do Windows Procura de DLLs

Criando e utilizando uma DLL


No Windows, uma DLL um arquivo executvel, contendo uma biblioteca de procedimentos e funes compilados, mas separada fisicamente do executvel principal. Ela nunca executada diretamente, mas sempre chamada a partir de programas (EXEs) ou outras DLLs. Uma DLL pode ser compartilhada por vrios programas, economizando espao em disco. O Windows sempre mantm apenas uma cpia da DLL em memria. O Delphi permite criar DLLs, que podem ser utilizadas por um executvel feito em Delphi ou mesmo em outra linguagem. Um executvel feito em Delphi pode utilizar uma DLL feita em Delphi ou em outra linguagem. Quando voc cria a DLL, voc define quais as funes e procedimentos que so exportados, isto , permitem acesso a outros programas. O prprio Windows fornece toda a sua interface de programao diretamente atravs de trs DLLs principais: KERNEL32.DLL, que contm rotinas de gerenciamento de memria e disco, USER32.DLL, com rotinas de gerenciamento de janela e GDI32.DLL (Graphics Device Interface) para rotinas de desenho grfico. Voc normalmente no precisa chamar essas rotinas, pois a VCL do Delphi j faz isso automaticamente.

O conjunto das rotinas fornecidas pelo Windows para uso dos programas chamado de API (Application Programming Interface=Interface para Programao de Aplicaes). Todas as rotinas do Windows esto disponveis no Delphi atravs da unidade Windows.

Criando uma DLL


Para criar um novo projeto, que gera uma DLL, clique em File|New..., selecione o cone e clique Ok. Note que vai aparecer o arquivo de projeto, contendo uma linha inicial: library Project1; A palavra library em vez de program diz que ser gerada uma DLL como resultado do projeto. Agora, crie um novo formulrio com File|New Form. Altere o Caption para "Fornea a Senha" e Name para "FormSenha". Altere tambm a propriedade Position para 'poScreenCenter'. Coloque um controle de edio e dois botes, como na figura: Para o componente Edit, altere o nome para 'editSenha' e a propriedade PasswordChar para o valor "*". Essa propriedade faz com que o controle mostre asteriscos (poderia ser qualquer outro caractere) durante a digitao, mas armazene internamente o valor digitado. Isso til para entrada de senhas. Para o boto Ok, altere Default para True, ModalResult para 'mrOk' e Name para 'btnOk'. Para o boto Cancelar, altere Cancel para True, ModalResult = 'mrCancel' e Name para 'btnCancelar'. Vamos criar agora uma funo que ser exportada pela DLL. Uma funo exportada aquela que pode ser usada fora da DLL. Essa funo ir criar dinamicamente o formulrio de senha e mostr-lo na tela. Na unidade do formulrio, coloque o seguinte na parte interface (antes ou depois da definio da classe do formulrio): function ObtemSenha(senha:ShortString):boolean; export; Esse apenas o cabealho da funo, declarando seus parmetros e tipos de dados. A palavra export depois do cabealho determina que a funo exportvel, ou seja, ela pode ser exportada pela DLL. O tipo ShortString o mesmo que string[255], uma string de tamanho fixo, com 255 caracteres. Depois veremos o porqu de usar esse tipo Agora, na parte implementation, defina o corpo da funo (note que o cabealho agora no tem export): function ObtemSenha(senha:ShortString):boolean; begin FormSenha := TFormSenha.Create(Application); with FormSenha do if ShowModal = mrOk then {usurio clicou Ok} Result := (senha = editSenha.Text) else Result := False; FormSenha.Free; end; A funo cria dinamicamente o formulrio 'FormSenha' (usando Create) e depois chama o mtodo ShowModal. Se este mtodo retorna 'mrOk', isso indica que o usurio clicou OK. Nesse caso a funo compara a senha fornecida com a senha digitada pelo usurio e coloca o resultado dessa comparao na varivel Result, que o valor retornado pela funo. Mas se o usurio clicou Cancelar ou fechou o formulrio, a funo retorna False. Essa funo at agora somente exportvel, o que significa que o Delphi gera cdigo de mquina que torna ela compatvel com uma chamada de um programa externo. Para que essa funo seja exportada, ou seja, que a DLL compilada inclua informao sobre a funo, e outros programas possam utiliz-la, devemos alterar o arquivo de projeto e colocar uma declarao de exportao. No arquivo de projeto, acrescente uma declarao, logo depois da lista do uses e antes do begin..end: library Project1; uses ... exports ObtemSenha; begin end. Agora salve o projeto como VSENHA.PAS e VERSENHA.DPR. Para gerar a DLL compilada, use o menu Project|Compile. Se voc tentar executar a DLL, o Delphi tambm vai compil-la, mas vai mostrar uma mensagem dizendo que impossvel executar uma DLL. O resultado final do projeto ser um arquivo chamado VERSENHA.DLL.

Utilizando uma DLL


Agora salve o grupo de projeto da DLL como 'gpDLL' e crie um novo projeto para um programa executvel que usar a nossa DLL. Salve esse projeto como USENHA.PAS e USASENHA.DPR. No formulrio coloque o seguinte: Chame o controle de edio de 'editSenha' e o boto de 'btnTeste'. No cdigo do boto digite o seguinte: begin if ObtemSenha(editSenhaTeste.Text) then Caption := 'Correto!' else Caption := 'Incorreto!'; end; Para que isso funcione preciso declarar a funo 'ObtemSenha' nessa unidade. A declarao informa, alm dos parmetros, qual a DLL onde ela se encontra. Logo acima, depois do implementation, digite: function ObtemSenha(senha:ShortString):boolean; external 'VERSENHA.DLL'; Depois de external informado o nome da DLL. Note que, normalmente, o Object Pascal no uma linguagem case-sensitive [que diferencia maisculas e minsculas]. Mas nomes de rotinas de DLL so case-sensitive. No vai funcionar se voc declarar 'obtemsenha' ou 'OBTEMSENHA', por exemplo. Execute o programa. Para testar, digite uma senha e clique em "Testar". Isso vai chamar o outro formulrio que est dentro da DLL. Se voc digitar a mesma senha e clicar em Ok, o ttulo do formulrio vai mudar para "Correto!", seno vai mudar para "Incorreto!". Nota: se voc no colocar a extenso de arquivo ".DLL" na diretiva external, o programa vai funcionar no Windows 95, mas no no Windows NT.

Usando strings de tamanho varivel com DLLs


Uma string de tamanho fixo (ShortString = string[255]) foi usada para passar parmetros para a DLL. Se voc for usar strings de tamanho varivel (string, sem decl.de tamanho) em parmetros de procedimentos/funes exportadas ou funes que retornam strings, voc precisa fazer os trs passos abaixo: - No arquivo de projeto da DLL, acrescente a unidade ShareMem do Delphi na clusula uses, antes da primeira unidade, por exemplo: uses ShareMem, SysUtils, Classes,...; - No arquivo de projeto do seu EXE, faa o mesmo. - Copie BORLNDMM.DLL, do diretrio do Delphi e instale com seu programa. Note que programas em outras linguagens que utilizarem a DLL no entendem o formato de strings variveis do Delphi. Nesse caso, se a DLL vai ser usada em outras linguagens, use PChar para passar parmetros que contm seqncias de caracteres.

Criando uma unidade de importao


Uma unidade de importao [import unit] uma unidade contendo exclusivamente declaraes de procedimentos e funes de DLLs. No caso dessa DLL que foi criada, suponhamos, para exemplificar, que existe mais um procedimento chamado DefinirTamanhoSenha. A unidade de importao poderia conter o seguinte: unit ImpSenha; interface function ObtemSenha(senha:ShortString):boolean; procedure DefinirTamanhoSenha(tam:integer); implementation // repare que ao declarar a funo de novo, no // preciso declarar os parmetros

function ObtemSenha; external 'VERSENHA.DLL'; procedure DefinirTamanhoSenha; external 'VERSENHA.DLL'; end. Outros procedimentos e funes exportados pela DLL poderiam ser declarados tambm nessa unit.

A API do Windows
O sistema operacional Windows tem vrias rotinas disponveis para uso dos programas Windows. Essas funes e procedimentos esto dentro de algumas DLLs do Windows, como USER32.DLL, GDI32.DLL e KERNEL32.DLL e outras. O conjunto de rotinas do Windows chamado de API: Application Programming Interface (interface de programao de aplicaes) do Windows. A VCL do Delphi utiliza internamente a API do Windows para: criar e desenhar os componentes, tratar eventos de componentes etc. Normalmente voc no precisa chamar as funes de API diretamente. Mas s vezes voc vai precisar chamar alguma rotina de API para fazer alguma coisa que no tratada pela VCL. Por exemplo, para consultar o nome do usurio de rede atual, voc pode usar a funo WNetGetUser, de USER32.DLL. Para chamar essa funo, no preciso declar-la no programa. O Delphi tem a unidade "Windows", uma unidade de importao com declaraes para todas as funes do Windows. Basta usar essa unidade e voc ter acesso a qualquer uma dessas funes. Crie um novo projeto. No formulrio, altere o Caption para "Consultar usurio". Coloque um boto "Consultar", com nome 'btnConsultar' e um componente Label ao lado, com o nome 'lblUsuario'. Quando for clicado o boto "Consultar", vamos chamar WNetGetUser e mostrar o nome do usurio atual. No evento OnClick do boto, faa: procedure TForm1.btnConsultarClick(Sender:TObject); var vet: array [0..30] of char; n: integer; begin n := sizeof(vet); WNetGetUser(nil,vet,n); lblUsuario.Caption := StrPas(vet); end; Note que uma unidade de formulrio j inclui "uses Windows" no topo da interface. A funo sizeof no Object Pascal calcula o tamanho de uma varivel ou tipo de dados. A funo WNetGetUser tem trs parmetros: um "nome de dispositivo" ou "nome da rede" e geralmente no necessrio: voc pode colocar nil. Note que o segundo parmetro do tipo PChar, que um ponteiro de caracteres. A funo vai usar esse ponteiro para acessar a rea de memria com os caracteres e preencher com a informao. Note que o argumento passado um vetor de caracteres, cuja primeira posio zero. Esse vetor compatvel com o tipo PChar. O terceiro parmetro deve indicar o tamanho desse vetor. Note que para converter o vetor de voltar para o formato string, usa-se a funo StrPas. Execute o programa e clique no boto "Consultar". O nome de usurio atual na rede deve aparecer. Salve como 'UsuarioRede.pas' e 'UsuarioRedeP.dpr'.

Procura de DLLs
Quando um programa executvel (ou uma DLL) chama uma DLL, o Windows procura a DLL nos seguintes diretrios at conseguir encontr-la: - Diretrio atual - Diretrio do Windows (C:\WINDOWS no Windows 95, ou C:\WINNT no Windows NT). - Diretrio SYSTEM do Windows (C:\WINDOWS\SYSTEM ou C:\WINNT\SYSTEM32). - Mesmo diretrio do executvel. Para instalar uma DLL no sistema do usurio, voc pode usar o InstallSHIELD para criar um instalador contendo os arquivos de DLL. Nota: Voc pode ver o arquivo fonte da unidade Windows, e de outras unidades de importao, no subdiretrio SOURCE\RTL\WIN do diretrio de instalao do Delphi.

Depurando uma DLL


Num programa executvel, voc pode facilmente colocar um breakpoint [ponto de parada] e acompanhar a execuo do programa passo-a-passo, consultando valores de variveis duranta a execuo. Voc pode fazer o mesmo em uma DLL. Para fazer depurao com uma DLL, voc deve especificar um programa EXE que ser executado e chamar alguma funo da DLL. Para isso, abra o projeto da DLL, VERSENHA.DPR. Clique em Run|Parameters... e digite em "Host application" o nome do programa executvel: USASENHA.EXE. Para testar, coloque um ponto de parada dentro da funo ObterTexto, na primeira linha aps o begin. Agora voc pode clicar no boto (Run) para executar o programa. Quando voc clicar no boto "Testar" no formulrio, a execuo vai parar na linha do breakpoint. Voc pode teclar [F8[ para acompanhar a execuo passo-a-passo, posicionar o cursor sobre uma varivel para ver o seu valor etc.

Apndice B - Criando Help


Conceitos Caractersticas de tpicos Criando um arquivo de ajuda Compilando a ajuda Usando o Help no Delphi Outros recursos

Conceitos
Um arquivo de ajuda [help] um arquivo contendo texto e imagens que pode ser lido pelo usurio com o recurso de hipertexto, isto , ao encontrar uma palavra sublinhada, ele pode clicar nela para ver outra parte do help. Um arquivo de ajuda geralmente tem a extenso HLP e mostrado pelo programa WINHLP32.EXE (ou Winhelp), um aplicativo que faz parte da instalao padro do Windows. O usurio pode executar o Winhelp diretamente, mas geralmente o help chamado a partir do programa. Arquivos de ajuda so divididos em tpicos. Cada tpico aparece como uma "pgina" e pode conter qualquer contedo de texto. Por exemplo, num programa que tem um menu, com vrios formulrios chamados a partir dele, poderia haver um tpico que explica o menu principal e um tpico para cada um dos outros formulrios. Geralmente cada tpico tem um ttulo para descrever seu contedo.

Caractersticas de tpicos
Cada tpico tem um identificador [topic ID] que no visto pelo usurio, mas identifica internamente o tpico para o sistema de help. Cada identificador tem opcionalmente um nmero de contexto atribudo a ele, que pode ser usado dentro do programa. Um dos tpicos do arquivo o tpico de contedo, o primeiro que o usurio v ao abrir o arquivo de help. Geralmente a partir dele, existem links de hipertexto para outros tpicos. Um tpico geralmente tem um ttulo, que o identifica em uma lista de tpicos do Winhelp. Opcionalmente ele tem palavras-chave, que podem ser usadas para procurar texto no tpico. Voc pode tambm criar vrios tpicos ligados em seqncia: a janela do Winhelp mostra botes [<<] e [>>], que permitem ver o anterior e prximo da seqncia. Nesse caso, cada tpico deve ter um identificador de seqncia mais um nmero de seqncia. Entre os tpicos podem ser criados links, que permitem chegar a um tpico a partir de outro. Um salto [jump] o um link onde uma palavra clicada abre o contedo de outro tpico na janela do Winhelp, substituindo o anterior. Um pop-up um link no qual, ao clicar na palavra, o tpico aparece em uma pequena janela retangular.

Ajuda sensvel ao contexto


Voc pode suportar um arquivo de help de vrias formas. A mais fcil colocar um comando "Ajuda" no seu programa que abre o tpico de contedo do seu help. Mas melhor para o usurio a ajuda sensvel ao contexto [context-sensitive help], ou seja, dependendo do local do programa (formulrio, controle) onde ele estiver posicionado, ao chamar o help, ser apresentado um tpico especfico. No formulrio de produtos, por exemplo, deve ser mostrado o tpico que explica o formulrio de produtos.

Criando um arquivo de ajuda


Para criar um arquivo de ajuda, voc deve escrever todo o texto em um arquivo no formato RTF (Rich Text Format) em qualquer editor de texto que suporta RTFs. Um arquivo RTF pode conter vrios tpicos, que so separados por quebras de pgina. Esses arquivos sero os arquivos-fonte do help. Para os nossos exemplos, usaremos o Microsoft Word, verso 97 para editar os arquivos. (Note que alguns comandos podem ser diferentes em outras verses do Word e completamente diferentes em outros produtos). Alm dos arquivos-fonte RTF, preciso ter um arquivo de projeto de ajuda [help project file], um arquivo .HPJ que descreve quais so os arquivos-fontes usados, qual o tpico de contedo, quais so os nmeros de contexto de cada tpico e algumas opes globais. Esse arquivo pode ser criado manualmente ou usando o Microsoft Help Workshop (HCRTF.EXE), um programa que instalado com o Delphi, no subdiretrio HELP\TOOLS. Depois voc deve compilar seu arquivo HPJ e os arquivos-fonte (*.RTF) com o HCW. Isso vai gerar um arquivo HLP, com o mesmo nome do projeto de ajuda (e.g. SISTEMA.HPJ => SISTEMA.HLP).

Editando arquivos RTF


Vamos criar um arquivo de ajuda para o projeto Almoxarifado.DPR. Ele ter trs tpicos: um para o menu principal, outro para o cadastro de fabricantes e outro para o cadastro de produtos. Execute o Word e ele vai criar um novo documento. Nota: o Wordpad do Windows 95/NT4 edita RTFs, mas ele no tem os recursos necesrios para criar arquivos de Help (como notas de rodap). Digite o texto do primeiro tpico como no exemplo abaixo: Menu Principal : Este o menu principal do programa. Aqui voc pode escolher uma das seguintes opes: Produtos - abre o cadastro de produtos. Fornecedor - abre o cadastro de fornecedores. Destaque o ttulo do tpico com uma fonte diferente, como preferir. Agora para separar um tpico de outro, voc deve fazer uma quebra de pgina (explcita) no final do texto. A tecla especfica do Word para colocar uma quebra de pgina explcita [Ctrl+Enter]. Digite os dois prximos tpicos, com uma quebra de pgina entre eles e uma quebra de pgina aps o segundo tpico: Cadastro de Fornecedores Aqui voc pode cadastrar fornecedores etc. Cadastro de Produtos Aqui voc pode cadastrar produtos etc.

Identificando os tpicos
Para cada tpico, voc deve definir no mnimo o identificador que ser usado internamente. recomendvel tambm definir um ttulo (o Winhelp no deduz o ttulo automaticamente) que aparece em algumas situaes. Opcionalmente voc pode colocar palavras-chave e identificadores de seqncia. Cada um desses itens representado por uma nota de rodap no arquivo RTF. O caractere da nota de rodap determina qual item de informao: # - identificador $ - ttulo K - palavras-chave + - nome & nmero de seqncia Coloque o cursor logo antes do ttulo Menu Principal e clique em Inserir|Notas no menu do Word. Clique em "Personalizada" e digite o caractere #. Clique Ok. Agora, no texto da nota, digite MENU_PRINCIPAL. Esse ser o identificador do tpico. Note que o identificador no pode conter espaos. No tecle Enter aps o texto da nota. Clique novamente ao lado do ttulo e em Inserir|Notas. Clique em "Personalizada", digite $ e tecle Ok. A nota "$" identifica o ttulo do tpico. Digite "Menu Principal".

Agora vamos criar palavras-chave. Clique novamente ao lado do ttulo e insira uma nota com o caractere K (note bem, maisculo). Digite o texto: menu;principal;fornecedor;produtos. At agora voc tem trs notas, que devem aparecem como o seguinte: # MENU_PRINCIPAL $ Menu Principal K menu;principal;fornecedor;produtos Faa o mesmo para os outros tpicos. Para o "Cadastro de Fornecedores", coloque: # CAD_FORNEC $ Cadastro de Fornecedores K fornecedores Para o "Cadastro de Produtos", crie trs notas como: # CAD_PROD $ Cadastro de Produtos K produtos

Criando links entre tpicos


Voltando ao texto do tpico principal, vamos criar links para os outros tpicos. Mas antes de fazer isso, vamos ativar uma opo do editor para exibir o texto oculto. Texto oculto normalmente no aparece na tela, mas voc deve editar esse texto para poder criar um link. No Word 97, o comando para mostrar texto oculto : clique em Ferramentas|Opes, na pgina Exibir, abaixo de "Caracteres no- imprimveis", marque a opo "Texto oculto". Para criar um link com outro tpico, voc deve selecionar a palavra desejada, e marc-la com duplo sublinhado e logo aps voc deve colocar o identificador do tpico desejado, com texto oculto. Vamos fazer, no tpico principal, links para os outros tpicos. Agora volte ao texto do menu principal. Na frase "Produtos - abre o cadastro de produtos.", selecione a primeira palavra Produtos e clique em Formatar|Fonte. Em "Sublinhado", escolha "Duplo" e clique Ok. Agora coloque o cursor imediatamente aps a palavra Produtos, sem deixar espaos. Clique em Formatar|Fonte e em "Sublinhado", escolha "(nenhum)". Marque a opo "Oculto" e clique Ok. Agora digite CAD_PROD (o identificador do tpico "Cadastro de Produtos". O texto deve aparecer da seguinte forma: ProdutosCAD_PROD - abre o cadastro de produtos. Faa o mesmo na linha de baixo: selecione a palavra "Fornecedores" e formate-a com texto duplo sublinhado. Aps a palavra, formate o texto sem sublinhado e oculto e digite CAD_FORNEC.

Salvando o arquivo
Agora vamos salvar o arquivo, com o cuidado de salvar no formato RTF. Clique em Arquivo|Salvar. Na lista "Salvar como tipo", escolha "Rich Text Format (*.rtf)". Salve com o nome de PROD.RTF, no diretrio dos arquivos do curso. Depois pode fechar o Microsoft Word. Vamos definir tambm qual o tpico de contedo, que aparece no incio da execuo. Para isso, clique no boto Options, e em Default Topic escreva "MENU_PRINCIPAL", exatamente como est digitado dento do arquivo RTF, na nota # do tpico principal. Em Help Title digite "Exemplo de Help", o ttulo que vai aparecer no arquivo. Clique Ok.

Mapeando os nmeros de contexto


Vamos definir para cada tpico o nmero de contexto correspondente. Isso feito clicando no boto Map. Primeiro clique em Add, digite Topic ID: MENU_PRINCIPAL e Mapped numeric value: 1 e clique Ok. Voc ver na lista um item 'MENU_PRINCIPAL=1'. Depois faa o mesmo para CAD_FORNEC (valor = 2) e CAD_PROD (valor = 3) e clique Ok na caixa de dilogo "Map".

Compilando
Agora vamos compilar o help. Clique File|Compile ou no boto correspondente. O Help Workshop vai abrir uma janela separada de compilao e mostrar os resultados, como possveis mensagens de erro. Se estiver tudo Ok, ele vai gerar um arquivo chamado PROD.HLP. Para voltar janela do projeto, clique em Window|1 Prod.hpj.

Testando o Help
Para testar o arquivo de help, clique em File|Run Winhelp. Marque a opo "A double-clicked file icon" e clique no boto View Help. O tpico de contedo deve aparecer na tela com links para os outros tpicos. Depois de clicar num link, voc pode clicar em "Voltar" para voltar atrs.

Se voc clicar em "Contedo", volta para o tpico principal. Se voc clicar em "ndice", ver as palavras-chave e, ao escolher uma, ver os tpicos que contm essa palavra.

Compilando a ajuda
Para criar a ajuda, voc deve executar o "Microsoft Help Workshop", o programa HCRTF.EXE no subdiretrio HELP\TOOLS do Delphi. Para criar um novo projeto de help, clique em File|New, escolha "Help Project" e clique Ok. Selecione o diretrio do curso e digite o nome PROD.HPJ para o arquivo. Voc deve escolher quais arquivos-fonte RTF sero includos. Clique em Files, depois clique no boto Add e escolha o arquivo PROD.RTF criado anteriormente. Depois clique OK. Vamos definir tambm qual o tpico de contedo, que aparece no incio da execuo. Para isso, clique no boto Options, e em Default Topic escreva "MENU_PRINCIPAL", exatamente como est digitado dento do arquivo RTF, na nota # do tpico principal. Em Help Title digite "Exemplo de Help", o ttulo que vai aparecer no arquivo. Clique Ok.

Mapeando os nmeros de contexto


Vamos definir para cada tpico o nmero de contexto correspondente. Isso feito clicando no boto Map. Primeiro clique em Add, digite Topic ID: MENU_PRINCIPAL e Mapped numeric value: 1 e clique Ok. Voc ver na lista um item 'MENU_PRINCIPAL=1'. Depois faa o mesmo para CAD_FORNEC (valor = 2) e CAD_PROD (valor = 3) e clique Ok na caixa de dilogo "Map".

Compilando
Agora vamos compilar o help. Clique File|Compile ou no boto correspondente. O Help Workshop vai abrir uma janela separada de compilao e mostrar os resultados, como possveis mensagens de erro. Se estiver tudo Ok, ele vai gerar um arquivo chamado PROD.HLP. Para voltar janela do projeto, clique em Window|1 Prod.hpj.

Testando o Help
Para testar o arquivo de help, clique em File|Run Winhelp. Marque a opo "A double-clicked file icon" e clique no boto View Help. O tpico de contedo deve aparecer na tela com links para os outros tpicos. Depois de clicar num link, voc pode clicar em "Voltar" para voltar atrs. Se voc clicar em "Contedo", volta para o tpico principal. Se voc clicar em "ndice", ver as palavras-chave e, ao escolher uma, ver os tpicos que contm essa palavra.

Usando o Help no Delphi


Agora, retorne ao Delphi e abra o projeto ALMOXARIFADO.DPR. Vamos definir o arquivo de help para ele. Clique em Project|Options e na pgina Application. Em "Help file", digite PROD.HLP e clique Ok. Com isso, o Delphi vai automaticamente abrir o arquivo de help quando voc pressionar a tecla [F1] durante a execuo. Mas vamos mudar algumas propriedades antes. Para implementar ajuda sensvel ao contexto no Delphi, cada formulrio e cada componente tem uma propriedade HelpContext, que o nmero de contexto do tpico correspondente. Altere essa propriedade nos trs formulrios: no principal, coloque o valor 1, no formulrio 'FormFornecedor'', o valor 2 e no formulrio 'FormProduto', o valor 3. Agora execute o programa. Se voc teclar [F1] no formulrio principal, ver o tpico principal. Se voc abrir o formulrio de produtos e teclar [F1], ver o tpico "Cadastro de produtos". Idem para o formulrio de fornecedor e seu tpico.

Outros recursos
Usando bitmaps
Voc pode incluir imagens bitmap (arquivos *.BMP) dentro do seu arquivo de help. Para isso, digite no texto do arquivo RTF um dos seguintes marcadores: {bmc nomearquivo.bmp} ou {bml nomearquivo.bmp} ou ainda

{bmr nomearquivo.bmp} Usando 'bmc', a imagem ser inserida na posio desse marcador, como se fosse um caractere a mais, alinhada na parte inferior com a base do texto: por exemplo. J usando 'bml', a imagem fica alinhada esquerda (left) do texto e 'bmr' mostra a imagem alinhada direita (right).

Usando um arquivo de contedo


Os novos arquivos de ajuda geralmente tm uma tela de contedo, que indica os tpicos de forma hierrquica, como a ajuda do Windows: Para fazer o mesmo, voc pode criar um arquivo de contedo [contents file] no Help Workshop. Clique em File| New, escolha "Contents file" e Ok. Um arquivo de contedo (.CNT) pode ser um ndice para vrios arquivos HLP. Clique em "Default filename" e digite PROD.HLP. Agora clique em "Add Below..." [adicionar abaixo], escolha a opo "Heading" e digite "Contedo". Clique Ok. Um "Heading" [cabealho] aparece como um "livro" no contedo. Clique em Add Below novamente e digite as opes como abaixo: Quando voc clicar em Ok, vai aparecer o tpico Menu Principal abaixo do Contedo. Continue adicionando outros itens. Coloque em Title o ttulo desejado e em Topic ID o identificador do tpico: CAD_FORNEC e CAD_PROD para os prximos. Depois de terminar, salve o arquivo de contedo como PROD.CNT no mesmo diretrio de PROD.HLP. Note que ao abrir PROD.HLP no Explorer, o Winhelp vai mostrar automaticamente a diviso de contedo.

Usando ajuda "o que isto"


Se um formulrio tiver um boto de help (se biHelp est marcado em BorderIcons), os tpicos de help so mostrados de forma diferente, como uma pequena janela do tipo "o que isto". Se voc clicar no boto de help e depois no formulrio, tambm vai ver os itens dessa forma. Essa alternativa melhor se voc tiver um tpico de ajuda para cada controle do formulrio. Nesse caso, para ver a ajuda, o usurio clica no boto de help e no controle desejado. Caso a propriedade HelpContext do controle atual seja 0 (zero), o Delphi usa o HelpContext do formulrio para localizar o tpico. Caso HelpContext do formulrio seja zero, o Delphi mostra o contedo padro.

Chamando comandos explicitamente


s vezes voc quer abrir um tpico diretamente a partir do programa, sem que o usurio tecle [F1]. Por exemplo, se voc tiver um boto de ajuda no formulrio. Para fazer isso, voc pode usar o mtodo HelpContext do objeto Application. Por exemplo, para abrir o tpico 3 (cadastro de produtos), use: Application.HelpContext(3); Se voc usa muito esse recurso, recomendvel declarar constantes para cada contexto de help usado. Mantenha uma unidade apenas com essas declaraes, por exemplo: unit Contextos; interface const hcMENU_PRINCIPAL = 1, hcCAD_FORNEC = 2, hcCAD_PROD = 3; implementation end. Nesse caso, voc poderia usar: uses Contextos; ... Application.HelpContext(hcCAD_PROD); Ou voc pode usar HelpJump, que faz o mesmo, mas com a string de contexto: Application.HelpJump('CAD_PROD'); J o mtodo HelpCommand permite chamar outras funes de help. Ele tem dois parmetros: 'Command' e 'Data': Command Data Ao

Help_Contents Help_ContextPopup Help_HelpOnHelp Help_Index

0 N de contexto 0 0

Mostra o tpico de contedo Mostra o tpico numa janela separada Mostra a ajuda do prprio Winhelp ("como usar a ajuda"). Mostra o ndice de palavras chave .

Bibliografia sugerida
Os seguintes livros so uma boa fonte de material para aprofundar-se um pouco mais no conhecimento do Delphi. LOBO, Rodrigo. Incrementando o Delphi. Ed. Advanced. ISBN: 8586916218. Este livro oferece uma srie de dicas, truques, utilitrios e componentes para o Delphi, visando tornar o trabalho do programador mais prtico e produtivo. Englobando as reas de banco de dados, multimdia, Internet, arquivos, etc. Inclui CD contendo os componentes e utilitrios. CANT, Marco. Dominando o Delphi 4 "A bblia". Ed. Makron. ISBN: 8534610460. Esta a edio completamente revisada e atualizada do livro de programao Delphi mais louvado de todos os tempos. Escrito pelo destacado especialista em Delphi, Marco Cant, este livro dedicado a usurios iniciantes e avanados, oferecendo a cobertura mais efetiva e cuidadosa da programao Delphi que se pode encontrar. PACHECO, Xavier e TEIXEIRA, Steve. Delphi 4 Developer's Guide (Developer's Guide Series). ISBN: 0672312840 Este livro uma referncia de nvel avanado mostrando aos desenvolvedores o mais importante que eles devem saber sobre o Delphi 4. Os autores lidam com desenvolvedores todos dias e citam tcnicas poderosas, dicas e conhecimento tcnico das mais avanadas caractersticas do Delphi 4. Tpicos incluem: Componentes de nvel avanado incluindo links embutidos, caractersticas especiais e DLLs, inclusive a criao de suas prprias Bibliotecas de Componentes Visuais (VCL), programao orientada a objeto avanada e object Pascal. O livro discute assuntos sobre desenvolvimento de aplicaes e conceitos de estrutura para aplicaes de bancos de dados cliente/servidor, desktop, bem como componentes MIDAS (Multi-tier Distributed Applications Services Suite), e como trabalhar com eles.