Sei sulla pagina 1di 9

1

Sistema Datacar - Parte II


Desenvolvimento Cliente/Servidor com dbExpress
por Guinther de Bitencourt Pauli e Fernando Sarturi Prass
Utilize a tecnologia dbExpress para acessar um servidor SQL, e aprenda na prtica importantes tcnicas de desenvolvimento Cliente/Servidor. Na primeira parte deste artigo apresentamos o sistema Datacar, modelamos as tabelas, criamos o banco de dados, construmos o formulrio principal, um formulrio base de cadastro e os formulrios para as tabelas do sistema. Neste segundo artigo, adicionaremos funcionalidade ao formulrio de cadastro, e construiremos o DataModule e o acesso a dados.

Personalizando o Formulrio de Cadastro Padro


No artigo anterior criamos uma interface padro para as fichas de cadastro, porm no adicionamos suas funcionalidades. Abra ento o formulrio FrmFichaPai que est na unit UFichaPai para iniciar a codificao. Quando abrir qualquer ficha de cadastro o usurio dever ter somente a opo de Incluir ou Procurar um registro. Ento, configure a propriedade Enabled dos botes Gravar, Cancelar e Excluir como False. A partir da o sistema dever controlar os botes automaticamente. O fato de o boto estar habilitado ou desabilitado depender do estado da tabela. Uma tabela pode estar em 13 diferentes estados, a ns vo importar apenas quatro: dsInsert (o usurio est inserindo um registro), dsEdit (est alterando), dsBrowse (est consultando ou navegando pelos registros) e dsInactive (a tabela est fechada). O componente TDataSource (que chamamos de DtSrc) possui uma propriedade chamada State que informa o estado atual da tabela. Alm disso, possui o evento onStateChange que chamado a cada vez que o estado da tabela alterado. Podemos controlar a propriedade Enabled dos botes a partir deste evento, com o seguinte cdigo:
procedure TFrmFichaPai.DtSrcStateChange(Sender: TObject); const Estados : array [TDataSetState] of string = ('Fechado','Consultando','Alterando','Inserindo','','','','','','','','',''); begin btnIncluir.Enabled:= DtSrc.State in [dsBrowse,dsInactive]; btnGravar.Enabled:= DtSrc.State in [dsInsert,dsEdit]; btnCancelar.Enabled:= DtSrc.State in [dsInsert,dsEdit]; btnExcluir.Enabled:= DtSrc.State in [dsBrowse]; btnProcurar.Enabled:= DtSrc.State in [dsBrowse,dsInactive]; StsBr.Panels[0].text:=Estados[DtSrc.State]; end;

Observe o array de strings baseado na enumerao TDataSetState, que contm os possveis estados da tabela. Este array usado na ltima linha de cdigo para preencher o primeiro painel da StatusBar (que chamamos de StsBr). Inicialmente este evento no ser chamado pois a tabela est fechada, ento coloque Fechado na propriedade Text do primeiro painel. Por falar em barra de status, acrescentamos a ela a capacidade de mostrar o texto colocado na propriedade hint dos componentes (botes, edits, etc). Isto feito a partir de um procedimento que chamamos de MostraHint. Declare o procedimento na seo private do formulrio:
private procedure MostraHint(Sender: TObject);

Pressione Shift+Ctrl+C para que o Delphi complete a declarao do procedimento. Implemente-o assim:
procedure TFrmFichaPai.MostraHint(Sender: TObject); begin StsBr.Panels[1].Text:=Application.Hint; end;

Agora basta fazer uma chamada mesmo, faa isto no evento onCreate do formulrio:
Application.OnHint:=MostraHint;

Outra funcionalidade importante que adicionamos ao cadastro a de verificar se os campos requeridos foram preenchidos. Declare Verifica_Campos_Em_Branco funo na seo private do formulrio da mesma forma que fizemos acima e implemente-a da seguinte forma:
function TFrmFichaPai.Verifica_Campos_Em_Branco: Boolean; var cont : integer; begin Result:=True; //assume que esto todos preenchidos for cont:= 0 to DtSrc.DataSet.FieldCount - 1 do if DtSrc.DataSet.Fields[cont].Required then if (DtSrc.DataSet.Fields[cont].IsNull) or (DtSrc.DataSet.Fields[cont].AsString='') then begin MessageDlg('Preencha o campo "'+DtSrc.DataSet.Fields[cont].DisplayLabel+'"', mtWarning,[mbOk],0,mbOk,nil); Result:=False; //significa que encontrou um campo em branco Break; //interrompe o lao for..to..do end; end;

Feito isto, vamos aos cdigos dos botes:


procedure TFrmFichaPai.btnIncluirClick(Sender: TObject); begin if not DtSrc.DataSet.Active then //verifica se est fechado DtSrc.DataSet.open; DtSrc.DataSet.Append; end; procedure TFrmFichaPai.btnGravarClick(Sender: TObject); begin if Verifica_Campos_Em_Branco then begin DtSrc.DataSet.Post; (DtSrc.DataSet as TClientDataset).ApplyUpdates(0); end else abort; end; procedure TFrmFichaPai.btnExcluirClick(Sender: TObject); begin if MessageDlg('Deseja realmente excluir este registro?',mtConfirmation, [mbYes,mbNo],0,mbYes,nil) = mrYes then begin //solicita uma confirmao antes de excluir DtSrc.DataSet.Delete; (DtSrc.DataSet as TClientDataSet).ApplyUpdates(0); end; end; procedure TFrmFichaPai.btnCancelarClick(Sender: TObject); begin DtSrc.DataSet.Cancel; end;

Observe o comando que aplica a cache dos dados: (DtSrc.DataSet as TClientDataset).ApplyUpdates(0). Como estamos usando um ClientDataSet preciso que faamos um TypeCasting do DataSet da ficha para que ele reconhea o mtodo ApplyUpdates (isto feito com o uso do operador as). Para que o cdigo seja compilado corretamente no esquea de acrescentar a DBClient na clausula uses desta unit. O boto Procurar o nico que no possui cdigo, pois a procura ser implementada em cada uma das fichas de cadastro, conforme veremos mais adiante. No evento onClose do formulrio precisamos fazer duas aes, verificar se os dados ainda no foram gravados (solicitando neste caso uma confirmao de gravao) e fechar a tabela se tudo estiver correto. O cdigo abaixo faz exatamente isto.
if DtSrc.State in [dsEdit,dsInsert] then if MessageDlg('Os dados foram alterados, deseja gravar?',mtConfirmation, [mbYes,mbNo],0,mbYes,nil) = mrYes then btnGravarClick(self);

3
DtSrc.DataSet.Close;

DataModule e Acesso a Dados


Construiremos agora a camada de acesso a dados do sistema, o DataModule. Clique em File | New | DataModule. Salve o DataModule como UDM, e configure sua propriedade name para DM. Coloque o componente SQLConnection (paleta dbExpress) no DataModule, e configure sua propriedade Name para SQLConnect. D um duplo clique no SQLConnection e no editor de conexes, clique no sinal de adio (+) para adicionar uma nova conexo. Na janela que aparece, escolha Interbase para o Driver Name e DATACAR para Connection Name. Pressione Ok, e no editor preencha as demais opes conforme abaixo:
Database = LOCALHOST:c:\Sistema\Banco\DATACAR.GDB Password = masterkey ServerCharSet = WIN1252 SQLDialect = 3 User_Name = sysdba

Depois de configurar estas opes, clique no boto OK para sair do editor. Configure a propriedade Login Prompt do SQLConnection para False e Connected para True.

Criao dos formulrios e DataModule


Criado o DataModule (chamaremos de DM a partir de agora), vamos otimizar a alocao de formulrios e do DM na memria. Voc deve criar um formulrio somente quando for utiliz-lo. Como exemplo, visualize a unit do formulrio principal, e substitua a instruo de ShowModal de FrmFichaMarcas por:
FrmFichaMarcas:=TFrmFichaMarcas.create(Application); // cria o form try // tenta FrmFichaMarcas.ShowModal; //exibir finally // no importa o que acontea, sempre faa isso abaixo FrmFichaMarcas.Free; //libera o form da memria end;

Agora faa o mesmo para os demais formulrios do sistema chamados a partir do formulrio principal. Devemos agora dizer ao Delphi que no crie mais estes formulrios na inicializao da aplicao. Clique no menu Project | View Source e altere o cdigo fonte do arquivo DPR como a seguir:
Application.Initialize; Application.CreateForm(TDM, DM); Application.CreateForm(TFrmPrincipal, FrmPrincipal); //os demais formulrios foram retirados daqui Application.Run;

Para que o Delphi no adicione futuros formulrios na criao automtica, clique em Tools | Environment Options | Designer. Em Module creations Options desmarque Auto create forms & data modules.

Acessando a tabela de Marcas


Coloque no DM um SQLDataSet da paleta dbExpress. Configure sua propriedade Name para dst_marcas e SQLConnection para SQLConnect. Abra o editor da propriedade CommandText e digite o seguinte:
select COD_MARCA, LOGOTIPO, NOME_MARCA from MARCAS where COD_MARCA=:PCOD_MARCA

Usamos aqui um recurso conhecido com parametrizao de consultas. Um Consulta (Query) parametrizada basicamente uma instruo SQL de SELECT que contm um parmetro na clusula where. O valor para este parmetro ento fornecido em tempo de execuo pelo usurio. Consultas parametrizadas so a essncia da programao Cliente/Servidor, o SELECT acima retorna somente um registro de acordo com o parmetro passado, e no todos os registros como comum se observar em alguns sistemas. Abra a propriedade Params de dst_marcas e selecione o parmetro no editor, configurando sua propriedade DataType para ftInteger. Coloque no DM um componente DataSetProvider da paleta DataAccess. Configure sua propriedade Name para dsp_marcas, e aponte sua propriedade DataSet para dst_marcas. Coloque um ClientDataSet no DM, definindo sua propriedade Name para cds_marcas e ProviderName para dsp_marcas. Agora d um clique de direita em cds_marcas e escolha a opo FetchParams, trazendo os parmetros definidos no SQLDataSet para o ClientDataSet.

Ative o ClientDataSet configurando sua propriedade Active para True. D um duplo clique nele. No editor de campos d um clique de direita e escolha Add All Fields. Configure a propriedade DisplayText de cada TField para exibir uma descrio para o campo, por exemplo Nome da Marca para o campo NOME_MARCA. Configure a propriedade Required para True do TField NOME_MARCA. Para o TField COD_MARCA configure Required como False, pois este valor dado pelo sistema. Seu DM deve estar como mostra a Figura 1:

Figura 1 DataModule com componentes dbExpress e DataAccess.

Configurada a busca dos dados com SELECT, precisamos dizer ao DataSetProvider como ele deve gerar as instrues de UPDATE, DELETE e INSERT (no dbExpress no temos um componente semelhante ao UpdateSQL do BDE). A propriedade ProviderFlags de um campo TField pode especificar como cada campo participar das instrues de atualizao geradas pelo DataSetProvider. No DM, d duplo clique em dst_marcas. No editor de campos d um clique direita e escolha a opo Add All Fields. Selecione ento todos os campos TFields, menos COD_MARCA, e configure a propriedade ProviderFlags.pfInWhere para False. Selecione ento apenas o TField COD_MARCA (que o campo chave da tabela) e configure sua propriedade ProviderFlags.pfInkey para True (pfInWhere j deve estar True). Finalmente, configure a propriedade UpdateMode de dsp_marcas para upWhereKeyOnly. Uma atualizao no Nome da Marca produzir agora a seguinte instruo de Update:
update "MARCAS" set "NOME_MARCA" = ? where "COD_MARCA" = ? //sem o uso de ProviderFlags todos os campos seriam colocados aqui

Valores Auto-Incrementados
O Firebird/Interbase no possui campos do tipo AutoIncrement (comumente usados no Paradox), porm possui uma funo chamada generator que deve ser usado associado a uma trigger. Optamos por no utilizar generators, pois este um recurso inexistente em alguns Sistemas Gerenciadores de Banco de Dados e gostaramos de apresentar uma soluo universal. Podemos facilmente contornar a inexistncia de um campo do tipo auto-incrementado com o uso de uma instruo SQL, como por exemplo:
select max(COD_MARCA) + 1 from MARCAS

O comando SELECT MAX utilizado acima trar o sucessor do maior valor existente na tabela para o campo indicado. Abaixo, construiremos uma procedure que reproduzir dinamicamente a consulta acima, retornando um novo valor para a chave. Na unit UDM localize a seo private na interface da classe TDM e acrescente a seguinte declarao da procedure:
private procedure Incrementa(Nome_Tabela : String; Chave_Primaria : TField);

Pressione Shift+Ctrl+C para que o Delphi complete a declarao da procedure. Implemente-a assim:
procedure TDM.Incrementa(Nome_Tabela : String; Chave_Primaria : TField); var Qry : TSQLQuery; begin //termina a execuo caso no esteja em modo de insero if Chave_Primaria.DataSet.State <> dsInsert then exit; Qry:=TSQLQuery.Create(nil); //cria uma instncia do objeto try Qry.SQLConnection:=SQLConnect; //componente de conexo Qry.SQL.Add('select max('+Chave_Primaria.FieldName+') from '+Nome_Tabela); Qry.Open; if Qry.Fields[0].IsNull then //se a tabela est vazia retornar nulo Chave_Primaria.AsInteger:=1 //ento este ser o primeiro registro else Chave_Primaria.AsInteger:=Qry.Fields[0].AsInteger+1; finally

5
FreeAndNil(Qry); //libera o objeto da memria end; end;

Utilizaremos agora o evento BeforePost do ClientDataSet para chamar esta procedure, que ativado no momento em os dados so gravados no seu buffer. O evento OnNewRecord neste caso poderia falhar, pois dois usurios da rede poderiam obter o mesmo valor para a chave caso inserissem registros ao mesmo tempo. Escreva ento o seguinte cdigo no evento BeforePost de cds_Marcas:
Incrementa('MARCAS',cds_marcasCOD_MARCA);

Devemos fazer isto para todas as tabelas do sistema, exceto ACESSORIOS_VEICULO, pois a sua chave primria formada pelos campos COD_ACESSORIO e COD_VEICULO_ATUAL que possuem valores que dependem de outras tabelas.

Formulrio de Marcas
Abra o formulrio de Marcas (menu View | Forms). Configure seu Caption para Marcas. Clique em File | Use Unit e escolha a UDM. Selecione o DtSrc e aponte sua propriedade DataSet para DM.cds_marcas. Coloque no Form de Marcas um componente DBText, um DBEdit e um DBImage. Aponte a propriedade DataSource destes componentes para o DtSrc Agora aponte suas propriedades DataField para COD_MARCA, NOME_MARCA e LOGOTIPO, respectivamente. Coloque trs Labels para indicar a finalidade destes controles, configurando sua propriedade Caption. Localize o DBText de destaque que est no Panel no topo do formulrio, e configure sua propriedade DataSource para DtSrc. Aponte sua propriedade DataField para NOME_MARCA. Para a manipulao do campo LOGOTIPO, coloque no formulrio um componente OpenDialog da paleta Dialogs. Coloque um boto no Form com o caption de Carregar..., e no seu evento OnClick digite:
if OpenDialog1.Execute then begin if DtSrc.State=dsbrowse then DM.cds_marcas.Edit; DM.cds_marcasLOGOTIPO.LoadFromFile(OpenDialog1.FileName); end;

Figura 2 Formulrio de cadastro de Marcas, rodando no Kylix 2.

Acessando a tabela de Modelos


Coloque no DM um SQLDataset da paleta dbExpress. Configure sua propriedade Name para dst_modelos e SQLConnection para SQLConnect. Abra o editor da propriedade CommandText e digite o seguinte:

6
select MO.COD_MARCA, MO.COD_MODELO, MO.FOTO, MO.NOME_MODELO, MA.NOME_MARCA from MODELOS MO inner join MARCAS MA on MA.COD_MARCA=MO.COD_MARCA where MO.COD_MODELO=:PCOD_MODELO

Repare aqui que o Nome da Marca de um Modelo ser obtido atravs de um join no lado servidor, e no usando um DBLookComboBox no lado cliente. Em aplicaes Cliente/Servidor voc nunca deve utilizar um campo Lookup, pois isto implica na busca de todos os registros do servidor para a tabela Lookup. Abra a propriedade Params de dst_modelos e configure o DataType do parmetro como ftInteger. D um duplo clique em dst_modelos e no editor adicione todos os campos. Selecione ento todos os TFields menos COD_MODELO. Configure as ProviderFlags de pfInWhere como false. Para o TField NOME_MARCA configure pfInUpdate como False. Isso porque este campo no existe na tabela de Modelos, ele deve somente ser obtido do banco de dados, no atualizado. Finalmente configure o TField COD_MODELO com ProviderFlags pfInkey e pfInWhere como True. Coloque no DM um DataSetProvider da paleta DataAccess. Configure sua propriedade Name para dsp_modelos, DataSet para dst_modelos e UpdateMode para upWhereKeyOnly. Coloque um ClientDataSet configurando sua propriedade Name para cds_modelos e ProviderName para dsp_modelos. D um duplo clique neste ClientDataSet, e no editor de campos escolha Add All Fields. Configure a propriedade DisplayText de cada TField para exibir uma descrio para o campo, por exemplo Nome do Modelo para o campo NOME_MODELO. Configure a propriedade Required para True do TField NOME_MODELO. Para COD_MODELO configure Required como False. Ative o ClientDataSet. Escreva ento o seguinte cdigo no evento BeforePost de cds_modelos:
Incrementa('MODELOS',cds_modelosCOD_MODELO);

Formulrio de Modelos
Abra o formulrio de Modelos. Configure seu Caption para Modelos. Clique em File | Use Unit e escolha a unit UDM. Selecione ento DtSrc e aponte sua propriedade DataSet para DM.cds_modelos. Coloque no Form dois componentes DBText, dois DBEdits e um DBImage. Aponte a propriedade DataSource destes componentes para o DtSrc e DataField para COD_MODELO, NOME_MARCA, COD_MARCA, DECRICAO_MODELO e FOTO, respectivamente. Coloque cinco Labels para indicar a finalidade destes controles, configurando sua propriedade Caption. Localize o DBText de destaque que est no Panel no topo do formulrio, e configure sua propriedade DataSource. Aponte sua propriedade DataField para NOME_MODELO. Utilize a mesma tcnica utilizada no formulrio de Marcas para fazer o carregamento da figura.

Figura 3 Formulrio de cadastro de Modelos.

Formulrio Padro de Procura


Crie um novo Form clicando em File | New | Form. D o nome de FrmProcurar para o formulrio e salve-o com o nome de UFormProcurar. Coloque no Form uma Label e d o Caption de Procurar por. Coloque um Edit com o nome de EditProc (retire seu texto) e um BitBtn com o Caption de Procurar.

Coloque um DBGrid da guia DataControls no centro do Form, e um DataSource com o nome de DtSrc. Aponte a propriedade DataSource do DBGrid para DtSrc e abra sua propriedade Options, configurando as flags dgRowSelect para True e dgColumnResize para False. Escolha tambm uma cor para a propriedade FixedColor do DBGrid. No seu evento OnTitleClick digite:
With DtSrc.dataset as TSQLClientDataSet do // Ateno: declare DBLocals na clusula uses IndexFieldNames:=Column.FieldName; //ordena os dados do DBGrid conforme a coluna cliclada

Coloque no Form um StatusBar com o nome StsBr, e tambm dois botes do tipo TBitBtn abaixo do DBGrid. D o nome de BtnOk e BtnCancel aos botes. Configure a propriedade Kind dos botes para mrOk e mrCancel respectivamente. Configure a propriedade Enabled de BtnOk para False. No evento OnClick do boto Procurar digite:
with DtSrc.Dataset as TSQLClientDataSet do begin Close; Params[0].AsString:=EditProc.Text+'%'; //atribui parmetro Open; BtnOk.Enabled:=not IsEmpty; //se no estiver vazio if IsEmpty then //se estiver vazio StsBr.SimpleText:=format('Nenhum registro foi encontrado com "%s"',[EditProc.text]) else StsBr.SimpleText:=format('%d registros encontrados com "%s"',[RecordCount,EditProc.text]); end;

Utilizaremos agora o prprio construtor virtual da classe bsica para receber o SQLClientDataSet que deve ser usado na procura, atravs do uso de polimorfismo e reintroduo de parmetros. Localize a seo public de TFrmProcurar, e redeclare o construtor:
public constructor create (AOwner : TComponent; DataSet : TSQLClientDataset); reintroduce;

Pressione ento Shift+Ctrl+C para o Delphi gerar o cabealho da implementao, e digite ento:
constructor TFrmProcurar.Create(AOWner: TComponent; DataSet: TSQLClientDataset); begin inherited create(AOWner); //chama o construtor da classe base DtSrc.DataSet:=DataSet; //atribui o DataSet de procura end;

Procura por Marcas e Modelos


Coloque no DM um SQLClientDataSet da paleta dbExpress. Defina seu nome para cds_procura_marca, e configure sua propriedade DBConnection para SQLConnect. Na sua propriedade CommandText digite:
select COD_MARCA, NOME_MARCA from MARCAS where NOME_MARCA like :PNOME_MARCA

V at a propriedade Params do cds_procura_marca e configure o nico parmetro com o valor ftString para DataType. D um duplo clique no cds_procura_marca e no editor de campos adicione todos os TFields. Selecione cada um dos TFields e configure sua propriedade DisplayText (ex. Nome da Marca). Agora volte ao formulrio de Marcas e pressione ALT+F11, e escolha a unit UFormProcurar. D um duplo clique no boto Procurar e escreva o seguinte no seu evento OnClick:
FrmProcurar:=TFrmProcurar.create(self,dm.cds_procura_marca); //cria try if FrmProcurar.ShowModal=mrOk then //se o usurio apertou o boto Ok begin DM.cds_marcas.Close; DM.cds_marcas.params[0].AsInteger:=DM.cds_procura_marcaCOD_MARCA.AsInteger; //atribui o parmetro DM.cds_marcas.Open; end; finally DM.cds_procura_marca.close; //fecha a procura FrmProcurar.Free; //libera o form de procura end;

Feita a abertura do cadastro de marcas usando a procura, utilizaremos o mesmo formulrio para procurar uma Marca para o Modelo, j que no temos um DBLookUpComboBox para alterar a Marca. Abra o formulrio Modelos e pressione ALT+F11, escolhendo a unit UFormProcurar. Coloque ento um boto com o caption de ... ao lado do DBText que mostra o Nome da Marca, e no seu evento OnClick digite:
FrmProcurar:=TFrmProcurar.create(self,dm.cds_procura_marca); //cria try if FrmProcurar.ShowModal=mrOk then // se o usurio apertou o boto Ok begin if DataSource1.State=dsbrowse then //se estiver em modo consulta DM.cds_modelos.Edit; //edita DM.cds_modelosCOD_MARCA.AsInteger:=DM.cds_procura_marcaCOD_MARCA.AsInteger; //atribui COD_MARCA DM.cds_modelosNOME_MARCA.AsString:=DM.cds_procura_marcaNOME_MARCA.AsString; //atribui NOME_MARCA end; finally DM.cds_procura_marca.close; // fecha a procura FrmProcurar.Free; //libera o form de procura end;

Repare a linha que atribui o Nome da Marca. Mas porque atribuir o Nome do Marca se no existe este campo na tabela de Modelos? Fizemos isso somente para efeitos de exibio em tela, para no ser necessrio um refresh no Join que receberia o novo Nome da Marca. Este campo no ser atualizado pois desativamos sua ProviderFlag de Update, j que sua atualizao causaria um erro. Agora utilize a mesma tcnica para construir uma procura para o cadastro de modelos, criando um novo SQLClientDataSet e passando-o como parmetro para o formulrio de procura. Chame esta procura no boto Procurar do formulrio de Modelos.

Figura 4 Formulrio de Procura.

Cadastro de Cidades e Acessrios


Utilizando os mesmos procedimentos acima construa agora o acesso a dados para a tabela de Cidades e Acessrios, as quais so bastante simples, j que possuem poucos campos. Coloque um trio de componentes para cada tabela como fizemos acima, e ento construa o formulrio de dados e procura. Qualquer dvida voc poder baixar a verso atual do sistema e consultar as fontes.

9 Figura 5 Cadastro de Acessrios e Cidades.

Concluso
Neste artigo construmos o acesso a dados criando um DataModule e utilizando os componentes da paleta dbExpress e DataAccess. Adicionamos funcionalidades aos formulrios de cadastros e construmos a interface para entrada de dados, utilizando controles DataAware. No prximo artigo construiremos o formulrio de clientes e veculos. Um forte abrao a todos!
Fernando Sarturi Prass mestrando em Cincia da Computao na UFSC. Desenvolve sistemas para os bancos de dados InterBase/Firebird, DB2 e SQL Server. Pode ser contatado em prass@inf.ufsc.br ou no site Planet Delphi (www.fprass.hpg.com.br). Guinther Pauli Bacharel em Sistemas de Informao (Unifra, Santa Maria RS) e desenvolvedor Delphi/Kylix certificado Master Delphi Programmer pela Tekmetrics e Delphi 5 Programmer pela Brainbench. Trabalha com DataSnap, dbExpress e WebSnap em solues distribudas, cliente/servidor e web. Pode ser contatado em guinther@unifra.br.

Potrebbero piacerti anche