Você está na página 1de 52

CAPA

Clube86.indb 1 02.07.07 14:59:24


ClubeDelphi
EDITORIAL
A integração entre sistemas é uma necessidade cada vez mais cons-
tante. Os Web Services permitiram que aplicações pudessem se co-
Ano 7 - 86ª Edição - 2007 - ISSN 1517990-7 Impresso no Brasil municar através do SOAP (Simple Object Access Protocol), e desde o
Delphi 6 podemos contar com esse suporte. Web Services e SOAP são
Corpo Editorial Atendimento ao Leitor baseados em XML, o que permite que aplicações rodando em platafor-
Diretor Editorial e Administrativo A DevMedia conta com um departa- mas heterogêneas possam se comunicar. E o melhor de tudo, através
Gladstone Matos mento exclusivo para o atendimento ao
da Internet. Nesta edição, você vai aprender a construir uma solução
gladstone@neoficio.com.br leitor. Se você tiver algum problema no
recebimento do seu exemplar ou pre- multicamadas em Delphi baseada nessas tecnologias, onde um servi-
Editor de Arte cisar de algum esclarecimento sobre
dor Web Service vai concentrar e centralizar o acesso a dados e regras
Vinicius O. Andrade assinaturas, exemplares anteriores,
viniciusoandrade@gmail.com endereço de bancas de jornal, entre de negócio, que são consumidas por diferentes tipos de cliente: Web
outros, entre em contato com: (ASP.NET), Desktop (Delphi 7 Win32) e Mobile (Compact Framework).
Capa Carmelita Mulin
Antonio Xavier www.devmedia.com.br/central/default.asp Ainda nesta edição, muitas dicas sobre o principal controle para de-
webdesigner@devmedia.com.br (21) 2220-5375
senvolvimento Web com ASP.NET e Delphi, o DataGrid, no artigo de
Kaline Dolabella
Editor Geral Gerente de Marketing e Atendimento Luciano Pimenta. O Adriano continua a série de artigos sobre a novíssi-
Guinther Pauli kalined@terra.com.br
guinther@devmedia.com.br (21) 2220-5375 ma versão do Delphi, aprofundando temas como suporte ao Windows

Publicidade Vista e AJAX. SOAP também é o tema do artigo do Michael Benford, na


Editor Técnico
Luciano Pimenta Para informações sobre veiculação de segunda parte da aplicação que mostra como criar um disco virtual.
lucianopimenta@clubedelphi.net anúncio na revista ou no site entre em
O Rodrigo mostra como é simples a impressão matricial em Delphi.
contato com:
Revisão O Fernando Prass e o Cristiano dão boas dicas para você obter maior
Kaline Dolabella
Rafael de Castro Santos publicidade@devmedia.com.br produtividade e controle no trabalho em equipe. E finalmente, o Fabrício
rafael@devmedia.com.br
Para fechar parcerias ou ações espe- apresenta uma solução inteligente para uma tarefa que parecia impos-
Distribuição cíficas de marketing com a DevMedia,
Fernando Chinaglia Dist. S/A sível, publicar na Web seus relatórios do Quick Report.
entre em contato com:
Rua Teodoro da Silva, 907
Jeff Wendell Um abraço a todos e muito sucesso com o Delphi!
Grajaú - RJ - 206563-900
jeff@devmedia.com.br

Apoio Guinther Pauli


guinther@devmedia.com.br
Microsoft Certified: MCP, MCAD, MCSD.NET
A revista ClubeDelphi é parte integrante da assinatura ClubeDelphi Borland Certified: Delphi 6, 7, 2005, 2006, Web, Kylix
PLUS. Para mais informações sobre o pacote PLUS, acesse:
http://www.devmedia.com.br/clubedelphi/portal.asp
Fale com o Editor
É muito importante para a equipe saber entre em contato com os editores, informan-

ÍNDICE o que você está achando da revista: que


tipo de artigo você gostaria de ler, que
do o título e mini-resumo do tema que você
gostaria de publicar:
artigo você mais gostou e qual artigo
você menos gostou. Fique a vontade para Guinther Pauli - Editor da Revista
06 - Integrando aplicações .NET, Win32 e Mobile com SOAP e Web Services
entrar em contato com os editores e dar a guinther@devmedia.com.br
Guinther Pauli
sua sugestão!
Se você estiver interessado em publicar Luciano Pimenta - Editor do Site
16 - Dicas de DataGrid um artigo na revista ou no site ClubeDelphi, lucianopimenta@clubedelphi.net
Luciano Pimenta

22 - Delphi 2007 em detalhes Portal do Assinante

NÃO A
Adriano Santos
A ClubeDelphi tem uma novidade para você que comprou este

C
PER
28 - SOAP - Aprenda técnicas avançadas em um exemplo passo a passo exemplar na banca de jornal: você pode acessar GRATUITAMENTE,

Michael Benford o Portal do Assinante ClubeDelphi!

Confira o que você encontra no Portal do Assinante:


38 - Impressão matricial no Delphi - Mais de 390 Vídeo Aulas!
- 6 cursos online!
Rodrigo Otto Mostaert
- 1 Livro Eletrônico sobre ADO.NET e BDP!
- Mais de 140 Artigos Exclusivos!
42 - Publique na Web seus relatórios do QuickReport
Para Utilizar o Portal do Assinante, acesse www.devmedia.com.br/clubedelphi/portal.asp
Fabrício Desbessel
e utilize as informações abaixo: Login: DVM.CD e Senha: KZW19

46 - Boas práticas para trabalhar em equipe O acesso é válido por 30 dias a partida da data de lançamento da revista. Todos os
meses a ClubeDelphi lhe dará uma senha válida para acessar o portal. Comprando a
Fernando Sarturi Prass e Cristiano Caetano
revista regularmente em bancas, você terá acesso ininterrupto a ele!

Edição 86 - ClubeDelphi 3

Clube86.indb 3 02.07.07 14:59:31


Ask The Expert
Perguntas e Respostas
Dúvidas respondidas por Guinther Pauli
(envie as suas para guinther@devmedia.com.br)

SubComponentes
Como alguns componentes da VCL, como Listagem 1. Exemplo de AutoRefresh
o SimpleDataSet e LabeledEdit, conseguem unit uCDSRefresh;
interface
ter subcomponentes associados com todas uses
as propriedades acessíveis internamente? SysUtils, Classes, DB, DBClient, ExtCtrls;
type
Gostaria de criar um componente com essas TCDSRefresh = class(TClientDataSet)
private
características mais não sei como. FTimer: TTimer;
Rodrigo Pizolotto procedure DoTimer(Sender: TObject);
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
A partir da versão 6, o Delphi passou published
property Timer: TTimer read FTimer;
a suportar o recurso de SubComponentes. end;
Como exemplo, vamos criar uma classe procedure Register;

descendente de ClientDataSet que possui implementation


procedure Register;
a característica de AutoRefresh, sendo que begin
RegisterComponents(‘Guinther’, [TCDSRefresh]);
o intervalo de Refresh é controlado por end;
um Timer interno que será usado com o constructor TCDSRefresh.Create(AOwner: TComponent);
begin
recurso de subcomponentes. O código é inherited;
if Assigned(FTimer) then exit;
mostrado na Listagem 1. FTimer := TTimer.Create(self);
FTimer.OnTimer := DoTimer;
Para criar esse componente, crie um FTimer.SetSubComponent(True);
novo Package a partir do Object Repository end;
destructor TCDSRefresh.Destroy;
e a seguir adicione um componente a ele. begin
FTimer.Destroy;
Quando usamos o componente em uma inherited;
end;
aplicação, o CDS dá um refresh automático procedure TCDSRefresh.DoTimer(Sender: TObject);
a cada n segundos. Observe que a “mágica” begin
if Active then begin
toda está na chamada ao SetSubComponent. Close;
Open;
O resultado é mostrado na Figura 1. end;
end;
end.

ClientDataSet não exclui dados fisicamente


Estou trabalhando em uma aplicação que lhar de forma correta com o ApplyUpdates.
salva os dados em XML. Quando eu excluo O DataSetProvider precisa desses valores
um registro em um DBGrid, por exemplo, ele excluídos, alterados, ou inseridos, para
deixa de ser exibido em tela, mas quando abro gerar instruções SQL de atualização.
o arquivo XML, verifico que mesmo excluído No entanto, se você está trabalhando
ele continua lá. O que o ClientDataSet faz? com XML local (também conhecido como
Thiago Balbino MyBase), não precisará armazenar o
delta. Para isso, basta chamar o método
Olá Thiago. O ClientDataSet mantém MergeChangeLog do ClientDataSet antes
atualizações em cache para poder traba- do salvamento.
Figura 1. SubComponentes

4 ClubeDelphi - Ask The Experts – Perguntas e Respostas

Clube86.indb 4 02.07.07 14:59:36


+390 vídeo aulas | 6 cursos online

www.clubedelphi.net/portal

Caro Leitor, Últimas Vídeo-Aulas (Somente para assinantes)

O portal ClubeDelphi PLUS é a continuação, na Web, da Sistema completo com Delphi 7, dbExpress e Firebird 2.0
revista ClubeDelphi. O portal recebe um conteúdo novo Acompanhe a criação de um sistema completo de gerenciamento de locadora,
todo dia e hoje conta com: i) mais de 390 vídeo aulas; ii) usando Delphi, dbExpress e Firebird 2.0. Luciano Pimenta, mostrará todos os passos
6 cursos online; iii) 1 livro eletrônico gratuito, de Guinther necessários para a criação do projeto em todas as fases de implementação, desde a
Pauli, sobre ADO.NET e BDP; iv) mais de 150 artigos criação do banco de dados, até a migração para .NET.
exclusivos (que não foram publicados na revista)!;
Acesse o portal ClubeDelphi PLUS e receba muito mais Criando uma tela de consulta dinâmica – Parte IV e V
conteúdo sobre Delphi! E o que é melhor: de graça! Veja nessa vídeo aula de Paulo Quicoli, como criar um tela de consulta dinâmica.
Todo leitor da revista ClubeDelphi, seja ele assinante ou
Mini curso de ClientDataSet - Parte I a VI
comprador da revista em bancas, tem acesso ao portal (para
Veja nessas aulas de Guinther Pauli um mini curso exclusivo sobre dicas avançadas
quem compra em bancas, o acesso é válido por 30 dias).
sobre o ClientDataSet.
Se você é assinante, utilize o seu login e senha pessoais
para acessar o portal. Se você comprou em bancas, utilize MainMenu no Delphi for PHP
o login e senha publicados na página do editorial desta Veja nessa vídeo aula de Fabricio Desbessel como criar um menu no Delphi for PHP.
edição.
Confira a seguir as últimas novidades do portal! Trabalhando com Word no Delphi – Parte III
Veja nessa vídeo aula de Jefferson Junglaus, como trabalhar com o Word no Delphi.
Boa leitura e sucesso!
Equipe DevMedia Instalando o FastReport 4 no Delphi 2007 – Parte I e II
Veja nessa vídeo aula de Paulo Quicoli, como instalar a nova versão no FastReport no
Delphi 2007.

Conhecendo o plugin GExpert


Curso Online - PLUS Veja nessa vídeo aula de Adriano Santos, como instalar e conhecer as características
do plugin GExpert.
O portal ClubeDelphi PLUS conta com 6 cursos online, confira!
Conhecendo o plugin CnWizards
Veja nessa vídeo aula de Adriano Santos, como instalar e conhecer as características
Sistema completo com Delphi 7, dbExpress e Firebird 2.0 do plugin CnWizards.
[Luciano Pimenta]

Aplicações WEB com IW e Delphi 7 (Delphi Win32)! . : : DESTAQUE : : .


[Guinther Pauli]
Confira no portal ClubeDelphi PLUS 6 vídeo aulas sobre Ferramentas para
Aplicações client/server no Delphi 2006 trabalhar com o Delphi
[Luciano Pimenta]
Compactação de arquivos com UPX
Criando uma Aplicação multi-camadas Completa com Delphi http://www.devmedia.com.br/articles/viewcomp.asp?comp=5393
[Guinther Pauli]
Comparação de arquivos fonte com o Compare It
Criando uma aplicação completa: sistema SysCash http://www.devmedia.com.br/articles/viewcomp.asp?comp=5431
[Everson Volaco]
Formatando código-fonte com o DelFor Exp
Aplicações Client/Server com dbExpress e Firebird
http://www.devmedia.com.br/articles/viewcomp.asp?comp=5454
[Luciano Pimenta]
Trabalhando com o Simple Resource Editor
http://www.devmedia.com.br/articles/viewcomp.asp?comp=5476

Conhecendo o plugin GExperts


http://www.devmedia.com.br/articles/viewcomp.asp?comp=5609

Conhecendo o plugin CnWizards


http://www.devmedia.com.br/articles/viewcomp.asp?comp=5652

ClubeDelphi PLUS - ClubeDelphi 5

Clube86.indb 5 02.07.07 14:59:38


Integrando aplicações .NET, Win32 e Mobile
com SOAP e Web Services

N
a edição 70 da ClubeDelphi, camadas. Teremos uma camada servido-
propus uma solução baseada em ra, na forma de um Web Service, que se
SOAP, XML e ClientDataSet para encarregará de acessar o banco de dados.
permitir que aplicações se comunicassem Diferentes tipos de aplicação farão acesso
através de Web Services. Entre algumas a esse Web Service, solicitando dados
vantagens da solução, destacaria o acesso e chamando métodos para atualizar
ao BD a partir de qualquer lugar da In- informações.
ternet e centralização do acesso a dados
em uma única camada. Arquitetura da solução
A aplicação cliente Win32 poderia, por Para ter uma visão geral da arquitetu-
exemplo, ficar em um determinado país e ra da aplicação que será criada, veja o
obter dados via Web Services de um servi- esquema da Figura 1. Durante todo este
Guinther Pauli dor rodando em um outro canto do globo. tutorial, sempre que tiver uma dúvida de
guinther@devmedia.com.br) Tudo trafegado através de XML sobre o como a solução está sendo implementada
Atua no ramo de tecnologia e programação há HTTP. São as facilidades da distribuição e em que camada está sendo empregada
mais de 17 anos, é autor de mais de 100 artigos
Web aliadas ao RAD para Desktop. determinada tecnologia, consulte essa
publicados, 200 vídeo-aulas e do livro "Delphi
– Programação para Banco de Dados e Web". É Neste artigo vamos dar um passo a figura. Ela será nosso “mapa”.
Bacharel em Sistemas de Informação, Microsoft mais nessa proposta. Além de permitir O Web Service será criado em Delphi for
Certified: MCP, MCAD, MCSD.NET, Borland Certi- todas essas vantagens já citadas, vamos .NET e utilizará ADO.NET para comuni-
fied: Delphi 6, 7, 2005, 2006, Web e Kylix. Editor adicionar mais um elemento importante cação com o banco de dados (SQL Server
Geral das Revistas .net Magazine, ClubeDel-
na arquitetura: nossa mesma solução ou Firebird para este exemplo). Um cliente
phi, WebMobile (.NET) e Mr.Bool. Palestrante
em vários eventos pelo Brasil, como TechDay poderá ter clientes rodando em diferentes ASP.NET será a interface Web da solução,
SP,RJ,POA,BH, Web Days e todas as edições da plataformas. permitindo que a aplicação seja acessada
Borland Conference. Nossa solução será, obviamente, multi- por qualquer browser pela Internet.

6 ClubeDelphi - Integrando aplicações .NET, Win32 e Mobile com SOAP e Web Services

Clube86.indb 6 02.07.07 14:59:44


BOA IDÉIA

Uma aplicação Win32 em Delphi 7 aces- wnload o SQL Server Management Studio No Delphi, clique em File>New>Other
sará os mesmos dados, comunicando-se Express, front-end para trabalhar com o e na categoria Delphi for .NET Projects
com o Web Service através de SOAP pela SQL Server 2005. escolha ASP.NET Web Service Application.
Intranet ou Internet. E finalmente, uma Usando o Management Studio, crie um Dê o nome de “DataServer” para a nova
aplicação Mobile construída com Delphi novo banco de dados chamado CUSTO- aplicação e salve o arquivo ASMX como
for .NET e Compact Framework vai per- MERS e dentro dele crie uma nova tabela “customer.asmx”, alterando o seu nome
mitir acesso aos mesmos dados a partir também chamada CUSTOMERS. A estru- no Project Manager. O arquivo ASMX é
de Pocket PCs. tura dessa tabela deve seguir o modelo o que define o nosso Web Service. Cada
Usando as tecnologias abordadas neste mostrado na Tabela 1. Lembre-se de definir classe dentro de um ASMX representa
artigo, você poderá ao final: o campo chave como Identity para que os uma tabela do banco de dados nessa
• Acessar seu banco de dados a partir de valores sejam criados automaticamente. solução que estou propondo.
qualquer lugar da Internet, usando qual- Note que vamos criar uma tabela
quer um dos três tipos de cliente. Mesmo extremamente simples, para que possa- Nota: Na arquitetura proposta, o Web
a solução Desktop pode se conectar ao BD mos focar nossas atenções nos aspectos Service é a camada que será encarrega-
remotamente através da Internet, graças relativos à multicamadas, Web Services da do acesso e persistência dos dados,
ao SOAP; e multi-plataforma. Caso queira usar um camada também conhecida como DAL
• Criar aplicações distribuídas para a banco de dados maior e pronto, como o (Data Access Layer).
Internet. Você pode, por exemplo, cen- Northwind ou Employee, fique a vontade.
tralizar o banco de dados e servidor de A solução demonstrada neste exemplo Acesse o código do serviço, clican-
aplicação na matriz da empresa e distri- funciona perfeitamente com o Firebird, do na aba customer.pas logo abaixo no
buir as aplicações clientes para as filiais, caso queira utilizar esse banco de dados. editor, e renomeie a classe criada para
que terão acesso a dados em tempo real e No endereço para download deste artigo, “TCustomer”. Lembre-se de fazer essa
que se mantêm sempre atualizados, sem você encontra uma versão do mesmo alteração em todo o código já criado. Se
a necessidade de sincronização; aplicativo deste artigo usando o banco quiser, pode usar o recurso de SynchEdit
• Poderá ter sua aplicação oferecida de dados open source. para alterar todas as referências de uma
como um serviço: seus clientes podem Após criado o banco de dados, insira única vez, bastando para isso selecionar
acessar a aplicação através da Internet, alguns registros na tabela CUSTOMERS todo o código, clicar no botão e fazer
sem a necessidade de nenhuma con- para facilitar nossos testes quando for- a alteração (use o TAB).
figuração local na empresa, tudo fica mos construir a aplicação no Delphi.
configurado e armazenado no servidor; Métodos para manutenção
• Centralizar todo o acesso a dados em Construindo o servidor Nossa classe TCustomer vai prover os
uma única camada servidora, que também Essa será a camada responsável por métodos necessários para manipular os
contém as regras de negócio. Os aplicati- toda a comunicação com o banco de da- dados da tabela CUSTOMERS. Vamos
vos clientes compartilham essa mesma dos. Por ser baseada em XML Web Servi- começar implementando as operações
camada, garantindo independência de ces, essa camada poderá ser acessada por básicas de manutenção de clientes.
banco de dados e tecnologia de acesso. Por diferentes tipos de aplicação rodando em Declare os métodos da Listagem 1 na
exemplo, uma única modificação em um diferentes plataformas. Além disso, esse seção public da classe. Observe que os
SQL ou código de negócio deve ser feito acesso não precisa necessariamente ser métodos precisam ter o atributo WebMe-
apenas no servidor e automaticamente é feito em uma LAN, pois dados em XML thod para poderem ser acessados através
refletido nas aplicações clientes; e chamadas a métodos SOAP podem ser de SOAP. Aperte Shift+Ctrl+C e imple-
• Oferecer ao seu cliente três tipos de feitos através da Internet. mente os métodos conforme mostrado
acesso ao servidor. Poderá utilizar a in- na Listagem 2.
terface Web para acesso via browser. A Estamos usando ADO.NET para comu-
aplicação desktop pode ser usada tanto nicação com o SQL Server. O namespace
em ambiente Intranet quanto Internet. E System.Data.SqlClient deve ser importado
finalmente, os clientes podem usar dis- para termos acesso às classes necessárias.
positivos móveis como Pocket PCs para Em ambos os métodos, criamos um obje-
acessarem o mesmo servidor e BD. to de conexão (SqlConnection) e utilizamos
um SqlCommand para executar um deter-
Criando o banco de dados minado comando.
O primeiro passo será a criação do
banco de dados. Para este exemplo, vou Coluna Tipo
utilizar o banco de dados free SQL Server CustomerId Integer (chave)
2005 Express, que pode ser obtido no FirstName Varchar(50)
endereço indicado na seção Links. Nesse Company Varchar(50)
mesmo endereço você encontra para do- Figura 1. Arquitetura da solução Tabela 1. Campos da tabela CUSTOMERS

Edição 86 - ClubeDelphi 7

Clube86.indb 7 02.07.07 14:59:53


O ExecuteNonQuery executa o comando Listagem 1. Definição dos métodos SOAP

de update ou insert. Para simplificar eu [WebMethod]


procedure Update(CustomerID: integer;
omiti o método delete, ele é bastante seme- Name,Company: string);
[WebMethod]
lhante aos demais e pode ser facilmente procedure Insert(Name, Company: string);
construído.
O fechamento da conexão está protegi- Listagem 2. Implementação dos métodos de manutenção
do por um bloco try finally para garantir uses System.Data.SqlClient;
...
que a conexão seja devolvida para o pool procedure TCustomer.Update(
de conexões mesmo que um erro acon- CustomerID: integer; Name, Company: string);
var
teça. Connection Pooling é o mecanismo Con: SqlConnection;
Cmd: SqlCommand;
que permite ao ADO.NET reaproveitar SQL: string;
conexões ao banco de dados, garantindo begin
Con := SqlConnection.Create(ConnectionString);
performance. SQL := System.&String.Format(
'update CUSTOMERS '+'set FirstName=''{1}'', Company=''{2}'' '+
Caso esteja utilizando Firebird, você de- 'where CustomerId={0}', [CustomerId,Name,Company]);
Cmd := SqlCommand.Create(SQL,Con);
verá utilizar os componentes nativos para Con.Open();
acesso via ADO.NET. Para mais informa- try
Cmd.ExecuteNonQuery();
ções de como fazer a instalação, consulte finally
Con.Close();
a edição 66. Feita a instalação, referencie o end;
end;
assembly FirebirdSql.Data.Firebird.dll através
da opção Add Reference no Project Manager procedure TCustomer.Insert(Name, Company: string);
var
e adicione o namespace no uses. O código Con: SqlConnection;
Cmd: SqlCommand;
muda pouco, basta usar FbCommand no lu- SQL: string;
begin
gar de SqlCommand, FbConnection no lugar Con := SqlConnection.Create(ConnectionString);
de SqlConnection e assim por diante. SQL := System.&String.Format(
'insert into CUSTOMERS (FirstName,Company) '+' values (''{0}'',''{1}'')',[Name,Company]);
A função ConnectionString está declarada Cmd := SqlCommand.Create(SQL,Con);
Con.Open();
na seção private da classe e tem a imple- try
Cmd.ExecuteNonQuery();
mentação mostrada na Listagem 3. A Con- finally
nectionString é usada como parâmetro no Con.Close();
end;
construtor da classe de conexão. Uma opção end;
seria armazenar essa string no Web.Config. Se
Listagem 3. Método que retorna a ConnectionString
estiver usando o Firebird, note que a mesma
tem parâmetros um pouco diferentes. function TCustomer.ConnectionString: string;
begin
Result := 'data source=".\SQLEXPRESS";'+
'integrated security=True;'+'initial catalog=CUSTOMERS';
Método para seleção end;
O método que obtém dados é chamado
SelectDS (o “DS” é de “DataSet”, pois Listagem 4. Executando um select e retornando um DataSet

esse é o tipo de retorno). Diferente dos public


[WebMethod]
métodos de update e insert, esse método function SelectDS(Filter: string): DataSet;
precisa retornar o conjunto de dados ...
function TCustomer.SelectDS(Filter: string): DataSet;
obtido através da execução do comando var
Con: SqlConnection;
select. No ADO.NET, um conjunto de da- Adpt: SqlDataAdapter;
Cmd: SqlCommand;
dos pode ser representado basicamente Dst: DataSet;
de duas formas: por um DataReader ou SQL: string;
begin
por um DataSet. if Filter = '' then Filter := '1=1';
Con := SqlConnection.Create(ConnectionString);
Como os dados vão precisar ser trafega- SQL := 'select * from CUSTOMERS where ' + Filter;
Cmd := SqlCommand.Create(SQL,Con);
dos pela Web em formato XML e o cliente Adpt := SqlDataAdapter.Create(Cmd);
precisa trabalhar de forma desconectada Dst := DataSet.Create;
Adpt := SqlDataAdapter.Create(Cmd);
do BD (off-line), vamos usar um DataSet, Adpt.Fill(Dst);
Result := Dst;
já que o DataReader exige uma conexão e end;
não pode ser serializado.
O código da Listagem 4 mostra a imple-
mentação do método SelectDS. Aqui te-
mos uma nova classe, um SqlDataAdapter
que é utilizado para preencher os dados
no DataSet através do método Fill.
A função que criamos recebe um parâ-

8 ClubeDelphi - Integrando aplicações .NET, Win32 e Mobile com SOAP e Web Services

Clube86.indb 8 02.07.07 14:59:54


BOA IDÉIA

metro que é a condição where do Select. Listagem 5. Métodos para atualização de DataSets

Note também que estamos retornando public


[WebMethod]
todos os campos, caso queira você pode procedure InsertDS(Dst: DataSet);
[WebMethod]
criar um outro método que retorne so- procedure UpdateDS(Dst: DataSet);
mente os campos que desejar. ...
procedure TCustomer.InsertDS(Dst: DataSet);
var
Con: SqlConnection;
Método para Update de DataSet Adpt: SqlDataAdapter;
Os métodos update e insert que codifica- Cmd: SqlCommand;
SQL: string;
mos anteriormente recebem os valores a begin
Con := SqlConnection.Create(ConnectionString);
serem atualizados diretamente por parâ- SQL := 'insert into CUSTOMERS ' + '(FirstName,Company) values ' +
'(@FirstName, @Company);';
metro. Quando trabalhamos com ADO. Cmd := SqlCommand.Create(SQL,Con);
NET, podemos usar um SqlDataAdapter Cmd.Parameters.Add(SqlParameter.Create('@CustomerId',SqlDbType.Int,4,'CustomerId'));
Cmd.Parameters.Add(SqlParameter.Create('@FirstName',SqlDbType.VarChar,50,'FirstName'));
para obter atualizações de um DataSet e Cmd.Parameters.Add(SqlParameter.Create('@Company',SqlDbType.VarChar,50,'Company'));
Adpt := SqlDataAdapter.Create();
gerar os respectivos comandos de update, Adpt.InsertCommand := Cmd;
Adpt.Update(Dst);
semelhante ao que faz um DataSetProvi- end;
der no DataSnap.
procedure TCustomer.UpdateDS(Dst: DataSet);
Dessa forma, criei mais dois métodos var
Con: SqlConnection;
(Listagem 5) que processam atualizações Adpt: SqlDataAdapter;
Cmd: SqlCommand;
via DataSet e SqlDataAdapter, que podem SQL: string;
ser utilizados diretamente por aplicações begin
Con := SqlConnection.Create(ConnectionString);
cliente que sejam construídas sobre o .NET SQL := 'update CUSTOMERS set ' + 'FirstName = @FirstName, ' +
'Company = @Company '+
Framework e reconheçam a classe DataSet. 'where CustomerId = @Original_CustomerId';
Cmd := SqlCommand.Create(SQL,Con);
Por exemplo, uma aplicação ASP.NET Cmd.Parameters.Add(SqlParameter.Create(
vai receber um DataSet com dados de '@Original_CustomerId',SqlDbType.Int,4, 'CustomerId'));
Cmd.Parameters.Add(SqlParameter.Create('@CustomerId',SqlDbType.Int,4,'CustomerId'));
uma consulta, vai guardar em cache, Cmd.Parameters.Add(SqlParameter.Create('@FirstName',SqlDbType.VarChar, 50, 'FirstName'));
Cmd.Parameters.Add(SqlParameter.Create('@Company',SqlDbType.VarChar,50,'Company'));
processar atualizações nesses dados e fi- Adpt := SqlDataAdapter.Create();
Adpt.UpdateCommand := Cmd;
nalmente vai devolver o DataSet alterado Adpt.Update(Dst);
para o Web Service, que enviará as altera- end;

ções ao BD, através desses métodos.


Uma opção aqui, caso queira, é codificar
um único método que processe a inserção
e atualização, também é possível.
Você já pode executar o Web Service e
fazer alguns testes. O .NET Framework
gera por padrão uma página para teste do
Web Service, usando Reflection para obter
informações sobre os métodos e criando a
UI necessária para preencher os parâme-
tros para os métodos (Figura 2).
Faça alguns testes para o update e insert
e verifique que as alterações nos dados
realmente foram efetivadas no BD. Para
o select, veja que os dados são retornados
em um documento XML, o que comprova
que um DataSet pode facilmente ser tra-
fegado via HTTP.
Nossa camada servidora não contém
interface (a da Figura 2 é apenas para teste
e não tem finalidade para o usuário final),
apenas cuida do acesso a dados e regras Figura 2. Testando o Web Service

Edição 86 - ClubeDelphi 9

Clube86.indb 9 02.07.07 14:59:59


de negócio. Essa é uma das grandes vanta- Clique na seta, visualize o documento selecione Columns, depois adicione uma
gens do desenvolvimento em camadas. WSDL e na opção Web reference folder coluna do tipo Button Column>Select.
Por trabalhar com SOAP e XML, nosso name digite “DataServer”. Clique em Add A Listagem 6 mostra o código do Find,
servidor pode ser agora acessado por Reference. Note que algumas referências nele estamos utilizando a Web Reference
uma variedade enorme de linguagens e serão adicionadas ao projeto, no Project configurada anteriormente. TCustomer
plataformas: .NET, Win32, Linux, Mobile, Manager. Um dos procedimentos feitos parece ser a nossa classe servidora, mas
Java, PHP, Kylix, VS etc. Veremos alguns nesse passo é a criação de uma classe pro- na verdade é a classe proxy, que repassa
exemplos a seguir. xy, que vai permitir que métodos sejam os métodos para o servidor (veja a sua
chamados através de um objeto local, implementação na unit DataServer.Cus-
Criando a aplicação cliente ASP.NET que se encarrega de invocar os métodos tomer.pas).
Passamos para a terceira camada da remotos via SOAP. Usando o SelectDS, executamos o mé-
nossa solução, vamos criar uma aplicação Desenhe uma interface semelhante a todo remoto e obtemos um DataSet de
Web usando ASP.NET. Inicie um novo mostrada na Figura 3 (já em tempo de retorno, trafegado em formato XML. A
projeto ASP.NET no Delphi dando o execução). Temos um botão e um TextBox seguir, colocamos esse DataSet em sessão
nome de “ClientWeb”. (“tbFind), destinados a executar a pesqui- e exibimos os dados no DataGrid.
O primeiro passo para acessar o servi- sa. Um DataGrid vai ser usado para exibir Quando o usuário selecionar um regis-
dor é adicionar uma Web Reference, opção os dados retornados. tro no DataGrid, pressionando o Select,
que pode ser acessada a partir de um Logo abaixo, três controles TextBox precisamos repassar os valores para os
clique de direita no nome do projeto no serão usados para permitir a edição TextBoxes que estão abaixo. Isso é feito no
Project Manager. Na caixa de diálogo que dos dados (outra opção seria permitir a evento SelectedIndexChanged do DataGrid
aparece digite o endereço do documento edição diretamente no DataGrid). Note (Listagem 7). Note que obtemos os valo-
WSDL do servidor, nesse caso: que o DataGrid tem um botão Select, para res a partir das células do DataGrid, outra
adicioná-lo acesse o Property Builder no opção seria obter a partir do DataSet.
http://localhost/DataServer/Customer.asmx?WSDL Object Inspector. No editor que aparece O código do Update (Listagem 8) captura
os dados editados nos TextBoxes e repassa-
Listagem 6. Código do botão Find os ao DataSet. Como o DataSet foi alterado,
precisamos enviar essas atualizações para
uses DataServer.Customer;
... o servidor, através do UpdateDS.
procedure TWebForm1.btnFind_Click(
sender: System.Object; e: System.EventArgs); Não precisamos enviar de volta todo o
var
ws: DataServer.Customer.TCustomer;
DataSet com todos os registros, somente
ds: DataSet; aqueles que foram alterados. Podemos
begin
ws := DataServer.Customer.TCustomer.Create(); dizer ao DataSet que precisamos somente
ds := ws.SelectDS(TextBox1.Text);
Session['ds'] := ds; dos registros alterados, através do méto-
DataGrid1.DataSource := ds;
DataGrid1.DataBind();
do GetChanges (pense nisso com um Delta
end; do ClientDataSet).
O código do Insert (Listagem 9) é bas-
Listagem 7. Evento SelectedIndexChanged
tante semelhante. Capturamos os valores
procedure TWebForm1.DataGrid1_SelectedIndexChanged( digitados nos TextBoxes e colocamos em
sender: System.Object; e: System.EventArgs);
begin um novo registro, representado no ADO.
tbCustomerID.Text :=
DataGrid1.SelectedItem.Cells[1].Text; NET pela classe DataRow (aqui é o nosso
tbFirstName.Text :=
DataGrid1.SelectedItem.Cells[2].Text; objeto row).
tbCompany.Text :=
DataGrid1.SelectedItem.Cells[3].Text;
Configuramos os campos do registro
end; (row) e o adicionamos na coleção de Rows do
primeiro DataTable do DataSet (veja que esse
Listagem 8. Código do botão Update
procedimento de inserção é bem diferente
procedure TWebForm1.btUpdate_Click(
sender: System.Object; e: System.EventArgs);
do modelo TDataSet da VCL). O próximo
var passo é enviar os dados ao servidor através
ws: DataServer.Customer.TCustomer;
ds: DataSet; do nosso método remoto InsertDS.
i: integer;
begin A Figura 3 mostra o exemplo em exe-
ds := Session['ds'] as DataSet; cução, usando um filtro para seleção dos
i := DataGrid1.SelectedIndex;
ds.Tables[0].Rows[i]['FirstName'] := registros.
tbFirstName.Text;
ds.Tables[0].Rows[i]['Company'] := tbCompany.Text;
ws := DataServer.Customer.TCustomer.Create();
ds := ds.GetChanges();
Conversão DataSet (ADO.NET) para
ws.UpdateDS(ds);
DataGrid1.DataSource := Session['ds'] as DataSet;
ClientDataSet
DataGrid1.DataBind(); Finalizada a interface Web da solução,
end;
vamos passar para a criação da aplicação

10 ClubeDelphi - Integrando aplicações .NET, Win32 e Mobile com SOAP e Web Services

Clube86.indb 10 02.07.07 15:00:03


BOA IDÉIA

cliente desktop, em Delphi 7 Win32. Aqui Listagem 9. Código do botão Insert


temos um grande problema, relativo à inte-
procedure TWebForm1.btInsert_Click(
roperabilidade ADO.NET e ClientDataSet. sender: System.Object; e: System.EventArgs);
var
Como dito anteriormente, nossa camada ws: DataServer.Customer.TCustomer;
servidora pode ser acessada por qualquer ds: DataSet;
row: DataRow;
plataforma. Porém, o método de seleção begin
ds := Session['ds'] as DataSet;
devolve DataSets em XML no formato do row := ds.Tables[0].NewRow();
row['FirstName'] := tbFirstName.Text;
ADO.NET. E o ClientDataSet do Delphi 7 row['Company'] := tbCompany.Text;
não consegue carregar um XML nesse ds.Tables[0].Rows.Add(row);
ws := DataServer.Customer.TCustomer.Create();
formato. Bom, temos várias possíveis ds := Session['ds'] as DataSet;
ds := ds.GetChanges();
soluções para o problema. ws.InsertDS(ds);
DataGrid1.DataSource := Session['ds'] as DataSet;
Poderíamos na aplicação cliente trans- DataGrid1.DataBind();
formar o XML do ADO.NET em um end;

DataPacket, usando o utilitário XML


Mapper e componentes de transformação
da paleta Data Access (veja meu artigo
da edição 36). Poderíamos também usar
um XmlDocument para ler o documento
e fazer o mapeamento no braço para o
ClientDataSet. Ou ainda deixar de lado o
ClientDataSet e usar controles não data-
aware para trabalhar no XML.
Dentre as várias opções, a minha es-
colha foi a mais ousada e livra o cliente
desse processamento extra (que é um dos
objetivos do desenvolvimento multica-
madas). Eu fiz o próprio servidor devol-
ver um formato aceito pelo ClientDataSet,
nesse caso o DataPacket.
Esse procedimento é trabalhoso e en-
volve um profundo conhecimento do
formato usado pelo DataSnap. Para um
bom entendimento das explanações e do
código a seguir, sugiro que o leitor obser-
ve com cuidado o documento DataPacket
em XML mostrado na Figura 4.
Note que o DataPacket tem duas seções
principais, MetaData e RowData. Uma
Figura 3. Cliente ASP.NET fornece uma interface Web
define os campos e tipos, e a outra contém
os registros com os dados. É exatamente
esse packet que precisamos montar “no
braço”, com base no DataSet.
Vamos então precisar modificar o servi-
dor (abra o projeto novamente) para incluir
um novo método, responsável por devol-
ver os dados em formato XML DataPacket.
Chamei o método de SelectDP (Listagem
10). O sufixo “DP” no nome do método
vem obviamente de “DataPacket”.
Ele não faz muita coisa, na verdade cha-
ma o método SelectDS (que retorna um
Figura 4. Gerando um DataPacket no "braço"
DataSet ADO.NET) e “passa a bola” para
uma classe que criei, vista a seguir.
Como temos um DataSet do ADO.NET e
precisamos retornar um XML DataPacket a
ser reconhecido pelo ClientDataSet, preci-

Edição 86 - ClubeDelphi 11

Clube86.indb 11 02.07.07 15:00:03


samos fazer uma conversão. Resolvi então Listagem 10. Método para devolver DataPackets
criar uma classe que faz esse mapeamento,
uses System.Xml ...
capturando cada definição de campo e public
registro do DataTable do DataSet e gerando [WebMethod]
function SelectDP(Filter: string): XmlDocument;
um XML no formato DataPacket. ...
uses AdoMidas; //criada a seguir
A classe foi chamada, apropriadamente, ...
function TCustomer.SelectDP(
de “AdoMidasConverter”, na unit AdoMi- Filter: string): XmlDocument;
das (Listagem 11). var
Dst: DataSet;
É muito fácil gerar documentos XML no begin
Dst := SelectDS(Filter);
.NET Framework. A classe XmlTextWriter Result := AdoMidasConverter.DataSetToMidas(Dst);
end;
possui métodos bastante intuitivos para ge-
rar o documento desejado. A primeira parte Listagem 11. Classe para converter DataSets do ADO.NET em XML DataPackets do ClientDataSet
do código gera a seção Metadata. Usando o
// Classe para interoperabilidade ADO.NET e MIDAS – Guinther Pauli
novo comando for in do compilador percor- unit AdoMidas;
interface
remos todas as colunas do DataTable e vamos uses
adicionando aos Fields do DataPacket. System.Data, System.Xml, System.IO, System.Text;
type
Durante a transformação foi necessário AdoMidasConverter = class
public
fazer um mapeamento do tipo do campo. class function DataSetToMidas(
Dst: DataSet): XmlDocument;
Por exemplo, o tipo System.String do .NET end;
foi mapeado para string. O System.Int32 implementation
(de 32 bits) foi mapeado para “i4” (inteiro { AdoToMidasConverter }
class function AdoMidasConverter.DataSetToMidas(
de 4 bytes). Converti aqui somente os Dst: DataSet): XmlDocument;
var
campos utilizados neste exemplo, fique a writer: XmlTextWriter;
vontade para mapear os demais tipos de Mem: MemoryStream;
Col: DataColumn;
dados que desejar, como datas e floats. ColType: string;
ColName: string;
A próxima seção é a RowData. Diferente Row: DataRow;
Xml: XmlDocument;
do XML do ADO.NET, que representa st: string;
cada valor de um campo como um ele- begin
Mem := MemoryStream.Create();
mento no documento XML, o DataPacket writer := XmlTextWriter.Create(Mem, System.Text.ASCIIEncoding.Create);
writer.Formatting := Formatting.Indented;
do Delphi representa valores de campos writer.Indentation := 4;
writer.WriteStartDocument(True);
como atributos. Essa foi uma boa idéia da writer.WriteStartElement('DATAPACKET');
equipe que projetou o MIDAS, pois ajuda writer.WriteAttributeString('Version','2.0');
writer.WriteStartElement('METADATA');
a reduzir o tamanho do XML e facilita o writer.WriteStartElement('FIELDS');
for Col in Dst.Tables[0].Columns do
tráfego no HTTP. begin
writer.WriteStartElement('FIELD');
Os dados do documento gerado estão writer.WriteAttributeString('attrname', Col.ColumnName);
guardados em um MemoryStream, usado if Col.DataType.ToString() = 'System.String' then
ColType := 'string';
pelo XmlTextWriter, como especificamos if Col.DataType.ToString() = 'System.Int32' then
ColType := 'i4';
no construtor. O último passo consiste em writer.WriteAttributeString('fieldtype', ColType);
if ColType = 'string' then
obter esses dados e jogar em um XmlDo- writer.WriteAttributeString('WIDTH', '255');
cument, que é a classe do .NET utilizada { mapear demais campos aqui }
writer.WriteEndElement;
para representar documentos XML. end;
writer.WriteEndElement; { fields }
Já que o XmlDocument é serializável, o writer.WriteStartElement('PARAMS');
writer.WriteAttributeString('DEFAULT_ORDER', '1');
devolvemos como valor de retorno do writer.WriteAttributeString('PRIMARY_KEY', '1');
SelectDP, que está pronto para receber writer.WriteEndElement; { params }
writer.WriteEndElement; { metadata }
chamadas SOAP vindas do ClientDataSet writer.WriteStartElement('ROWDATA');
for Row in Dst.Tables[0].Rows do
(lembre-se de dar um Build no servidor begin
writer.WriteStartElement('ROW');
antes de prosseguir). for Col in Dst.Tables[0].Columns do
begin
ColName := Col.ColumnName;
Criando a aplicação cliente Delphi 7 writer.WriteAttributeString(ColName, Row[ColName].ToString());
end;
(Win32) writer.WriteEndElement;
end;
Vamos criar a segunda interface clien- writer.WriteEndElement;
te para a nossa solução, uma aplicação writer.WriteEndDocument();
writer.Close;
Desktop escrita em Delphi 7. Incie uma st := Encoding.UTF8.GetString(Mem.ToArray);
Xml := XmlDocument.Create();
nova aplicação VCL e salve todos os ar- Xml.LoadXml(st);
Result := Xml;
quivos. Dê ao projeto o nome de “Clien- end;
tWin32”. end.

12 ClubeDelphi - Integrando aplicações .NET, Win32 e Mobile com SOAP e Web Services

Clube86.indb 12 02.07.07 15:00:09


O primeiro passo é fazer referência ao
servidor, semelhante ao que fizemos no
Delphi for .NET através da opção Add Web
Reference. No Delphi 7, essa opção está
em File>New>Other>WebServices>WSDL
Importer. Informe o caminho do docu-
mento WSDL do nosso servidor, para
este exemplo é:
http://localhost/DataServer/Customer.asmx?WSDL
Figura 5. Cliente em Delphi 7 Win32
Clique em Next e Finish. Salve o novo ar-
quivo criado como “Customer.pas”. O có-
digo gerado automaticamente pelo Delphi Listagem 12. Obtendo dados em DataPacket do servidor
é como uma “classe proxy”, que fornece os uses Customer;
mecanismos necessários para invocar os ...
procedure TForm1.BtnFindClick(Sender: TObject);
métodos remotos através do SOAP. var
Xml: string;
Agora desenhe uma interface seme- Mem: TStream;
begin
lhante à mostrada na Figura 5 (já em Xml := GetTCustomerSoap().
execução). Nela temos dois botões (Find e SelectDP(EdtFind.Text);
Mem := TStringStream.Create(Xml);
Update), um Edit para que seja informado cds.LoadFromStream(Mem);
Mem.Free();
o parâmetro (“EdtFind”) e um DBGrid. end;
Colocamos também um ClientDataSet
(“cds”) e um DataSource. Relacione os Listagem 13. Atualizando os dados
componentes apropriadamente.
procedure TForm1.BtnApplyClick(
Para acessar o servidor SOAP, usarí- Sender: TObject);
amos normalmente um HTTPRIO da begin
cds.First;
paleta WebServices. A boa nova é que a while not cds.Eof do
begin
unit gerada pelo Delphi já contém uma if cds.UpdateStatus = usModified
then
função pronta que instancia e configura begin
esse objeto para nós, chamada aqui de GetTCustomerSoap().Update(
cds.FieldByName('CustomerId').
GetTCustomerSoap. AsInteger,cds.FieldByName('FirstName').
AsString,cds.FieldByName('Company').
Na Listagem 12 temos o código do AsString);
end;
Find, que faz uso dessa função. Nele if cds.UpdateStatus = usInserted
chamamos o SelectDP, que no servidor then
begin
vai fazer o select no banco de dados, ob- GetTCustomerSoap().Insert(
cds.FieldByName('FirstName').
ter um DataSet do ADO.NET, converter AsString,cds.FieldByName('Company').
AsString);
em XML DataPacket (através do nosso end;
“engine” AdoMidas) e devolver para a // trate a exclusão aqui
cds.Next;
aplicação cliente. end;
end;
Para carregar o XML de retorno no
ClientDataSet, não é necessário usar o
LoadFromFile, o que nos obrigaria a sal-
var o documento no disco. Usamos um
TStringStream para obter o conteúdo de métodos padrão já definidos no servidor,
retorno SOAP (o DataPacket) e a seguir o como update e insert, e apenas invocar
método LoadFromStream do ClientDataSet esses métodos.
é chamado para carregar os dados direta- Foi o que fiz na Listagem 13. Foi neces-
mente da memória. Execute a aplicação e sário examinar o UpdateStatus de cada re-
faça alguns testes. gistro do ClientDataSet. Se for encontrada
Para atualizar os dados obviamente alguma modificação, o método apropria-
não adianta chamar o ApplyUpdates do do é invocado no servidor, atualizando
ClientDataSet. Temos aqui novamente corretamente os dados no BD.
várias possíveis soluções, como criar um E com isso temos uma aplicação cliente
método no servidor que recebesse o delta desktop 100% funcional, escrita em D7,
e transformasse de volta em um DataSet acessando uma camada intermediária
do ADO.NET. Outra solução seria usar os em .NET, comunicando-se através de

Edição 86 - ClubeDelphi 13

Clube86.indb 13 02.07.07 15:00:09


SOAP. Uma combinação perfeita que cativos para Compact Framework. Web Services para se comunicar com o
pode garantir o sucesso de seus projetos O que existe são um compilador e bi- servidor, nesse caso, é necessário que o
Delphi. bliotecas, o que torna o desenvolvimento dispositivo tenha acesso a mesma rede
possível, mas se você tiver paciência. onde se encontra o servidor, por exemplo,
Criando a aplicação Mobile (Pocket Coloquei na seção Links várias referên- via Wi-Fi, ou acesso Internet que permite
PC) cias que sugiro a leitura antes de iniciar conectar o servidor WS. Uma alternativa
Antes de iniciar qualquer exemplo, o desenvolvimento com CF. Ali constam ao modelo conectado seria criar uma ver-
gostaria de deixar bem claro aqui que, o informações sobre configuração, compi- são desconectada (gravando os dados em
desenvolvimento para aplicativos móveis lação, uso de emuladores etc. Não entra- XML local ou usando o SQL Server ).
com o Delphi ainda é bem limitado e rei em todos esses detalhes aqui. No BDS, inicie um nova aplicação Del-
complicado. Não existem designers, nem phi for .NET do tipo Windows Forms,
ferramentas apropriadas para criar apli- Nota: Para uma introdução ao desen- chamando o projeto de “ClientMobile”.
volvimento com Delphi e Compact Fra- Adicione uma Web Reference da mesma
www.devmedia.com.br/clubedelphi/portal.asp mework, sugiro ao leitor ver a edição 67. forma que foi feito para a aplicação ASP.
Acesse agora o mesmo o portal do assinante ClubeDelphi e assista a NET. Uma diferença aqui é que você
aulas de Jefferson Junglaus que mostra como trabalhar com o Compact Nesta última etapa da nossa solução, deve colocar o endereço IP do adaptador
Framework no Delphi. criaremos então um cliente baseado em Loopback Adapter ao invés de localhost no
www.devmedia.com.br/articles/listcomp.asp? aplicativos móveis, mais especificamen- endereço do servidor.
txtsearch=Compact+Framework+no+Delphi
te o Pocket PC. O aplicativo utilizará
Nota: Você deve adicionar um Loopback
Adapter para testar as aplicações com CF.
Listagem 14. Código do formulário para CF
Isso pode ser feito a partir do item Adi-
uses DataServer.Customer
...
cionar/Remover Hardware>Adaptadores
public de Rede. A seguir atribua um IP fixo para
ds: DataSet;
ws: DataServer.Customer.TCustomer; o mesmo. Mais informações sobre esse
...
procedure TWinForm1.TWinForm1_Load( procedimento podem ser encontradas
sender: System.Object; e: System.EventArgs);
begin
na seção Links.
ws := DataServer.Customer.TCustomer.Create();
end;
procedure TWinForm1.btFind_Click( Desenhe o formulário mostrado na
sender: System.Object; e: System.EventArgs);
begin Figura 6. Temos três TextBoxes (“tbFind”,
ds := ws.SelectDS(tbFind.Text);
DataGrid1.DataSource := ds.Tables[0];
“tbFirstName” e “tbCompany”), dois bo-
DataGrid1_CurrentCellChanged(nil,nil); tões (Find e Update) e um DataGrid, além
end;
procedure TWinForm1.DataGrid1_CurrentCellChanged(sender: System.Object; e: System.EventArgs); de alguns Labels.
var
row: DataRow; A Listagem 14 mostra o código com-
begin
row := ds.Tables[0].Rows[dataGrid1.CurrentRowIndex];
pleto do formulário. No evento Load do
tbFirstName.Text := row['FirstName'].ToString(); formulário instanciamos uma variável
tbCompany.Text := row['Company'].ToString();
end;
procedure TWinForm1.btUpdate_Click(sender: System.Object; e: System.EventArgs);
var
row: DataRow;
begin
row := ds.Tables[0].Rows[dataGrid1.CurrentRowIndex];
row['FirstName'] := tbFirstName.Text;
row['Company'] := tbCompany.Text;
ws.UpdateDS(ds);
end;

Listagem 15. Arquivo BAT para compilar aplicações CF

set _DCCILPATH_="c:\program files\borland\BDS\4.0\bin\dccil.exe"


set _CFUNITS_="c:\program files\borland\BDS\4.0\lib\cf"
set _PROJECTNAME_=%1
set _PROJECTDIR_=%2
set _BDSDIR_=..\BDS\4.0\Bin
path %_BDSDIR_%;%PATH%
SHIFT
SHIFT
:LOOP
IF "%1" == "" GOTO END
set _PROJECTDIR_=%_PROJECTDIR_% %1
SHIFT
GOTO LOOP
:END
cd %_PROJECTDIR_%
rem del *.dcuil
%_DCCILPATH_% "%_PROJECTDIR_%%_PROJECTNAME_%" -u%_CFUNITS_% -luSystem.Windows.Forms
-luSystem.Data
Pause
Figura 6. Aplicação Compact Framework

14 ClubeDelphi - Integrando aplicações .NET, Win32 e Mobile com SOAP e Web Services

Clube86.indb 14 02.07.07 15:00:11


BOA IDÉIA

chamada ws (declarada no form), que configurações mostradas a seguir:


representa uma referência ao Web Servi- Title = Delphi .NET CF Preview Compiler
Program = C:\Program Files\Borland\BDS\4.0\
ce. No botão Find chamamos o SelectDS Bin\cf.bat
do servidor, que devolve um DataSet do Parametres = $SAVEALL $NAMEONLY($PROJECT).dpr
$PATH($PROJECT)
ADO.NET que é usado como DataSource
do DataGrid. Crie o arquivo BAT da Listagem 15
O evento CurrentCellChanged do Data- dentro do diretório Bin do Delphi, certi-
Grid verifica que registro foi selecionado ficando-se que os caminhos mostrados
e repassa os valores para os TextBoxes. O sejam o mesmo da sua máquina. O nome
Update simplesmente captura os valores do arquivo deve ser “cf.bat”.
dos TextBoxes e repassa ao WebService, A partir do menu Tools, acesse o novo
para atualização. item criado e uma janela de prompt deve
Para não estender demais o exemplo aparecer indicando o processo de compi-
não codifiquei a inserção e exclusão, é lação da aplicação CF. Se algo der errado,
semelhante ao Update. observe a mensagem de erro do compila-
dor e verifique o seu código-fonte.
Compilando para Compact O último passo é fazer o deploy para
Framework um emulador (caso não tenha um Pocket
Vamos precisar fazer alguns ajustes na PC). Existem vários emuladores que você
aplicação para que ela possa ser compila- pode utilizar para testar a aplicação cria-
da para CF. O primeiro passo é no Project da (para download de emuladores, veja
Figura 7. Aplicação Delphi rodando no Pocket acessando o
Manager, no item References, adicionar a seção Links). WS e BD
uma referência ao assembly System. Nesse exemplo foi utilizado o Pocket
Windows.Forms.DataGrid.dll (localize esse PC 2002 Emulator. A aplicação Delphi
assembly no seu disco). rodando no Pocket PC, acessando nosso recursos oferecidos pelo ambiente Web
O próximo passo é retirar as seguintes Web Service, aparece na Figura 7. (como distribuição) aliados às facilidades
linhas do arquivo do projeto (Project>View No meu caso, para fazer o deploy da apli- do desenvolvimento RAD e desktop do
Source): cação para o Pocket PC Emulator 2002, Delphi. Tudo combinado em harmonia
{$R 'WinForm1.TWinForm1.resources' 'WinForm1.resx'} compartilhei primeiramente o diretório em uma única arquitetura, que conversa
...
[STAThread]
onde compilei a aplicação. Depois sim- a mesma linguagem: XML.
plesmente iniciei o dispositivo e no File Na próxima edição vamos continuar
A seguir, no arquivo do formulário, Explorer fiz o mapeamento do diretório explorando o universo do desenvolvi-
algumas alterações devem ser feitas no do servidor usando a seguinte sintaxe mento multicamadas, onde veremos
InitializeComponent do formulário, que "\\NomeDaMaquina\\DiretorioCompar- como construir uma camada cliente Web
contém todo o código para inicialização tilhado". Copiado o EXE, foi só executar desenvolvida com ASP.NET que faz aces-
dos controles. Essas alterações são: e conferir o resultado. so a um servidor de aplicação DataSnap
• Comentar a linha Self.SuspendLayout; Comprove a flexibilidade da nossa solu- / COM+.
• Comentar a linha Self.AutoScaleBase ção executando os três aplicativos cliente Sucesso nos projetos com XML Web Ser-
Size...; simultaneamente, vendo que a alteração vices, encontro vocês na próxima edição.
• Comentar a linha Self.ResumeLayout(False); nos dados feita em um cliente é automa- Grande abraço!
• Comentar todas as atribuições às ticamente refletida no outro.
propriedades Name e TabIndex de todos
os controles; Conclusão
• No DataGrid manter somente as linhas O desenvolvimento de aplicações em
que configuram o Location, Size e Text, as de- camadas com SOAP, XML e Web Services, Links
mais linhas devem ser todas comentadas. oferece inúmeros benefícios: independên- SQL Server 2005 Express
cia de plataforma, linguagem e dispositi- www.microsoft.com/sql/editions/express
Nota: Uma simples modificação em um vo, acesso remoto de uma variedade de Artigo sobre Compact Framework com
controle no form designer fará todas ambientes através da Internet, separação Delphi (plug-ins, emuladores)
essas alterações serem desfeitas, então do acesso a dados / lógica de negócio das dn.codegear.com/article/33507
certifique-se que o mesmo esteja total- aplicações clientes, que cuidam apenas da Usando emuladores
www.nsbasic.com/ce/info/technotes/TN23.htm
mente pronto e configurado antes de UI, entre outras.
aplicar as configurações. A aplicação apresentada neste artigo Loopback Adapter
www.microsoft.com/technet/prodtechnol/
vai permitir a você oferecer ao seu cliente virtualserver/2005/proddocs/vs_operate_
Acesse agora o menu Tools>Configure uma solução bastante robusta, flexível using_loopback.mspx?mfr=true
Tools. Adicione um novo item com as e inovadora, desfrutando de todos os

Edição 86 - ClubeDelphi 15

Clube86.indb 15 02.07.07 15:00:12


Dicas de DataGrid

D
esde a minha primeira experi- em conexões com o banco ou formata-
ência com o ASP.NET, um dos ções do controle. Temos ótimos artigos
controles que mais utilizei foi o publicados (edição 66), mostrando essas
DataGrid, pela sua facilidade em mostrar características, bem como temos vídeos
os dados no formato de tabela. Adicionar aulas no Portal do Assinante. Apenas
imagens, controles e formatações de indicaremos quando usar FbCommand
acordo com determinado campo, são as ou DataSet.
primeiras funcionalidades que deseja- Formate o DataGrid na opção que deseja
mos nesse controle. O DataGrid é muito e conecte ao banco de dados Employee que
customizável e neste artigo, veremos acompanha o Firebird. Você pode usar
algumas dicas úteis para a utilização do outro banco de sua preferência. Para este
mesmo em suas aplicações Web. exemplo, use os dados da tabela Customer
(selecione apenas alguns campos).
Adicionando controles Acesse a propriedade Columns do Data-
A funcionalidade de templates do ASP. Grid, adicione um campo do tipo Template
NET ajuda em muito a customização Column e clique em OK (Figura 1).
do DataGrid para a adição de controles. Clique com o botão direito no DataGrid
Luciano Pimenta Basta criar um coluna do controle do tipo e acesse a opção Columns[0]. Na seção
(lucianopimenta@clubedelphi.net) Template Column e adicionar os controles ItemTemplate, adicione um CheckBox.
é Técnico em Processamento de Dados, Editor necessários. Feche o editor, rode a aplicação e veja os
Técnico da Revista ClubeDelphi, .net Maga-
Neste exemplo, vamos trabalhar o Che- CheckBoxes adicionados ao DataGrid.
zine e WebMobile. Editor de vídeo aulas do
Portal DevMedia (www.devmedia.com.br). ckBox. Crie uma nova aplicação Web no Para saber as linhas que foram “sele-
Palestrante da 4ª edição da Borland Conference Delphi 2006. Na WebForm1.aspx adicione cionadas” com o CheckBox, adicione um
(BorCon). um DataGrid. Não vamos no concentrar botão e um TextBox na página. No Click do

16 ClubeDelphi - Dicas de DataGrid

Clube86.indb 16 02.07.07 15:00:13


AS P.NET

botão adicione o código da Listagem 1.


O código está comentando para um
fácil entendimento. O que fizemos foi
criar uma variável do tipo StringBuil-
der (já adicionando um texto inicial),
percorrer todas as linhas do DataGrid
e procurar o controle CheckBox com o
FindControl.
Esse método retorna um Control, então
fizemos um typecast para CheckBox. Vale
salientar que o parâmetro aceito pelo
FindControl é o nome do controle. Se o
controle não for nulo (foi encontrado) e
estiver marcado (Checked), então adiciona-
mos na sb os valores das colunas 1 e 2.

Nota: As colunas do DataGrid começam


com 0, sendo que a do Template Column
é contada também. Figura 1. Adicionando um Template Column no DataGrid

Rode a aplicação, marque alguns Che- Listagem 1. Obtendo a linha com o CheckBox marcado

ckBoxes e clique no botão. Como mostra uses System.Text;


...
a Figura 2, o TextBox exibe as linhas procedure TWebForm1.Button1_Click(sender: System.Object; e: System.EventArgs);
“selecionadas”. var
sb: StringBuilder;
row: DataGridItem;
ch: Control;
Master/Detail begin
{ Cria um StringBuilder }
Criar relacionamentos master/detail sb := StringBuilder.Create(‘* Itens Selecionados *’);
no DataGrid é muito simples. Crie uma
{ Percorre todos os itens do DataGrid }
nova página e adicione dois DataGrids. for row in DataGrid1.Items do
begin
Para o primeiro, criei uma conexão com a { procura pelo CheckBox }
ch := row.FindControl(‘CheckBox1’);
tabela Department. Para o segundo acessei
os dados da tabela Employee com uma { Se achou e esta marcado }
if (ch <> nil) and (ch as CheckBox).Checked then
cláusula where no campo DEPT_NO. No begin
{ Adiciona na var sb os campos do DataGrid }
Page_Load estou carregando os dados do sb.Append(#13);
sb.Append(row.Cells[1].Text + ‘ - ‘ + row.Cells[2].Text);
DataGrid mestre, conforme o código da end;
Listagem 2. end;
TextBox1.Text := ‘’;
Código simples para quem está fami- TextBox1.Text := sb.ToString;
end;
liarizado com ASP.NET, onde abrimos a
conexão com o banco, repassamos para Listagem 2. Mostrando os dados do DataGrid master
o DataSource do DataGrid o DataReader
if not IsPostBack then
e chamamos o DataBind do controle. Por begin
FbConnection1.Open;
fim, fechamos a conexão com o banco. try
A propriedade DataKeyField é muito DataGrid1.DataSource := FbMaster.ExecuteReader;
DataGrid1.DataKeyField := ‘DEPT_NO’;
importante, pois ela dirá o campo chave DataGrid1.DataBind;
finally
que estamos selecionando. Adicione no FbConnection1.Close;
end;
DataGrid master uma coluna do tipo Se- end;
lect. No evento ItemCommand filtraremos
o segundo DataGrid com o código da Listagem 3. Filtro para o DataGrid Detail

Listagem 3. if e.CommandName = ‘Select’ then


begin
No evento estamos passando para o FbConnection1.Open;
parâmetro do FbCommand a chave da try
FbDetail.Parameters.Add(‘@DEPT_NO’,
linha selecionada do DataGrid master. O FbDbType.Integer, 4, ‘DEPT_NO’).Value := DataGrid1.DataKeys[e.Item.ItemIndex];
DataGrid2.DataSource := FbDetail.ExecuteReader;
restante é igual ao exemplo anterior. Rode DataGrid2.DataBind;
finally
a aplicação e selecione um departamento FbConnection1.Close;
para visualizar os funcionários do mesmo end;
end;
(Figura 3).

Edição 86 - ClubeDelphi 17

Clube86.indb 17 02.07.07 15:00:19


Listagem 4. Carregando o DataSet em memória Como você deve ter notado, quando o
if not IsPostBack then usuário clica no DataGrid master estamos
begin
{ Preenche o DataSet } abrindo e fechando uma conexão com o
FbMaster.Fill(dsMasterDetail, ‘Department’);
{ Indica a fonte de dados para o DataGrid master }
banco. Para poucos dados (como é nosso
DataGrid1.DataSource := dsMasterDetail.Tables[‘Department’]; exemplo) a performance não é afetada,
DataGrid1.DataKeyField := ‘DEPT_NO’;
DataGrid1.DataBind; mas imagine uma quantidade maior de
end;
dados.
Vamos fazer um exemplo agora usan-
do DataSet, pois ele coloca os dados em
memória e para filtrar usamos os seus
métodos. O código da Listagem 4 deve
ser adicionado no Page_Load (crie um
novo Web Form). Nesse código estamos
preenchendo o DataSet com uma consulta
à tabela Department.
O dsMasterDetail é um DataSet adicio-
nado no design da página. No evento
ItemCommand do DataGrid1 colocaremos
o código que fará o filtro do DataTable
detail, usando o código da Listagem 5.
Como podemos ver, filtramos os dados
do DataTale usando o DefaultView e repas-
samos os dados para o DataGrid2. Execute
a aplicação e veja o resultado (Figura 4).
Para otimizar o resultado, você pode co-
locar o DataSet em cache ou session.

Trabalhando com imagens


Para este exemplo, imagine que tenha-
mos imagens salvas na pasta da apli-
cação, chamada imagens, e na tabela do
Figura 2. Obtendo os CheckBox marcados banco tenhamos apenas o nome dela, ou
seja, as imagens não ficam salvas no ban-
co mas no disco, o que é melhor quando
se trata de aplicações Web.
O processo para exibir a imagem em
um DataGrid é simples. Usando o campo
que guarda o nome da imagem, você
deve abrir o editor de colunas e confi-
gurar a propriedade DataFormatString,
adicionando o seguinte valor: “<img
src=imagens/{0}>”. Rode a aplicação e
veja as imagens sendo mostradas no
DataGrid (Figura 5).

Figura 3. Master/Detail com dois DataGrids

18 ClubeDelphi - Dicas de DataGrid

Clube86.indb 18 02.07.07 15:00:23


AS P.NET

www.devmedia.com.br/clubedelphi/portal.asp
Listagem 5. Filtrando os dados do DataTable
Acesse agora o mesmo o portal do assinante ClubeDelphi e assista a uma if e.CommandName = ‘Select’ then
vídeo aula de Luciano Pimenta que mostra como fazer um master/detail begin
{ Preenche o DataSet }
no DataGrid usando somente um controle DataGrid. FbDetail.Fill(dsMasterDetail, ‘Employee’);
www.devmedia.com.br/articles/viewcomp.asp?comp=2050 { Filtra os dados do DataTable com uma expressão }
dsMasterDetail.Tables[‘Employee’].DefaultView.RowFilter :=
‘DEPT_NO = ‘+DataGrid1.DataKeys[e.Item.ItemIndex].ToString;
{ Repassa para o DataGrid Details a fonte de dados }
DataGrid2.DataSource := dsMasterDetail.Tables[‘Employee’].DefaultView;
www.devmedia.com.br/clubedelphi/portal.asp DataGrid2.DataBind;
end;
Acesse agora o mesmo o portal do assinante ClubeDelphi e assista a uma Listagem 6. Criando um método que retorne o nome do arquivo
vídeo aula de Luciano Pimenta que mostra como salvar imagens em
function TWebForm1.GetImagem(aSexo: string): string;
diretórios e exibi-las no DataGrid. begin
if aSexo = ‘M’ then
www.devmedia.com.br/articles/viewcomp.asp?comp=2853
Result := ‘~/imagens/M.bmp’
else if aSexo = ‘F’ then
Result := ‘~/imagens/F.bmp’
end;
Imagens de acordo com Listagem 7. Indicando a imagem de acordo com o campo SEXO
determinada condição var
Em uma tabela de clientes, certamente, aControl: Control;
begin
possuímos um campo indicador do sexo aControl := e.Item.Cells[2].FindControl(‘Image1’);
do mesmo. Que tal mostrar no DataGrid if aControl <> nil then
(aControl as Image).ImageUrl := GetImagem(e.Item.Cells[2].Text);
a listagem dos clientes e de acordo com o end;

sexo mostrar uma imagem diferente?


Listagem 8. Colocando um somatório no rodapé
Criei uma tabela de clientes, onde existe
um campo chamado SEXO, salvando as if e.Item.ItemType = ListItemType.Footer then
begin
opções “M” ou “F”. Vou usar DataSet e.Item.Cells[3].Text := ‘Total: ‘+
System.&String.Format(‘{0:c}’, DataSet1.Tables[0].Rows[0][‘TOTAL’]);
neste exemplo, mas você pode adaptá- end;
lo facilmente para usar DataReader. O
código da Listagem 6 vai retornar o tipo
da imagem de acordo com o valor do
parâmetro.
O código anterior retorna o nome do
arquivo, salvo na pasta imagens de acordo
com o valor passado como parâmetro. No
editor de colunas do DataGrid, adicione
uma do tipo Template Column. Acesse o
editor de templates, adicione um Image
e feche o mesmo. No evento ItemData-
Bound do DataGrid adicione o código da
Listagem 7.
Com esse código, passamos para
propriedade ImageURL do controle, o
respectivo arquivo de imagem que deve
ser mostrado. Rode a aplicação e veja as
imagens sendo mostradas de acordo com
o sexo do cliente (Figura 6).
Se desejar, você pode desmarcar a opção
Visible no Property Builder para o campo
Sexo não ser mostrado no DataGrid, já que
existe a figura indicativa.

Somatório no rodapé
Se o DataGrid possui um campo com
valores, certamente você gostaria de
mostrar um total no rodapé da página. E
essa técnica é bastante simples. Primei-
ramente, altere para True a propriedade
ShowFooter do DataGrid. Figura 4. Master/detail usando DataSet e filtro

Edição 86 - ClubeDelphi 19

Clube86.indb 19 02.07.07 15:00:27


Figura 5. Mostrando imagens da aplicação no DataGrid Figura 6. Mostrando imagens de acordo com o valor do campo SEXO

Neste exemplo, vamos utilizar um Da-


taSet com um “campo calculado”. Após
configurar o DataSet, adicione uma nova
coluna com o nome de TOTAL do tipo
System.Decimal. No Page_Load adicione o
seguinte código:
FbDataAdapter1.Fill(DataSet1);
DataSet1.Tables[0].Columns['TOTAL'].
Expression := 'SUM(TOTAL_VALUE)';
DataGrid1.DataSource := DataSet1;
DataGrid1.DataBind;

Veja que estamos passando para a colu-


na TOTAL a soma do campo TOTAL_VA-
LUE, usando a propriedade Expression da
coluna. Acesse o evento ItemDataBound e
adicione o código da Listagem 8.
Verificamos primeiramente se estamos
no rodapé do DataGrid e depois coloca-
mos na célula o valor total (Figura 7).

Conclusão
Vimos neste artigo algumas dicas avan-
çadas para a utilização do DataGrid, um
Figura 7. Mostrando o somatório no DataGrid
dos controles mais utilizados em aplica-
ções ASP.NET.
Para quem quiser avançar, que tal co- www.devmedia.com.br/clubedelphi/portal.asp

locar todas essas dicas em prática, esten-


Acesse agora o mesmo o portal do assinante ClubeDelphi e assista a uma
dendo o controle padrão para um Data- vídeo aula de Luciano Pimenta que mostra como criar um somatório no
Grid turbinado. Fica aí a dica! Um grande rodapé do DataGrid.
abraço a todos e até a próxima. www.devmedia.com.br/articles/viewcomp.asp?comp=2648

20 ClubeDelphi - Dicas de DataGrid

Clube86.indb 20 02.07.07 15:00:29


Clube86.indb 21 02.07.07 15:00:47
Delphi 2007 em Detalhes

D
urante todo o mês de Março até 2006, porém a página inicial, chamada
o início de Abril, a CodeGear, de Home pela CodeGear, contém um item
nova divisão da Borland criada bastante prático: os favoritos. Nesse item
para atender 100% dos desenvolvedores é possível adicionar os projetos que são
e responsável pelo desenvolvimento do mais utilizados no dia a dia e até criar
Delphi, JBuilder, C++ Builder e outras fer- grupos para o acesso rápido.
ramentas da empresa, esteve mostrando Após a abertura ou criação de algum pro-
as novidades para o ano. jeto é gravada uma referência do mesmo na
Entre elas estão o Delphi for PHP, fer- seção Recently Opened Projects e ao lado de
ramenta para desenvolvimento de aplica- cada projeto aparece o link (Make me a Fa-
ções com PHP, e a nova versão do Delphi vorite). Basta clicar nesse link que o mesmo
para Win32, o Delphi 2007. Estive no será adicionado a seção My Favorites.
evento e pude ver, testar e comprovar de Nessa última encontramos as opções
perto os novos recursos da ferramenta. Show Favorites e Manage Favorites. Clican-
Neste artigo veremos as principais novi- do em Manage Favorites observamos que
dades e faremos alguns exemplos pra de- ao centro da página inicial é mostrada
monstrar os novos recursos disponíveis. uma nova seção: Create New Favorites
Group e nela a opção Group Title. Para criar
Adriano Santos Nota: Para uma introdução ao Delphi um novo grupo basta digitar o nome dele
(artes@doiscliques.com) 2007, consulte a edição 84. e clicar em Create.
é desenvolvedor Delphi desde 1998. Professor Instantaneamente seu novo grupo
e programador PHP. Bacharel em Comunicação
Social pela Universidade Cruzeiro do Sul, SP. É
Favoritos aparecerá na seção Manage Your Favorite
colunista e membro da Comissão Editorial da Logo que iniciamos a ferramenta no- Projects logo acima. Desse momento em
revista ClubeDelphi. tamos uma leve semelhança com o BDS diante você poderá mover o atalho para

22 ClubeDelphi - Delphi 2007 em Detalhes

Clube86.indb 22 02.07.07 15:01:05


NOVIDADES

seu projeto apenas clicando em Move de projeto abertas clique em Build Events. ser utilizados e mesclados entre si para
e escolhendo o grupo que deverá ser Nesse item encontramos dois grupos criação de pequenas tarefas em lote.
incluído. de opções: Pre-Build e Post-Build. Cada Para as opções Compiler, Compiler Mes-
Ainda falando de favoritos, a seção um deles possui um campo para edição e sages, Linker e Directories/Conditionals foi
Recent Projects, à esquerda da página um botão. No primeiro deles digite “calc. criada a opção de guardar configurações
inicial, contém os últimos cinco projetos exe” e no segundo “notepad.exe”. Clique específicas para cada Build da aplicação.
abertos e ainda as opções Show Recent em OK e efetue a compilação do projeto Dessa forma você pode organizar melhor
Projects, Open Project, New Project e pressionando CTRL+F9. as opções do projeto e determinar como
Help, agilizando ainda mais a visua- Nesse ponto notará que a calculadora cada compilação será efetuada.
lização de últimos projetos, abertura, do Windows se abre, isso porque deter- Entre em Project>Options novamente e
criação e ainda acesso à ajuda do IDE minamos que a mesma seria aberta antes clique em uma das opções mencionadas.
(Figura 1). da compilação. Para continuar o processo Note que à direita surge um ComboBox
feche a calculadora e note que logo que chamado Build Configuration. Clicando
MSBuild, Vista e XP Themes o IDE encerra o Build o Bloco de Notas é nele podemos notar que há dois itens
E falando em facilidades ao desenvol- mostrado. criados: Release e Debug. Podemos criar
vedor, a CodeGear tratou de criar um Isso facilita a automatização de ta- nossos próprios itens.
excelente recurso que auxilia e ajuda a refas, já que no lugar da calculadora e Clique em Compiler e nas configurações
automatizar tarefas na hora do Build. do NotePad podemos incluir comandos desmarque as caixas Optimization e Gene-
Chamado de MsBuild, o recurso dá a que serão executados antes e depois da ration XML documentation ou marque e
oportunidade ao desenvolvedor de compilação. desmarque outras opções a sua escolha.
determinar tarefas pré (Pre-build) e pós Em Compiler Messages retire alguns War-
(Post-build) compilação. Vamos fazer Nota: Observe que tanto no pré quanto nings do grupo principal e em Directories/
um exemplo simples para entendermos no pós build o Delphi 2007 fica aguar-
melhor como funciona. dando até que a tarefa seja efetuada.
www.devmedia.com.br/clubedelphi/portal.asp
Crie uma nova aplicação no Delphi
usando o menu File>New>VCL Forms Volte ao Project>Options e clique agora Acesse agora o mesmo o portal do assinante ClubeDelphi e assista a uma
Application – Delphi for Win32 e logo em no botão Edit da opção Pre-Build. Note que vídeo aula de Adriano Santos que mostra como utilizar as configurações
de Build no Delphi 2007.
seguida clique em Project>Options ou pres- a tela que se abre contém uma série de
www.devmedia.com.br/articles/viewcomp.asp?comp=5349
sione CTRL+SHIFT+F11. Com as opções comandos pré-configurados que podem

Figura 1. Home inicial do Delphi 2007

Edição 86 - ClubeDelphi 23

Clube86.indb 23 02.07.07 15:01:13


Conditionals mude os diretórios de output e qualquer outro item. Veja que as confi- mático, tais como MS Build da Microsoft
tais como Output Directory e Unit Output gurações são carregadas de acordo com a ou o Ant, aplicativo freeware bastante
Directory. configuração que fizemos (Figura 2). conhecido da comunidade Java.
Feito isso clique em Save As ao lado do Alguns detalhes que passam despercebi- Isso significa que ao abrir um projeto fei-
Build Configuration. Na tela que se abre ape- dos, são as novas extensões dos arquivos to em Delphi 2006, por exemplo, o Delphi
nas digite o nome que deseja chamar essa de projeto que passaram de BDSPROJ e 2007 mostrará as mensagens Creating a new
configuração e clique OK. Repita a opção BDSGROUP para DPROJ e GROUPPROJ project e Upgrading Project, ambas acompa-
fazendo novas configurações e salve-a. respectivamente. Essas novas extensões nhadas dos diretórios e nomes de arquivos
Clique agora em Build Configuration e são necessárias para compatibilizar os a serem criados e/ou atualizados.
alterne entre o primeiro item que criamos projetos com ferramentas de Build auto- Outro recurso de extrema importância
(também nas opções do projeto) é o Enable
runtime themes que foi adicionado para com-
patibilizar visualmente nossa aplicação a
todos os sistemas operacionais Windows.
Entre as versões 7 e BDS 2006 tínhamos
o XP Manifest, que fazia com que nossas
aplicações ficassem com o estilo visual do
Windows XP. Para isso bastava incluir o
componente na tela principal e compilar
o projeto.
Esse recurso agora é um opcional e é
válido para qualquer Windows. Para tes-
tar o recurso Enable runtime themes insira
alguns componentes no projeto que aca-
bamos de criar tais como: Buttons, Edits,
Labels, ListBoxes e ComboBoxes (Figura 3).
Agora entre no menu Project>Options e
na opção Application marque o item Enable
runtime themes. Compile sua aplicação e
execute. Esse recurso detecta o Windows
onde a aplicação está rodando e modifica
o visual das telas e componentes pra se
Figura 2. Build Configuration adaptar ao sistema operacional.
Dessa forma, nosso usuário não terá
a sensação de estar com uma versão
ultrapassada do sistema rodando em
um Windows mais recente. Observe na
Figuras 4 o mesmo exemplo rodando em
Windows Vista e XP.
Ainda se tratando de Windows Vista,
a CodeGear introduziu e atualizou as
bibliotecas de mensagens e diálogos do
Delphi. A exemplo disso temos agora a
paleta Vista Dialogs onde encontramos
os componentes FileOpenDialog, FileSave
Dialog e TaskDialog.
O TaskDialog certamente é a maior
novidade. Com ele podemos adicionar
diversos recursos aos diálogos, tais
como RadioButtons, ProgressBars e botões
personalizados. Crie uma nova aplicação
Win32 e vamos entender um pouco me-
lhor como isso funciona.
Adicione um TaskDialog da paleta Vista
Dialogs no formulário. Clique duas vezes
Figura 3. Exemplo de janela para teste com Vista Themes na propriedade RadioButtons. Na tela que

24 ClubeDelphi - Delphi 2007 em Detalhes

Clube86.indb 24 02.07.07 15:01:15


NOVIDADES

se abre adicione três itens e modifique a O código identifica o RadioButton cli- Personalizado”. Adicione novos itens e
propriedade Caption de cada um para que cado e configura a propriedade Position configure a seu gosto.
fique como “Item1”, “Item2” e “Item3” da ProgressBar de maneira que fique Após as devidas configurações adicione
respectivamente. com 10, 50 e 100 por cento preenchida. um Button no formulário e o faça a cha-
Retornando ao componente abra a Vamos a mais um exemplo agora usando mada ao método Execute do componente.
propriedade Flags e marque como True a propriedade Buttons do componente Execute a aplicação e clique no botão
a opção tfShowProgressBar. Dessa forma em questão. para executar a caixa de diálogo. Veja na
adicionamos três RadioButtons e um Pro- Clique duas vezes na propriedade Figura 5 a caixa de diálogo em execução
gressBar na caixa de diálogo. e insira um novo item no editor que no Windows Vista.
No evento OnRadioButtonClicked do se abre. No item criado configure sua
TaskDialog digite o código da Listagem 1. propriedade Caption para “Meu Botão Nota: Os componentes, obviamente,
serão mostrados apenas se você estiver
Listagem 1. Código de atualização da ProgressBar no TaskDialog trabalhando com o Windows Vista.

procedure TForm1.TaskDialog1RadioButtonClicked(Sender: TObject);


begin O último teste com os componentes
if TaskDialog1.RadioButton = TaskDialog1.RadioButtons[0] then
TaskDialog1.ProgressBar.Position := 10; da paleta Vista Dialogs é inserirmos no
if TaskDialog1.RadioButton = TaskDialog1.RadioButtons[1] then
TaskDialog1.ProgressBar.Position := 50; formulário um FileOpenDialog e um Fi-
if TaskDialog1.RadioButton = TaskDialog1.RadioButtons[2] then leSaveDialog. Insira também dois novos
TaskDialog1.ProgressBar.Position := 100;
end; botões e no evento OnClick de cada um
faça uma chama ao Execute de cada com-
ponente. Veja na Figura 6 as novas caixas
de diálogo sendo exibidas.

Nota: Ao utilizar somente os compo-


nentes da paleta Vista Dialogs estamos
compatibilizando nossas aplicações
apenas com Windows Vista, portanto
deve-se ter atenção ao escolher tais
componentes.

Mais gráficos no Delphi


Até a versão BDS 2006 tínhamos apenas
uma opção para geração de gráficos. Isso
mudou e temos agora uma poderosa suíte
de componentes que agiliza a produção
de gráficos em nossas aplicações. Crie
uma nova aplicação e insira os com-
ponente SqlConnection (“sqlConexao”)
e sqlDataSet (“sqlEmployee”) da paleta
dbExpress.
Faça a ligação do SqlDataSet ao Sql
Connection usando a propriedade Sql
Connection. Configure o SqlConnection para
Figura 4. Utilizando Enable runtime themes no Windows Vista e XP
conectar-se ao banco Employee.fdb presente
no diretório examples do Firebird.
Adicione também um DataSetProvider
(“dspEmployee”), um ClientDataSet (“cd-
sEmployee”) e um DataSource (“dtsEm-

www.devmedia.com.br/clubedelphi/portal.asp

Acesse agora o mesmo o portal do assinante ClubeDelphi e assista a uma


vídeo aula de Adriano Santos que mostra como utilizar o Enable runtime
thmes no Windows Vista, XP e 98.
www.devmedia.com.br/articles/viewcomp.asp?comp=5349
Figura 5. Exemplo de TaskDialog no Windows Vista

Edição 86 - ClubeDelphi 25

Clube86.indb 25 02.07.07 15:01:19


ployee”) todos da paleta Data Access. Faça
todas as ligações dos componentes para
que possamos visualizar os dados. Na
propriedade CommandText do SqlDataSet
adicione o seguinte comando SQL:
SELECT * FROM JOB

Clique duas vezes no SqlDataSet e adicione


todos os campos da tabela. Vamos ao grá-
fico. Insira um DBChart da paleta TeeChart
Std no formulário e clique duas vezes nele.
Na guia Series clique no botão Add, adicione
uma série utilizando a opção Bar, criando
dessa forma um gráfico do tipo barras.
Após confirmar e retornar ao formu-
lário, adicione um DBCrossTabSource.
Configure sua propriedade DataSet es-
colhendo o cdsEmployee. Na propriedade
GroupField escolha JOB_COUNTRY e em
ValueField escolha MAX_SALARY. Nesse Figura 6. FileOpenDialog e FileSaveDialog no Windows Vista
ponto basta ativar o gráfico marcando a
propriedade Active para True. Nos Labels à esquerda apenas mude o Cap-
Repare que o gráfico já foi montado e tion de cada um deles e suas configurações
está pronto para ser exibido (Figura 7). de fonte se necessário. Agora no evento
Pra finalizar, adicione um ButtonColor da onAsyncClick do btnConfirmar vamos fazer a
paleta TeeChart Std na tela. Com ele, vamos codificação do exemplo fazendo com que os
mudar a cor da primeira coluna do DB- Labels do rodapé sejam atualizados. Insira
Chart, pois o botão tem a característica de o código da Listagem 2.
abrir automaticamente a caixa de diálogo Pressione F9 e execute a aplicação. Note
de cores padrão do Windows, dessa forma que ao digitar algo nos Edits e clicar no
podemos pegar a cor selecionada. botão não há mais recarga da página
Vamos usá-la para substituir a cor da pri- e todos os Labels são atualizados, isso
meira barra. Para isso, adicione o seguinte porque estamos utilizando o evento
Figura 7. Gráfico usando TeeChart
código no evento OnClick do botão: OnAsyncClick que utiliza o AJAX para
atualizar a página (Figura 9). ‘
DBChart1.Series[0].Color :=
ButtonColor1.SymbolColor; IWButton. Coloque-os no formulário Até a versão 2006 do Delphi tería-
principal como na Figura 8. mos que criar uma série de funções
Execute novamente o exemplo e teste Nomeie os IWEdits para “edtNome”, JavaScript para nos auxiliar na criação
o botão. “edtEndereco”, “edtCidade” e “edtEsta- de sites com AJAX. Sempre que for
do”, e mude o Caption de cada IWLabel necessária a utilização de AJAX em
AJAX na VCL para “Nome”, “Endereço”, “Cidade” e aplicações Web, basta transferirmos o
Outra grande notícia do Delphi 2007 é “Estado” respectivamente. No IWButton código do evento convencional para os
a total compatibilidade do AJAX com a troque o Name para “btnConfirmar” e o eventos do tipo Async.
nova IntraWeb, agora chamada de VCL for Caption para “Confirmar”.
Web. Está muito mais fácil incluir rotinas Os dois IWLabels que sobraram, colo- Nota: Observe que quase todos os com-
em aplicações Web sem fazer requisições que-os acima do nosso formulário de ca- ponentes possuem os métodos Async
desnecessárias ao servidor. Vamos criar dastro com o Caption que preferir. Neste tais como OnAsyncChange, OnAsyncExit
um exemplo simples pra demonstrar exemplo usamos ClubeDelphi no primeiro do IWEdit.
como usar AJAX nessa nova versão. e Exemplo de Cadastro no segundo.
Crie uma aplicação Web através do menu Abaixo do botão colocamos oito Labels
File>New>Other>VCL for Web e escolha a sendo que os quatro à direita receberão Compatibilidade com componentes
opção VCL for the Web Application Wizard. os nomes “lblNome”, “lblEndereco”, de terceiros
Clique em OK na próxima tela e aguarde a “lblCidade” e “lblEstado”. Esses Labels E os recursos não param por ai. A Co-
criação dos formulários necessários. serão atualizados com os dados que serão deGear se preocupou também com uma
Na paleta de componentes adicione digitados nos Edits simulando uma pré- das partes cruciais do Delphi: a compa-
quatro IWEdits, quatorze IWLabels e um confirmação do que foi digitado. tibilidade com componentes de terceiros.

26 ClubeDelphi - Delphi 2007 em Detalhes

Clube86.indb 26 02.07.07 15:01:23


NOVIDADES

Quem usa componentes desenvolvidos


Listagem 2. Código para atualizar os Labels
por outros desenvolvedores e/ou em-
procedure TIWForm1.btnConfirmarClick(Sender: TObject);
presas sabe que uma das primeiras pre- begin
ocupações quando se pensa em migrar lblNome.Caption := edtNome.Text;
lblEndereco.Caption := edtEndereco.Text;
o sistema para uma versão mais recente lblCidade.Caption := edtCidade.Text;
lblEstado.Caption := edtEstado.Text;
do Delphi é procurar ou aguardar novas end;
verões desses componentes, ou as vezes
até abandonar alguns deles.
O problema pode ser resolvido apenas deverão passar por esse mesmo processo, Os novos componentes, atualização de
recompilando o código-fonte do pacote mas nesse caso quem o terá que fazer é mensagens e diálogos para compatibi-
no próprio Delphi 2007 e efetuar a insta- o próprio desenvolvedor e/ou empresa lizar com Windows Vista. Também foi
lação. Isso já faz com que o componente se detentora do código-fonte. vista a nova suíte de componentes para
compatibilize com a versão 2007 facilitan- criação de gráficos, TeeChart, e a adição
do a migração de nossos sistemas. Conclusão de eventos especiais pra lidar com AJAX
Desde a instalação do Delphi em meu Este artigo mostrou as principais caracte- na nova IntraWeb.
laboratório de testes, efetuei a compilação rísticas da nova versão do Delphi for Win32. Para melhor entender o funcionamento
de diversas suítes tais como: RxLib, LMD, Entre as novidades estão as modificações e as novidades dessa versão acesse o
FortesReport, JediVCL e QuickReport 4. no Project>Options para compatibilizar portal da DevMedia e assista a seis vídeo-
Todos sem a necessidade de baixar uma as aplicações com MSBuild e outras ferra- aulas mostrando as principais novidades
versão especial para Delphi 2007. mentas de Build automático. Criação de e também como instalar a versão trial do
Os componentes que não possuem fonte favoritos na Home para melhor gerencia- Delphi 2007.
e não estão compatibilizados com 2007 mento dos projetos mais recentes. Um forte abraço e até a próxima.

Figura 8. Exemplo de tela em VCL for Web Figura 9. Aplicação Web utilizando AJAX

Edição 86 - ClubeDelphi 27

Clube86.indb 27 02.07.07 15:01:24


SOAP - Aprenda técnicas avançadas em um
exemplo passo a passo – Parte 2

N
a primeira parte deste artigo, co- são por natureza stateless, o que significa
meçamos a construir nosso dis- que nenhuma informação é persistida entre
co virtual, um aplicativo onde o as requisições processadas. Mas o servidor
usuário poderá enviar arquivos para um precisa “lembrar” dos identificadores ge-
servidor remoto, listá-los e recuperá-los rados, para poder comparar com os forne-
posteriormente. Criamos a interface grá- cidos pelos clientes. Como conseguir isso?
fica do cliente, vimos um pouco sobre au- Uma alternativa é controlar sessões.
tenticação com SOAP, exceções remotas, A implementação do protocolo SOAP
envio e recebimento de SOAP Headers e disponível com o Delphi não fornece ne-
como rastrear as chamadas ao servidor. nhum mecanismo de persistência nativo.
Nesta edição, veremos gerenciamento Dessa forma, precisamos criar um. Atente
de sessão, envio de arquivos via SOAP, então para um detalhe importante: nosso
compactação e muito mais. Web Service reside em uma DLL ISAPI,
Michael Benford que é carregada pelo IIS na primeira re-
(michael@devmedia.com.br) Gerenciamento de sessão quisição recebida pelo servidor.
é Acadêmico do curso de matemática da Uni- O mecanismo de autenticação que imple- Essa DLL fica no contexto do processo
versidade Federal Fluminense e desenvolvedor
Delphi e .NET da QSi, Qualidade em Sistemas
mentamos funciona, mas não está completo. DLLHost.exe, também conhecido como
de Informação. Programa em Delphi há 5 anos, Ele retorna sempre o mesmo identificador COM Surrogate. E ela ficará lá, em memó-
desenvolvendo aplicações cliente/servidor, mul- de sessão, definido de forma hard-coded no ria, até ser descarregada, como você fez
ticamadas, Web, utilitários de uso geral, com- código do servidor, o que não é, obviamente, algumas vezes ao longo desse artigo. Mas
ponentes, experts e aplicativos não-comerciais. uma solução segura. Precisamos gerar um Web Service não são statesless?
É colunista do portal da DevMedia, articulista e
membro da comissão editorial da revista Clube-
valor que seja único entre todos os usuários Sim, são! Abra novamente uDiscoVirtu-
Delphi. Palestrante da primeira edição do Deve- que podem vir a se autenticar. alImpl e observe a classe TDiscoVirtual.
loperís Webdays e da BorCon Brasil 2006. Aqui temos um problema: Web Service Esse é o nosso Web Service. É essa classe

28 ClubeDelphi - SOAP - Aprenda técnicas avançadas em um exemplo passo a passo – Parte 2

Clube86.indb 28 02.07.07 15:01:26


S OAP

que é criada e destruída automaticamente Listagem 1. Métodos de TdmSessions

a cada execução remota. O resto da unit function NewSession(const UserID: Integer;


UserName: string): string;
permanece intacto, carregado na memó- procedure RemoveSession(const SessionID: string);
function IsValidSession(const SessionID: string): Boolean;
ria. Confuso? Vamos implementar para function GetUserID(const SessionID: string): Integer;
você entender melhor. procedure UpdateLastAccess(const SessionID: string);

Selecione o projeto do servidor e adicione


Listagem 2. Métodos de manipulação de sessão
a ele um Data Module. Chame-o de “Td-
function TdmSessions.NewSession(const UserID: Integer; UserName: string): string;
mSession” e salve o arquivo com o nome var
“uSessionDataModule”. Clique agora no SessionID: TGUID;
begin
menu Project e escolha View Source. Remo- CriticalSection.Enter;
try
va a seguinte linha de código: if not cdsSessions.Active then cdsSessions.Open;
CreateGUID(SessionID);
Application.CreateForm(tdmSession,dmSession); Result := GuidToString(SessionID);
cdsSessions.Insert;
cdsSessionsSessionID.AsString := Result;
Fizemos isso porque criaremos manu- cdsSessionsStartDateTime.AsDateTime := Now;
cdsSessionsLastAccess.AsDateTime := Now;
almente o Data Module. Adicione um cdsSessionsUserName.AsString := UserName;
ClientDataSet. Dê um duplo clique sobre cdsSessionsUserID.AsInteger := UserID;
cdsSessions.Post;
ele para abrir o editor de campos e insira finally
CriticalSection.Leave;
os seguintes Fields: end;
end;
1. SessionID: Tipo String, tamanho 38;
2. LastAccess: Tipo DateTime; function TdmSessions.IsValidSession(const SessionID: string): Boolean;
begin
3. UserName: Tipo String, tamanho 10; CriticalSection.Enter;
try
4. UserID: Tipo Integer. Result := cdsSessions.Locate(‘SessionID’, SessionID, []);
if Result then
Result := MinutesBetween(cdsSessionsLastAccess.AsDateTime, Now) < 1;
Clique de direita sobre o ClientData- finally
CriticalSection.Leave;
Set e escolha Create DataSet. Pronto, já end;
end;
temos um DataSet para armazenar os function TdmSessions.GetUserID(const SessionID: string): Integer;
begin
identificadores de sessão gerados para CriticalSection.Enter;
cada usuário. Vamos então criar alguns try
Result := 0;
métodos para gerenciar sessões. Adicione if IsValidSession(SessionID) then
Result := cdsSessionsUserID.AsInteger;
as linhas da Listagem 1 na seção public finally
CriticalSection.Leave;
da classe TdmSessions: end;
Implemente agora todos esses métodos end;
procedure TdmSessions.RemoveSession(const SessionID: string);
(Ctrl+Shift+C) com o código da Listagem 2. begin
CriticalSection.Enter;
Esses métodos servem para: try
• Criar um novo registro no ClientData- if IsValidSession(SessionID) then
cdsSessions.Delete;
set de sessões, retornando um identifica- finally
CriticalSection.Leave;
dor único (NewSession); end;
end;
• Remover uma sessão do DataSet (Re- procedure TdmSessions.UpdateLastAccess(const SessionID: string);
moveSession); begin
CriticalSection.Enter;
• Verificar se existe uma sessão com o try
if IsValidSession(SessionID) then
identificador informado ou se o usuário begin
cdsSessions.Edit;
está há muito tempo sem realizar uma cdsSessionsLastAccess.AsDateTime := Now;
requisição (IsValidSession); cdsSessions.Post;
end;
• Retornar o ID do usuário de uma finally
CriticalSection.Leave;
determinada sessão (GetUserID) e para end;
end;
atualizar a data/hora do último acesso
do usuário (UpdateLastAccess).
Para usá-las, porém, precisamos rea- Por fim, implemente as seções initiali-
Observe o uso de critical sections para lizar mais algumas coisas. Inclua a unit zation e finalization da unit com o código
sincronizar o acesso ao código, pois o SyncObjs à cláusula uses e declare a variá- a seguir:
Web Service pode receber muitas solici- vel CriticalSection na seção var da mesma,
initialization
tações simultâneas. Essas seções críticas como mostrado a seguir: CriticalSection := TCriticalSection.Create;
finalization
vão garantir que apenas uma chamada var CriticalSection.Free;
dmSessions: TdmSessions;
por vez execute o código entre os méto- { Adicione essa linha }
dos Enter e Leave, preservando a aplicação CriticalSection: TCriticalSection; Volte ao arquivo uDiscoVirtualImpl e
implementation
em um estado consistente. adicione a linha a seguir à mesma seção

Edição 86 - ClubeDelphi 29

Clube86.indb 29 02.07.07 15:01:28


var mencionada anteriormente:
Listagem 3. Criando e verificando sessões
var
Sessions: TdmSessions; procedure TDiscoVirtual.Login(const UserName, Password: string);
implementation var
UserID: Integer;
SessionID: string;
E, por último, novamente nas seções Header: TAuthenticationHeader;
begin
initialization e finalization da unit, inclua with TDataAccess.Create(nil) do
try
o seguinte código (apenas as linhas cdsUsers.Params.ParamByName(‘NOME’).AsString := UserName;
destacadas): cdsUsers.Open;
if cdsUsers.RecordCount = 0 then
initialization raise EUserNotFound.CreateFmt(‘Usuário %s não foi encontrado’, [UserName]);
{ Adicione essa linha } if not SameStr(cdsUsersSENHA.AsString, Password) then
Sessions := TdmSessions.Create(nil); raise EInvalidPassword.Create(‘A senha informada é inválida’);
finalization UserID := cdsUsersID.AsInteger;
Sessions.Free; { E essa também} finally
Free;
end;
Muito bem, já podemos fazer uso do me- SessionID := Sessions.NewSession(UserID, UserName);
Header := TAuthenticationHeader.Create;
canismo de sessão. Então, retorne aos mé- Header.SessionID := SessionID;
todos Login e CheckHeader e modifique-os (Self as ISOAPHeaders).Send(Header);
end;
com o código da Listagem 3. Também procedure TDiscoVirtual.CheckHeader(var SessionID: string);
var
nessa listagem está o código do Logout, Header: TAuthenticationHeader;
begin
que deve ser implementado. (Self as ISOAPHeaders).Get(TAuthenticationHeader, TSOAPHeader(Header));
Descarregue a DLL do servidor no IIS if not Assigned(Header) then
raise ERemotableException.Create(‘Cabeçalho de identificação não encontrado’);
e recompile o projeto. Alterne para o if Sessions.IsValidSession(Header.SessionID) then
begin
projeto do cliente e abra o evento OnClick SessionID := Header.SessionID;
Sessions.UpdateLastAccess(SessionID);
do Login. Crie um desvio com else para o end
primeiro if existente (if FLoggedOut then) else
raise ERemotableException.Create(‘Identificador de sessão inválido. ‘+
e adicione o código da Listagem 4. ‘Por favor efetue o login novamente’);
end;
Observe que estamos tratando as exceções procedure TDiscoVirtual.Logout;
var
vindas do Logout, para evitar que a aplicação SessionID: string;
não atualize os controles na tela e o usuá- begin
CheckHeader(SessionID);
rio não possa realizar o login novamente. Sessions.RemoveSession(SessionID);
end;
Execute agora o cliente, efetue o login e
observe na barra de status o novo código de Listagem 4. Alterando quando o usuário não for autenticado
identificação recebido (Figura 1).
(...)
Teste as demais funcionalidade imple- else
begin
mentadas, como informar um nome de try
PrepareHeader;
usuário inexistente ou uma senha invá- AppServer.Logout;
lida. Se você demorar mais do que um except
on E: ERemotableException do
minuto para realizar o logout, receberá MessageBox(E.Message, MB_OK + MB_ICONERROR);
end;
uma exceção vinda do IsSessionValid, que FLoggedOut := True;
determina a sessão como expirada se esse btnLogin.Caption := ‘Login’;
StatusBar.Panels[0].Text := ‘Não autenticado’;
tempo transcorrer. StatusBar.Panels[1].Text := ‘’;
lstRemoteFiles.Items.Clear;
O valor de um minuto foi definido edtServerURL.Enabled := True;
end;
propositalmente para ser possível testar end;
o recurso. Em produção você deve esco-
lher um valor mais perto da realidade
da aplicação.

Enviando arquivos via SOAP


Figura 1. Identificador único de sessão recebido do servidor
Prosseguindo com o desenvolvimento
da nossa aplicação, vamos ver agora como
enviar arquivos através do protocolo conforme mostrado na Figura 2. reita sobre ele e selecione Add all fields. Em
SOAP. Então, sem demoras, selecione o Configure a propriedade CommandText do seguida, configure a propriedade Required
projeto do servidor e abra o Data Module dsFileInsert para “select ID, ID_USUARIO, do campo ID para False. Repita o mesmo
criado anteriormente. NOME_ARQUIVO, TAMANHO, DATA_ procedimento para o cdsFileInsert.
Adicione os seguintes componentes: HORA , ARQUIVO from ARQUIVO where Para o dsFileSelect, altere a propriedade
dois SQLDataSets, dois DataSetProviders ID_USUARIO = :ID_USUARIO”. CommandText para “select ID_USUARIO,
e dois ClientDataSets. Configure o rela- Dê um duplo clique sobre o componente NOME_ARQUIVO, TAMANHO, DATA_
cionamento entre cada trio e nomeio-os para abrir o editor de campos, clique de di- HORA from ARQUIVO where ID_USU-

30 ClubeDelphi - SOAP - Aprenda técnicas avançadas em um exemplo passo a passo – Parte 2

Clube86.indb 30 02.07.07 15:01:28


S OAP

ARIO = :ID_USUARIO”. Abra o editor de


campos do cdsFileSelect e adicione todos
os Fields retornados na consulta.
Você deve estar pensando porque es-
tamos usando dois DataSets ao invés de
apenas um, já que as queries são pratica-
mente iguais. Pois o detalhe é exatamente
esse: elas são apenas parecidas. Observe
que o segundo comando SELECT não
retorna o campo ARQUIVO, onde os ar-
quivos enviados serão armazenados.
Isso é proposital: se carregássemos
para a memória todos os arquivos que o Figura 2. Novos componentes de acesso a dados
usuário possa ter enviado para o servidor,
estaríamos literalmente destruindo o Listagem 5. Enviando arquivos com TByteDynArray e SOAP
desempenho da aplicação. Mais: também procedure TDiscoVirtual.UploadFile(const FileName: string; FileData: TByteDynArray);
faríamos que todos trafegassem pela rede var
FileStream: TMemoryStream;
em uma simples listagem, por exemplo, o SessionID: string;
begin
que obviamente é desnecessário. CheckHeader(SessionID);
FileStream := TMemoryStream.Create;
Explicado esse ponto, abra uDiscoVir- try
tualImpl e localize o UploadFile. Preste ConvertEngine.ByteArrayToStream(FileData, FileStream);
with TDataAccess.Create(nil) do
atenção no seu parâmetro FileData: ele é try
cdsFileInsert.Open;
do tipo TByteDynArray, que é um array cdsFileInsert.Insert;
dinâmico de bytes. Podemos armazenar cdsFileInsertID_USUARIO.AsInteger := Sessions.GetUserID(SessionID);
cdsFileInsertNOME_ARQUIVO.AsString := FileName;
nele qualquer coisa serializável para cdsFileInsertTAMANHO.AsInteger := FileStream.Size;
cdsFileInsertDATA_HORA.AsDateTime := Now;
bytes, e isso inclui, é claro, arquivos. cdsFileInsertARQUIVO.LoadFromStream(FileStream);
cdsFileInsert.Post;
Implemente então o código do UploadFi- cdsFileInsert.ApplyUpdates(0);
le como mostrado na Listagem 4. finally
Free;
Note que estamos fazendo uso pela end;
finally
primeira vez da segunda versão do Che- FileStream.Free;
end;
ckHeader, para obter o identificador de ses- end;
são recebido no cabeçalho da mensagem
SOAP. Precisamos dele para chamar o Ge-
tUserID, do Data Module de sessões, a fim Com o UploadFile pronto, vamos imple- pareHeader antes de execução remota. Como
de conseguir o identificador do usuário no mentar agora o seu oposto: DownloadFile. mencionado anteriormente, precisamos fa-
banco de dados, necessário para inserir Portanto, localize-o dentro da unit e zer isso antes de cada chamada ao servidor
um registro na tabela de arquivos. digite o código da Listagem 5. para enviar junto como envelope do SOAP
Por fim, repare que estamos converten- Aqui fazemos o inverso do método an- o cabeçalho contendo o identificador de
do o parâmetro FileData, que é do tipo terior: localizamos o arquivo pelo nome, sessão recebido pelo método Login.
TByteDynArray, em um TStream, através salvamos o campo BLOB para um stream Antes de testarmos o envio de arqui-
do ByteArrayToStream, da classe Conver- e o convertemos para o tipo TByteDynAr- vos, precisamos implementar o evento
tEngine. Essa classe, escrita por mim, ray, novamente através da classe Conver- OnClick do Procurar. Então, dê um duplo
encontra-se na unit Tools, disponibilizada tEngine, com o StreamToByteArray. clique sobre ele e digite o código da
juntamente com os fontes da aplicação. Agora só precisamos invocar esses Listagem 7.
Mesmo seu código tendo apenas duas métodos do cliente. Recompile então o Fizemos uso da função nativa do Del-
linhas, ele não será publicado aqui no- servidor e alterne para o projeto DiscoVir- phi, PromptForFileName. Ela é muito útil
vamente por questões de espaço, mas tualClient. Codifique o evento OnClick do quando precisamos exibir ao usuário
é recomendável que você o estude para Enviar como mostrado na Listagem 6. uma caixa de diálogo Abrir ou Salvar,
aprender como a conversão funciona. Não há mistérios aqui. Simplesmente evitando o overhead da criação dos com-
carregamos o arquivo para um stream ponentes OpenDialog e SaveDialog.
Nota: Estou considerando que o leitor (através da classe TFileStream), e de- Entretanto, a função tem suas limitações:
saiba lidar com os componentes de pois fizemos uso novamente da classe ela não permite que você configure vários
acesso a dados do Delphi, bem como a ConvertEngine. O resto é exatamente aspectos da caixa de diálogo, disponíveis
tecnologia dbExpress. Dessa forma, não igual ao que vimos na implementação através da propriedade Options dos com-
comentarei as demais linhas de código do servidor. ponentes mencionados. Nesse caso não há
da Listagem 4. Observe novamente que chamamos o Pre- outra escolha a não ser usá-los.

Edição 86 - ClubeDelphi 31

Clube86.indb 31 02.07.07 15:01:29


Dica: Confira no help do Delphi o sig-
Listagem 5. Recebendo arquivos com TByteDynArray e SOAP
nificado de todos os parâmetros de
function TDiscoVirtual.DownloadFile(const FileName: string): TByteDynArray;
PromptForFileName. var
FileStream: TMemoryStream;
SessionID: string;
Execute a aplicação e realize o login no begin
CheckHeader(SessionID);
servidor. Em seguida, selecione um ar- FileStream := TMemoryStream.Create;
quivo no seu computador e clique no En- try
with TDataAccess.Create(nil) do
viar. Dependendo do tamanho, o processo try
cdsFileInsert.Params.ParamByName(‘ID_USUARIO’).AsInteger := Sessions.GetUserID(SessionID);
demorará muito ou pouco. Agora que cdsFileInsert.Open;
if not cdsFileInsert.IsEmpty and
sabemos como enviar arquivos através cdsFileInsert.Locate(‘NOME_ARQUIVO’, FileName, [loCaseInsensitive]) then
do protocolo SOAP, precisamos exibí-los begin
cdsFileInsertARQUIVO.SaveToStream(FileStream);
ao usuário, para que ele possa selecionar end;
finally
um e realizar o seu download. Free;
end;
Veremos isso no próximo tópico. ConvertEngine.StreamToByteArray(FileStream, Result);
finally
FileStream.Free;
Nota: Há ainda uma outra forma de end;
end;
trafegar arquivos via SOAP. Trata-se
da classe TSOAPAttachment. Porém, Listagem 6. Evento OnClick do botão Enviar
os arquivos enviados dessa forma são
procedure TfrmMain.btnUploadFileClick(Sender: TObject);
codificados apenas no padrão MIME, o var
FileStream: TFileStream;
que pode limitar as aplicações do Web FileData: TByteDynArray;
begin
Service. Se quiser saber mais sobre essa Screen.Cursor := crHourGlass;
classe, consulte a seção Links. try
FileStream := TFileStream.Create(edtFileName.Text, fmOpenRead or fmShareDenyWrite);
try
ConvertEngine.StreamToByteArray(FileStream, FileData);
PrepareHeader;
Trafegando DataSets no protocolo AppServer.UploadFile(ExtractFileName(edtFileName.Text), FileData);
finally
SOAP FileStream.Free;
end;
Acabamos de ver como trafegar arrays finally
Screen.Cursor := crDefault;
de bytes entre o cliente e o servidor, e end;
como dito, podemos enviar qualquer end;

tipo de dado através desse método, Listagem 7. Evento OnClick do botão Procurar
basta serializá-lo. Em alguns casos,
procedure TfrmMain.btnBrowseForFileClick(Sender: TObject);
porém, pode não ser tão fácil realizar var
essa conversão. Para enviar um DataSet, FileName: string;
begin
por exemplo, há uma forma muita mais if PromptForFileName(FileName,
‘Todos os arquivos|*.*’, ‘’,
simples e pronta. ‘Procurar arquivo’) then
edtFileName.Text := FileName;
Se o leitor já tiver alguma experiência end;
com SOAP, deve conhecer a interface
IAppSOAPServer. É ela que torna possível
conectar um ClientDataSet a um provider po BLOB do arquivo. Isso é proposital, fra grandes inclusões e/ou exclusões, para
remoto, exibir e atualizar seus dados. Isso pois não queremos carregá-lo para a evitar efeitos visuais, como flickering.
é feito através do método remoto GetRe- memória, sobrecarregando desnecessa- Depois, como de costume, preparamos
cords, que retorna um OleVariant. riamente o servidor. o cabeçalho da mensagem com Prepa-
E se você já observou, a propriedade Descarregue a DLL no IIS e recompile o reHeader e invocamos o método remoto
Data da classe TClientDataset é do mesmo projeto. Selecione o projeto DiscoVirtual- GetFileList, atribuindo o seu retorno dire-
tipo (acredite, não é mera coincidência). Client e insira um ClientDataSet no formu- tamente à propriedade Data do ClientDa-
Dessa forma, é perfeitamente possível lário. Chame-o de “cdsFiles”. Vá então para taset, preenchendo-o, dessa forma.
trafegar todo o conteúdo de um DataSet a seção private da classe do formulário e crie Em seguida, iteramos pelos registros
através desse tipo de dado. um método chamado “DoGetFileList”. recebidos e os adicionamos ao ListView.
Voltando para a prática, selecione o pro- Pressione Ctrl+Shift+C e digite o código Há duas funções no código que merecem
jeto do servidor, abra uDiscoVirtualImpl e da Listagem 9. ser comentadas: GetFileType e AddFileIcon.
localize o GetFileList. Codifique-o como Vamos entender o que fizemos. A A primeira recupera a descrição do tipo
mostrado na Listagem 8. primeira linha chama o BeginUpdate, da do arquivo, baseado em sua extensão.
Esse método é auto-explicativo, exceto propriedade Items da classe TListView. Já a segunda extrai o ícone do programa
por um detalhe: estamos fazendo uso Juntamente com EndUpdate, ele deve ser vinculado à extensão informada e o inclui no
do cdsFileSelect, que não possui o cam- executado sempre antes que o controle so- ImageList, a fim de ser utilizado no ListView

32 ClubeDelphi - SOAP - Aprenda técnicas avançadas em um exemplo passo a passo – Parte 2

Clube86.indb 32 02.07.07 15:01:30


S OAP

(não se esqueça de apontar a propriedade


Listagem 8. Retornando a lista de arquivos remotos
SmallImages do ListView para o ImageList).
function TDiscoVirtual.GetFileList: OleVariant;
O código de ambas não está publicado, var
mas encontra-se disponível juntamente SessionID: string;
begin
com os fontes da aplicação. CheckHeader(SessionID);
with TDataAccess.Create(nil) do
try
Nota: Poderíamos simplesmente exibir cdsFileSelect.Params.ParamByName(‘ID_USUARIO’).AsInteger := Sessions.GetUserID(SessionID);
cdsFileSelect.Open;
os dados em um DBGrid, o que seria de Result := cdsFileSelect.Data;
finally
fato muito mais simples. Porém, para Free;
end;
dar um acabamento mais profissional à end;
solução, optei por utilizar um ListView.
Listagem 9. Preenchendo o ListView de arquivos remotos

Muito bem. Agora volte ao evento OnCli- procedure TfrmMain.DoGetFileList;


var
ck do Login e substitua a linha AppServer. FileName, FileExtension, FileType, FileDateTime: string;
GetFileList por DoGetFileList. Já no mesmo FileSize, Icon: Integer;
begin
evento do Enviar, adicione uma chamada lstRemoteFiles.Items.BeginUpdate;
try
à função recém-criada logo após a invoca- lstRemoteFiles.Items.Clear;
PrepareHeader;
ção do método remoto UploadFile. cdsFiles.Data := AppServer.GetFileList;
Execute a aplicação, envie alguns arquivos cdsFiles.First;
while not cdsFiles.Eof do
para o servidor e veja a listagem remota sen- begin
FileName := cdsFiles.FieldByName(‘NOME_ARQUIVO’).AsString;
do exibida dentro do ListView (Figura 3). FileExtension := ExtractFileExt(FileName);
FileSize := cdsFiles.FieldByName(‘TAMANHO’).AsInteger;
Resta-nos agora apenas implementar o
Salvar como. Então, dê um duplo clique so- { Convertemos o tamanho para kilobytes }
if FileSize < 1024 then FileSize := 1024;
bre ele e digite o código da Listagem 10.
FileType := GetFileType(FileExtension);
Mais uma vez, não há nada novo aqui. FileDateTime := FormatDateTime(‘dd/mm/yyyy hh:nn:ss’, cdsFiles.FieldByName(
‘DATA_HORA’).AsDateTime);
Pegamos o nome do arquivo selecionado
no ListView, abrimos uma caixa de diá- Icon := AddFileIcon(FileExtension);
with lstRemoteFiles.Items.Add do
logo Salvar (usando PromptForFileName), begin
Caption := FileName;
executamos o método remoto Downloa- SubItems.Add(Format(‘%d KB’, [FileSize div 1024]));
dFile e finalmente convertemos o array SubItems.Add(FileType);
SubItems.Add(FileDateTime);
de bytes em um stream, através da classe SubItems.Add(FileExtension);
ImageIndex := Icon;
ConvertEngine. end;
cdsFiles.Next;
Recompile a aplicação para testar a nova end;
funcionalidade. cdsFiles.Close;
finally
lstRemoteFiles.Items.EndUpdate;
end;
Excluindo os arquivos remotos end;
O último recurso a ser implementado em
Listagem 10. Baixando os arquivos do servidor
nossa aplicação é a exclusão dos arquivos
no servidor de aplicação. Então, selecione procedure TfrmMain.btnDownloadFileClick(Sender: TObject);
var
o projeto DiscoVirtualServer, abra uDisco- FileStream: TFileStream;
FileData: TByteDynArray;
VirtualImpl e localize o DeleteFile. RemoteFileName, LocalFileName: string;
Implemente-o como mostrado na Lis- begin
if not Assigned(lstRemoteFiles.Selected) then
tagem 11. Exit;
RemoteFileName := lstRemoteFiles.Selected.Caption;
Note que estamos usando o cdsFileSelect, LocalFileName := RemoteFileName;
if PromptForFileName(LocalFileName, ‘Todos os arquivos|*.*’, ‘’, ‘Salvar como’, ‘’, True)
pela mesma razão explicada no tópico then
Trafegando DataSets no protocolo SOAP. begin
Screen.Cursor := crHourGlass;
Recompile o projeto do servidor e alterne try
PrepareHeader;
em seguida para o cliente. Implemente FileData := AppServer.DownloadFile(RemoteFileName);
FileStream := TFileStream.Create(LocalFileName, fmCreate or fmShareExclusive);
então o evento OnClick do Excluir como try
mostrado na Listagem 12. ConvertEngine.ByteArrayToStream(FileData, FileStream);
finally
FileStream.Free;
end;
Exibindo o progresso de envio e finally
recebimento Screen.Cursor := crDefault;
end;
O HTTPRIO e, por herança, o nosso end;
end;
HTTPRIOex, possui dois eventos para
capturar o progresso do envio e recebi-

Edição 86 - ClubeDelphi 33

Clube86.indb 33 02.07.07 15:01:30


mento de pacotes SOAP: OnPostingData e
OnReceivingData. Na verdade, esse even-
tos pertencem à classe THTTPReqResp,
que é composta por THTTPRIO.
Vamos então ver como usá-los. Adicione
um novo formulário ao projeto, chame-o
de “frmProgress” e salve sua unit com
o nome “uFrmProgress”. Em seguida,
configure as propriedade BorderStyle e
Position para bsDialog e poScreenCenter,
respectivamente. Insira então um Label e
um Progressbar (Win32).
Dê ao primeiro o nome de “StatusLa-
bel”, e ao segundo, “Progressbar”. Por
fim, disponha-os visualmente como
mostrado na Figura 4.
Implemente agora o evento OnCreate do
formulário com o seguinte código:
Height := ClientHeight;
SetWindowLong(Handle, GWL_STYLE, Figura 3. Arquivos remotos sendo exibidos no ListView
GetWindowLong(Handle,
GWL_STYLE) and not WS_CAPTION);

Isso removerá a barra de títulos da Listagem 11. Excluindo arquivos do banco de dados
janela. Salve tudo e vá para o formulário
procedure TDiscoVirtual.DeleteFile(const FileName: string);
principal da aplicação. Adicione então var
SessionID: string;
dois métodos à seção private da classe begin
CheckHeader(SessionID);
TfrmMain, chame-os de “ShowProgres-
sDialog” e “HideProgressDialog”, e with TDataAccess.Create(nil) do
try
implemente-os (Ctrl+Shift+C) como cdsFileSelect.Params.ParamByName(
‘ID_USUARIO’).AsInteger := Sessions.GetUserID(SessionID);
mostrado na Listagem 13. cdsFileSelect.Open;
Muito bem. Implemente agora os even- if cdsFileSelect.Locate(‘NOME_ARQUIVO’, FileName, [loCaseInsensitive]) then
tos OnPostingData e OnReceivingData, begin
cdsFileSelect.Delete;
do HTTPRIOEx, como mostrado na cdsFileSelect.ApplyUpdates(0);
end;
Listagem 14. finally
Volte ao OnClick do Enviar e adicione as Free;
end;
linhas destacadas na Listagem 15. end;

Faça o mesmo com o Salvar como. Com-


pile a aplicação, execute-a, envie arquivos Listagem 12. Excluindo um arquivo remoto
e receba arquivos para/de o servidor. procedure TfrmMain.btnDeleteFileClick(Sender: TObject);
Talvez você precise escolher arquivos begin
if MessageBox(
grandes para ver com clareza a caixa de ‘Você tem certeza que deseja excluir o arquivo ‘+‘selecionado?’, MB_YESNO +
MB_ICONQUESTION) = IDYES then
progresso sendo exibida (Figura 5). begin
Se a barra de progresso que apareceu PrepareHeader;
AppServer.DeleteFile(lstRemoteFiles.Selected.Caption);
quando você realizou o download de lstRemoteFiles.Selected.Delete;
end;
um arquivo não se comportou como o end;
esperado, não foi erro seu. Há um bug na
implementação do HTTPReqResp, que não Listagem 13. Mostrando uma barra de progresso
atualiza corretamente os valores repassa-
procedure TfrmMain.ShowProgressDialog;
dos ao evento OnReceivingData. begin
Self.Enabled := False;
Para corrigi-lo, abra a unit SOAPHTTP- frmProgress.ProgressBar.Position := 0;
Trans (digite o nome da unit no Code Editor frmProgress.Show;
Application.ProcessMessages;
end;

procedure TfrmMain.HideProgressDialog;
begin
Self.Enabled := True;
frmProgress.Hide;
end;

Figura 4. Formulário frmProgress

34 ClubeDelphi - SOAP - Aprenda técnicas avançadas em um exemplo passo a passo – Parte 2

Clube86.indb 34 02.07.07 15:01:31


S OAP

e pressione Ctrl+Enter) e localize o Receive. projeto ou ao Library Path do IDE. Comprimindo os pacotes SOAP
Adicione a ele as seguintes variáveis: transmitidos
TotalSize, TotalRead: DWORD; Dica: Para entender melhor essas alte- Os dados transmitidos através do proto-
rações no código do SOAP, consulte a colo SOAP passam por processos de seria-
Nota: Faça uma cópia dessa unit antes seção Links. lização e desserialização. Em nossa aplica-
de realizar qualquer alteração. ção, os arquivos binários são expandidos

Em seguida, desça algumas linhas Listagem 14. Exibindo o progresso de envio e recebimento
e localize o comentário Extract Mime-
procedure TfrmMain.HTTPRIOExHTTPWebNode1PostingData(Sent, Total: Integer);
Boundary. Adicione então acima dele o begin
frmProgress.StatusLabel.Caption := Format(
código a seguir: ‘Enviando %d de %d kbytes’, [Sent div 1024, Total div 1024]);
frmProgress.Progressbar.Position := Trunc(Sent/Total * 100);
Index := 0;
if Sent = Total then
HttpQueryInfo(Pointer(Context),
frmProgress.StatusLabel.Caption := ‘Salvando arquivo no banco de dados...’;
HTTP_QUERY_CONTENT_LENGTH or
Application.ProcessMessages;
HTTP_QUERY_FLAG_NUMBER,
end;
@TotalSize, Len, Index);
TotalRead := 0;
procedure TfrmMain.HTTPRIOExHTTPWebNode1ReceivingData(Read, Total: Integer);
begin
frmProgress.StatusLabel.Caption := Format(
Por último, localize o repeat, mais abai- ‘Recebendo %d de %d kbytes’, [Read div 1024, Total div 1024]);
frmProgress.Progressbar.Position := Trunc(Read/Total * 100);
xo, e substitua as linhas: if Read = Total then
frmProgress.StatusLabel.Caption := ‘Salvando o arquivo no disco...’;
if Assigned(FOnReceivingData) then Application.ProcessMessages;
FOnReceivingData(Size, Downloaded) end;

Listagem 15. Mostrando uma barra de progresso na aplicação


Por essas: (...)
begin
if Assigned(FOnReceivingData) then ...
begin ShowProgressDialog; { Adicione essa linha }
Inc(TotalRead, Downloaded); finally
FOnReceivingData(TotalRead, TotalSize) ...
end; HideProgressDialog; { E essa também }
end;
Salve tudo e adicione a pasta da unit end;

SoapHTTPTrans ao Search Path do seu

Edição 86 - ClubeDelphi 35

Clube86.indb 35 02.07.07 15:01:33


durante esses processos, ocupando mais Listagem 16. Comprimindo os pacotes enviados e recebidos no cliente
espaço do que o seu tamanho original. procedure TfrmMain.HTTPRIOExBeforeExecuteStream(const MethodName: string; SOAPRequest: TStream);
Para melhorar a performance, pode- var
TempStream, CompressedStream: TMemoryStream;
mos comprimir os pacotes enviados, de OriginalSize, CompressedSize: Integer;
begin
forma a diminuir o tempo necessário TempStream := TMemoryStream.Create;
CompressedStream := TMemoryStream.Create;
para a transmissão ser concluída. Isso é
extremamente simples, bastando seguir try
SOAPRequest.Position := 0;
os seguintes passos: TempStream.CopyFrom(SOAPRequest, 0);
CompressionEngine.CompressStream(SOAPRequest, CompressedStream);
1. Compactar os pacotes no cliente antes OriginalSize := SOAPRequest.Size;
de enviá-los ao servidor; CompressedSize := CompressedStream.Size;
SOAPRequest.Position := 0;
2. Descompactá-los no servidor antes SOAPRequest.CopyFrom(CompressedStream, 0);
SOAPRequest.Size := CompressedStream.Size;
de processá-los;
LogMessages(True, MethodName, TempStream, OriginalSize, CompressedSize);
3. Compactá-los no servidor antes de finally
enviar a resposta; TempStream.Free;
CompressedStream.Free;
4. Descompactá-los no cliente antes de end;
end;
ler a resposta.
procedure TfrmMain.HTTPRIOExAfterExecute(const MethodName: string; SOAPResponse: TStream);
var
Vamos então implementar tudo isso. Já TempStream, DecompressedStream,
DecryptedStream: TMemoryStream;
sabemos como acessar os pacotes envia- OriginalSize, CompressedSize: Integer;
begin
dos e recebidos no cliente, pois o fizemos DecompressedStream := TMemoryStream.Create;
TempStream := TMemoryStream.Create;
quando criamos o trace das mensagens. try
Volte aos eventos OnBeforeExecuteStream CompressionEngine.DecompressStream(SOAPResponse, DecompressedStream);
OriginalSize := SOAPResponse.Size;
e OnAfterExecute e modifique-os para o CompressedSize := DecompressedStream.Size;
SOAPResponse.Position := 0;
código da Listagem 16. SOAPResponse.CopyFrom(DecompressedStream, 0);
SOAPResponse.Size := DecompressedStream.Size;
A classe CompressionEngine também faz SOAPResponse.Position := 0;
parte da unit Tools. Ela usa a biblioteca de TempStream.CopyFrom(SOAPResponse, 0);

compressão Zlib, que acompanha o Delphi. LogMessages(False, MethodName, TempStream, CompressedSize, OriginalSize);
finally
Para entender como os métodos Com- TempStream.Free;
DecryptedStream.Free;
pressStream e DecompressStream funcio- end;
nam, não deixe de olhar esse arquivo. As end;

chamadas ao LogMessage também foram


alteradas, para receber corretamente agora
o tamanho do pacote compactado.
Compile a aplicação e alterne para o pro-
jeto do servidor. Abra a uWebModule, sele-
cione o HTTPSoapPascalInvoker existente e
implemente seus eventos AfterDispatchE-
vent e BeforeDispatchEvent da forma inversa
feita no cliente, isso é, descompactando
antes e compactando depois.
Em seguida, descarregue a DLL do
servidor, recompile o projeto e execute
a aplicação cliente. Realize o envio e o
recebimento de alguns arquivos e alterne
para a aba Trace (Figura 6).
Observe na Figura 12 que conseguimos
uma taxa de compressão de mais de 50%
na maioria dos métodos, e quase 55% no
download de um arquivo!

Criptografia de pacotes
Os dados trafegados entre o cliente e o
servidor são transmitidos em texto puro,
sem nenhuma proteção. Se você observou
o trace para o método Login, deve ter
notado que a senha do usuário está lá, Figura 5. Barra de progresso em ação

36 ClubeDelphi - SOAP - Aprenda técnicas avançadas em um exemplo passo a passo – Parte 2

Clube86.indb 36 02.07.07 15:01:33


S OAP

totalmente desprotegida. Delphi, e que podem ajudar no processo de


Há muitas formas de se criptografar os desenvolvimento de suas aplicações.
dados enviados e recebidos. Uma delas é Se você tiver qualquer dúvida, sinta-se à
através de um certificado digital, utilizan- vontade para me contatar via e-mail. Um
do-se o protocolo HTTPS ao invés do HTTP. forte abraço!
Outra, criptografando os pacotes antes de
enviá-los, como fizemos na compressão.
No código da aplicação disponível para
download, você encontrará uma maneira
de fazer isso. Eu utilizei a biblioteca de
criptografia LockBox, da extinta Turbo Po-
wer. Ela é open source e você pode baixá-la
no site do SourceForge (veja seção Links).

Nota: Tenha em mente que criptografar


todos os pacotes pode comprometer a
eficácia da compressão de dados, anu-
lando-a em alguns casos.

Conclusão
Esse artigo foi extenso, mas o assunto
assim o exigiu. Espero ter conseguido pas-
sar para o leitor algumas técnicas não tão
óbvias sobre os Web Services criados com o Figura 6. Comprimindo os pacotes enviados e recebidos

Edição 86 - ClubeDelphi 37

Clube86.indb 37 02.07.07 15:01:35


Impressão matricial no Delphi

N
este artigo veremos como é pela segunda alternativa. Descompacte
simples, fácil e rápido montar o arquivo baixado, crie um novo projeto
um projeto que imprima dados em Delphi, copie todos os arquivos da
em uma impressora matricial usando as pasta src do arquivo baixado e adicione-
classes do componente VDO, que é fre- os ao projeto.
eware e open-source. O componente VDO Alguns formulários automaticamente fi-
tanto imprime em impressoras matriciais carão no Auto-create do projeto, transfira-
como em impressoras de Jato. os colocando no Available forms, são eles:
Em nosso exemplo mostraremos como frmVDOPreview, frmVDOPrinterTypeDlg e
imprimir em uma impressora matricial frmVDOPrintProgress. O único formulá-
usando a classe VDODmPrinter, para rio que ficará na criação automática é o
impressoras Jato basta usar a classe formulário principal (Figura 1).
VDOCaPrinter. Como o VDO não envia os
dados direto à porta, envia para o Spool Propriedades
do Windows, não teremos problemas com A seguir temos as principais proprie-
impressoras USB ou em Rede. dades do VDO.
• CurrentLine: Integer - Leitura do núme-
Rodrigo Otto Mostaert Criando um projeto e usando as ro da linha atual de impressão dentro da
(otto3d@gmail.com) classes página;
é Analista/Desenvolvedor Delphi/C#/C++. Tra- Primeiro temos que baixar o compo- • CurrentPage: Integer - Leitura do núme-
balha com desenvolvimentos Win32/.NET com
nente no site indicado na seção Links, ro da página atual de impressão;
banco de dados MySQL e Firebird em projetos
Cliente/Servidor/n-tier. Especializado em siste- poderemos instalar o componente ou • IsPrinting: Boolean - Leitura do estado
mas WMS e possui diversos cursos oficiais Bor- adicionar os fontes dele ao projeto e da impressora. Retorna True se a impres-
land pela Facilit em Recife (www.facilit.com.br). instanciar as classes na mão, faremos sora estiver imprimindo ou False se não

38 ClubeDelphi - Impressão matricial no Delphi

Clube86.indb 38 02.07.07 15:01:45


REL ATÓRIOS

estiver imprimindo; • ShowProgress: Boolean - Determina • OnError: TNotifyEvent (VCL) - Dispa-


• PrintersList: TStrings - Leitura das se será exibido o diálogo de progresso rado quando ocorrer um erro na inicia-
impressoras instaladas do Windows; durante a impressão; lização ou finalização da impressora ou
• CharMode: T VDODmCharMode = • Title: String - Título do trabalho de durante a impressão;
(cmClear, cmNormal) - Determina se será impressão a ser utilizado pelo spool do • OnNewLine: TNotifyEvent (VCL) - Dis-
removida a acentuação do texto a ser Windows; parado a cada nova linha;
impresso; • OnNewPage: TNotifyEvent (VCL) - Dis-
• Font: TVDODmFont - Determina a Eventos parado a cada nova página;
fonte a ser utilizada para impressão; Os principais eventos são: • OnPrint: TNotifyEvent (VCL) - Dispa-
• LineSpacing: TVDODmLineSpacing = • OnAbort: TNotifyEvent (VCL) - Dispa- rado a cada vez que algo é impresso;
(ls6lpp, ls8lpp) - Determina o espaçamento rado ao abortar a impressão; • OnStart: TNotifyEvent (VCL) - Dispara-
vertical (entre linhas) da impressora. Se
6 ou 8 linhas por polegada; Listagem 1. Gerando o relatório via código-fonte sem acesso ao banco
• Paper: TVDODmPaper - Determina as unit Unit1;
configurações de papel e página utiliza- ...
type
das pela impressora; tLado = (lEsquerdo, lDireito);
TForm1 = class(TForm)
• PrinterName: String - Fixa o nome da Button1: TButton;
procedure Button1Click(Sender: TObject);
impressora a ser utilizada. Se o nome da private
impressora não for válido, o diálogo de ...
uses VDODmPrinter;
impressão para seleção de uma impres- { ALT+F11, selecione o VDODmPrinter e clique em OK }
{$R *.dfm}
sora válida será exibido; procedure TForm1.Button1Click(Sender: TObject);
• ShowPreview: Boolean - Determina se var
{ Variável da classe TVDODmPrinter que fará o relatório }
o texto a ser impresso será renderizado VDOImpressao: TVDODmPrinter;
begin
na tela para pré-visualização da impres- VDOImpressao := TVDODmPrinter.Create(Self);
with VDOImpressao do
são; begin
• ShowDialog: Boolean - Determina se { Colocando um título ao relatório }
Title := ‘Impressão matricial DevMedia (ClubeDelphi)’;
será exibido o diálogo de impressão, { Informando que haverá quebra de página }
Paper.AutoNewPage := True;
permitindo a seleção da impressora, { Informando que desejamos mostrar uma tela de preview do relatório }
ShowPreview := True;
número de cópias etc. Se ShowDialog for { Desejamos mostrar o progresso de montagem do relatório }
True, a fixação do nome em PrinterName ShowProgress := True;
{ Iniciando o relatório }
é desconsiderada e será sobreposta pela BeginDoc;
{ o Primeiro parâmetro indica a posição da coluna que iremos imprimir, o Segundo é o
impressora selecionada no diálogo de texto a ser impresso e o Terceiro, se for true o relatório vai para a próxima linha,
caso contrário continua até chegar ao máximo da coluna informada }
impressão, porém se PrinterName for Print(00, ‘Primeira Linha do relatório na posição zero’, True);
uma impressora válida, ela estará pré- Print(10, ‘Segunda Linha do relatório na posição dez’, True);
Print(20, ‘Terceira Linha do relatório na posição vinte’, True);
selecionada no diálogo de impressão; Print(30, ‘Quarta Linha do relatório na posição trinta’, True);
NewLine(5); { Pula 5 linhas }
Print(00, ‘Linha de número.: ‘+Format(‘%3.3d’, [VDOImpressao.CurrentLine]), True);
Print(00, ‘Número da página: ‘+Format(‘%3.3d’, [VDOImpressao.CurrentPage]), True);
NewPage(1); { Próxima página }
Print(00, ‘Número da página: ‘+Format(‘%3.3d’, [VDOImpressao.CurrentPage]), True);
NewPage;
Font.Size := fsDefault;
Font.Align := faLeft;
Print(00, ‘Texto com 10cpp e alinhado a esquerda’, True);
Font.Size := fsCondensed;
Font.Align := faCenter;
Print(00, ‘Texto com 10cpp e alinhado ao centro’, True);
Font.Size := fsLargeCondensed;
Font.Align := faRight;
Print(00, ‘Texto condensado expandido, c/10cpp e ‘+‘alinhado a direita’, True);
Font.Size := fsLarge;
Font.Align := faLeft;
Print(00, ‘Texto expandido, c/10cpp e al. ‘+‘esquerda’, True);
Font.Size := fsDefault;
Font.Align := faLeft;
Font.Style := [fsDmBold];
Print(00, ‘Texto com 10cpp em “negrito”’, True);
Font.Style := [fsDmItalic];
Print(00, ‘Texto com 10cpp em “itálico”’, True);
Font.Style := [fsDmUnderline];
Figura 1. Definindo a criação de formulários Print(00, ‘Texto com 10cpp em “sublinhado”’, True);
Font.Style := [fsDmBold, fsDmItalic];
Print(00, ‘Texto com 10cpp em “negrito” E ‘+‘”italico”’, True);
{ Finalizando o relatório, caso o ShowPreview seja True, abrirá uma tela com o preview
do relatório }
EndDoc;
end;
{ Liberamos o objeto da memória }
FreeAndNil(VDOImpressao);
end;
Figura 2. Tela do sistema end.

Edição 86 - ClubeDelphi 39

Clube86.indb 39 02.07.07 15:01:46


do ao iniciar a impressão;
• OnTerminate: TNotifyEvent (VCL) - Dis-
parado ao finalizar a impressão.

Métodos
Os métodos principais são:
• Abort: Procedure - Sua função é abortar
o trabalho de impressão;
• BeginDoc: Procedure - Sua função é
iniciar o trabalho de impressão;
• EndDoc (AEjectPage: Boolean = True):
Procedure - Sua função é finalizar o traba-
lho de impressão. O parâmetro EjectPage
indica se a página deve ser ejetada após
a finalização. Se AEjectPage for True, a
impressora será orientada a realizar um
Eject antes de finalizar o trabalho de
impressão. O valor default do parâmetro
AEjectPage é True. O resultado esperado
Figura 3. Preview do relatório usando o VDO
de AEjectPage := True depende diretamen-
te do ajuste correto da propriedade Lines
de TVDODmPaper;
• NewLine (ALines: Integer = 1): Procedure -
Sua função é avançar o número de linhas
determinadas pelo parâmetro ALines.
Caso não seja informada a quantidade
linhas o valor default é 1;
• NewPage (APages: Integer = 1): Procedure
- Sua função é avançar o número de pági-
nas determinado pelo parâmetro APages.
Caso não seja informada a quantidade de
páginas o valor default é 1. O resultado es-
perado do NewPage depende diretamente
do ajuste correto da propriedade Lines de
TVDODmPaper.
• Print (AColumn: Integer; AText: String;
ALine: Boolean = False): Procedure - Sua
função é efetuar a impressão na coluna
determinada pelo parâmetro AColumn
do texto determinado pelo parâmetro
AText. O ALine é opcional e é o indicador
de que a linha foi finalizada e que a im-
pressora deve avançar para a próxima. Ao
informar o parâmetro ALine como True, Figura 4. Preview do relatório usando o VDO
não é necessária a chamada posterior ao
NewLine, a não ser que o salto de linhas
adicionais seja desejado. coloque dois botões (Figura 2). banco de dados.
No evento OnClick do Gerar relatório sem
Colocando a mão na massa banco faremos um exemplo sem acesso a Mostrando dados do banco
Já temos nosso projeto pronto para mon- banco, com o código da Listagem 1. No evento OnClick do Gerar relatório com
tar um relatório para imprimir. O VDO dá O código da listagem anterior adiciona banco faremos um exemplo acessando o
uma boa facilidade na hora de programar, várias linhas ao objeto de impressão com banco de dados do BDE (tabela Employee)
devido a sua simples codificação e um várias formas de formatação, como: tipo com o código da Listagem 2. Você pode
resultado bastante satisfatório na hora da fonte, alinhamento, tamanho, estilo da adaptar o exemplo para qualquer tecno-
de imprimir. Chega de conversa e vamos letra etc. A Figura 3 mostra o resultado logia de acesso a dados e banco, como o
ao que interessa. No formulário principal final do nosso exemplo sem acesso a um Firebird ou SQL Server.

40 ClubeDelphi - Impressão matricial no Delphi

Clube86.indb 40 02.07.07 15:01:47


REL ATÓRIOS

Listagem 2. Gerando o relatório com BD Instalando os componentes no IDE


uses DBTables;
...
procedure TForm1.Button2Click(Sender: TObject);
function CompletaComBranco(AValue: string; AQuantidade:integer; ALado: tLado): string;
var
C omo alternativa a instanciar os compo-
nentes na mão como fizemos, é possível
fazer a instalação dos componentes do VDO
contador: integer;
begin no IDE do Delphi. Ao fazer o download do ar-
Result := ‘’; quivo, descompacte-o na pasta desejada. No
for contador := 1 to Abs(AQuantidade - Length(
Avalue)) do
IDE do Delphi acesse File>Open, e na pasta
begin de instalação, localize a sub-pasta packages.
Result := Result + ‘ ‘;
end;
if ALado = lEsquerdo then
Acesse a sub-pasta correspondente a sua
Result := Result + AValue versão do Delphi e abra o arquivo VDO-
else Print.dpk. Clique em Compile e depois em
Result := AValue + Result;
end; Install. Feche o pacote (não há necessidade
de salvar). No IDE do Delphi vá ao menu
var
{ o Qry fornecerá os dados do banco } Tools>Enviroment Options e na aba Libra-
Qry: TQuery; ry acesse Library Path.
{ Variável da classe TVDODmPrinter que fará o relatório }
VDOImpressao: TVDODmPrinter;
Linha: string;
begin Adicione pasta de instalação/src do compo-
Qry := TQuery.Create(Self); nente e feche os editores. Veja na Figura 5 os
{ Informa o alias do banco }
Qry.DatabaseName := ‘DBDemos’; componentes instalados no IDE do Delphi.
{ Instrução SQL }
Qry.SQL.Text := A seguir, temos uma descrição de cada
‘select empno, firstname, salary from employee’;
{ Abre o DataSet pra que traga os registros da tabela }
componente:
Qry.Open; - TVDOPrinter: Destinado a impressão em
VDOImpressao := TVDODmPrinter.Create(Self); impressoras jato de tinta, laser ou matri-
VDOImpressao.Title := ‘Impressão matricial DevMedia (ClubeDelphi)’;
VDOImpressao.Paper.AutoNewPage := True; ciais. É uma classe que encapsula as pro-
VDOImpressao.ShowPreview := True; priedades, eventos e métodos das classes
VDOImpressao.ShowProgress := True;
TVDOCaPrinter e TVDODmPrinter, com
with VDOImpressao do apenas uma funcionalidade a mais que é
begin
BeginDoc; permitir ao desenvolvedor e/ou ao usuário
Print(0, ‘Listagem’, True); (opcionalmente) a escolha do tipo de im-
Print(0, ‘Código | Salário | Nome’, True);
Print(0, StringOfChar(‘-’,80), True);
pressora desejada no momento do início
{ Posiciona no primeiro registro } da impressão;
Qry.First;

{ Enquanto não chegar no último registro imprime no relatório } - TVDOCaPrinter: Destinado a impressão em
while not Qry.Eof do impressoras jato de tinta e laser. Utiliza inter-
begin
{ Na variável Linha será feito a concatenação namente o objeto TPrinter da VCL em con-
dos dados pra que saiam formatados } junto com o Canvas para efetuar a escrita.
Linha := Format(‘%6.6d’,
[Qry.FieldByName(‘empno’).AsInteger])+’ | ‘+ Utiliza o driver instalado no Windows e obe-
CompletaComBranco(FormatFloat(‘#,###,##0.00’, Qry.FieldByName(‘salary’).AsFloat), 11, dece a todas as configurações de perfil do
lEsquerdo)+’ | ‘+ Qry.FieldByName(‘firstname’).AsString;
Print(0, Linha, True); usuário e da impressora. Pode ser utilizado
{ Próximo registro } em impressoras matriciais, mas a impressão
Qry.Next;
end;
é consideravelmente mais lenta;
FreeAndNil(Qry);
EndDoc; - TVDODmPrinter: Destinado a impressão
end;
FreeAndNil(VDOImpressao); em impressoras matriciais. Efetua gravação
end; do documento no spool de impressão do
Windows. Não envia os dados diretamente
às portas de impressão, e por esse motivo
pode ser usado, sem nenhum problema,
com impressoras USB e de rede. Utiliza o
padrão Epson de caracteres de controle da
impressora (Escapes).
O código da listagem anterior cria uma tendo como objetivo mostrar como é sim-
conexão com o banco dbdemos e faz uma ples, rápido e fácil fazer um relatório para
consulta na tabela employee trazendo todos imprimir em impressora matricial em
os dados. Criamos um laço e adicionamos Delphi usando os componentes VDO.
os registros dentro do objeto com uma Recomendo o uso desses componentes, Figura 5. Componentes instalados no IDE
formatação simples de cada linha um re- pois, é realmente muito rápido a impres-
gistro. A Figura 4 mostra o nosso exemplo são usando as classes do VDO, basta
acessando um banco de dados. pesquisar as propriedades e eventos a Links
fundo do mesmo e olhar os exemplos em
Site do VDO
Conclusão anexo ao componente. vdo.sourceforge.net
O exemplo desenvolvido é bem básico, Bons estudos e até a próxima!

Edição 86 - ClubeDelphi 41

Clube86.indb 41 02.07.07 15:01:48


Publique na Web seus relatórios do
QuickReport

E
ste artigo vai mostrar como é Criando o projeto Web
possível aproveitar relatórios do Como a idéia é utilizar relatórios já
QuickReport para serem exibidos desenvolvidos, vamos adicionar as units
na Web, através da exportação para PDF. que contenham relatórios ao nosso projeto.
Com isso, você não precisará reconstruir Também teremos uma página inicial, que
seus relatórios existentes para que pos- funcionará como um menu, exibindo to-
sam ser disponibilizados na Web, po- dos os relatórios disponíveis ao usuário.
dendo ser acessados a partir de qualquer Para criar uma aplicação WebBroker
lugar da Internet. acesse o menu File>New>Other e na aba
New escolha o ícone Web Server Applica-
Instalações necessárias tion (Figura 1).
Primeiramente precisamos ter a paleta Na tela seguinte escolha o tipo de apli-
de componentes do QuickReport dispo- cação CGI Stand alone executable. Se você
nível no IDE do Delphi 7. Se você não tem interesse em saber as diferenças des-
está com a paleta visível, acesse o menu ses tipos de aplicação, pode acessar um
Component>Install Packages. Clique no artigo de minha autoria, através do link:
Fabrício Desbessel botão Add e, dentro do diretório Bin do www.devmedia.com.br/articles/viewcomp.
(fabricio@fabricio.pro.br) Delphi, escolha o arquivo dclqrt70.bpl. asp?comp=1652. Salve a unit e para o pro-
é professor de Linguagem de Programação do Como o QuickReport não possui um
Curso Técnico em Informática do Colégio Fre- componente ou método para exportar o www.devmedia.com.br/clubedelphi/portal.asp
derico Jorge Logemann de Horizontina/RS e relatório em PDF, será necessário utilizar
da FAHOR Faculdade Horizontina. Delphiano de Acesse agora o mesmo o portal do assinante ClubeDelphi e assista a
coração está sempre disposto a provar que com
o componente ExportQR. A instalação é uma vídeo aula de Luciano Pimenta que mostra como trabalhar com o
o Delphi sempre teremos a melhor solução. Site simples (o componente está para down- ExportQR.
www.fabricio.pro.br. load com os fontes do artigo). www.devmedia.com.br/articles/viewcomp.asp?comp=3130

42 ClubeDelphi - Publique na Web seus relatórios do QuickReport

Clube86.indb 42 02.07.07 15:01:49


MÃO NA MAS S A

jeto informe o nome “Relatorios”.


Nesse momento, temos disponível um
Web Module (parecido com o Data Modu-
le) para adicionar os componentes neces-
sários para o funcionamento da aplicação,
bem como as ações e códigos.

Adicionando os relatórios
Nesse momento já é possível adicio-
nar ao projeto as units dos relatórios
existentes. Para tanto, clique no botão
Add file to project e selecione as units que
contenham relatórios, desenvolvidos com
o QuickReport.
Depois de adicionar as units ao projeto,
é necessário certificar-se que a aplicação
Figura 1. Criando o projeto Web Server Application
não criará os formulários na sua inicia-
lização, o que poderá gerar uma exceção.
No menu Project>Options>Forms coloque Listagem 1. Declarando variáveis ao projeto
todos os formulários de relatórios para uses Forms;
...
Available forms (direita), deixando so- Classes: Array[1..5] of TFormClass;
mente o WebModule1 no Auto-create forms Formularios: Array[1..5] of TForm;
Menu: Array[1..5] of string;
(esquerda). Qtde: Integer;
Link: string;

Nota: É importante salientar que nes- Listagem 2. Informando os relatórios disponíveis no OnCreate
te exemplo, os formulários que con-
procedure TWebModule1.WebModuleCreate(Sender: TObject);
tenham os relatórios do QuickReport begin
Classes[1] := TrelEmpregados;
possuam os componentes de acesso a Formularios[1] := relEmpregados;
Menu[1] := ‘Listagem de Empregados’;
dados no próprio relatório. Classes[2] := TrelPaises;
Formularios[2] := relPaises;
Menu[2] := ‘Listagem de Países’;
Nesse momento precisamos dizer ao Qtde := 2;
end;
WebModule1 para utilizar as units dos
formulários que contenham relatórios. Listagem 3. Código HTML do PageProducer ppMenu
Para fazer isso acesse o menu File>Use
<html>
Unit, selecione os formulários e pressio- <head><title>Clube Delphi - Exportação PDF</title></head>
<body>
ne OK. <p><font color=”#000000”><strong>
Para que o WebModule saiba quais os <font size=”3” face=”Verdana, Arial, Helvetica, sans-serif”>
Sistema Relat&oacute;rios na WEB - Clube Delphi</font>
relatórios estão disponíveis, vamos criar </strong></font></p><p>
<font size=”2”><strong><font color=”#000000” face=”Verdana, Arial, Helvetica, sans-serif”>
arrays para os formulários, classes, op- Menu de Relat&oacute;rios</font>
</strong></font></p>
ções de menu, bem como variáveis para a <hr/>
quantidade de relatórios e link dos PDFs <p><#menu></p>
</body>
gerados. Acesse o código do WebModule1 </html>

e na seção var acrescente o código da


Listagem 1.
Depois de declarar as variáveis deve- Criando as ações nente ao WebModule.
mos informar os formulários que foram Primeiramente vamos criar uma ação Da paleta Internet, adicione um PagePro-
adicionados ao projeto. Isso é feito no (action), que exibirá uma página listando ducer e altere seu Name para “ppMenu”.
evento OnCreate do WebModule1. Para os relatórios disponíveis. Clique duas Na propriedade HTMLDoc informe o
exemplificar, codifiquei para dois relató- vezes sobre o WebModule para abrir o código da Listagem 3.
rios, conforme mostra a Listagem 2. editor de ações. Clique no botão Add New Note que no código, que é um HTML
Para cada relatório você deverá infor- para inserir. normal, temos uma tag <#menu> que
mar sua classe, seu Name e o nome que Altere a propriedade Name para “wai- o Delphi reconhecerá e codificaremos
aparecerá no menu. Ao final, a variável Menu”, coloque “/Menu” na propriedade para, no lugar dela, ser exibido a lista
Qtde deve receber a quantidade de rela- PathInfo e mude para True a propriedade de relatórios. Para isso acesse o evento
tórios inseridos no projeto e que estarão Default. Antes de colocar o código da ação OnHTMLTag do ppMenu e coloque o có-
disponíveis na Web. precisamos adicionar mais um compo- digo da Listagem 4.

Edição 86 - ClubeDelphi 43

Clube86.indb 43 02.07.07 15:01:51


Volte ao editor de ações, clicando duas
Listagem 4. OnHTMTag do ppMenu
vezes sobre o WebModule e selecione a
ação waiMenu. Acesse o evento OnAction procedure TWebModule1.ppMenuHTMLTag(Sender: TObject;
Tag: TTag; const TagString: string;
e adicione o seguinte código: TagParams: TStrings; var ReplaceText: string);
var
i: integer;
Response.Content := ppMenu.Content; begin
if TagString = ‘menu’ then
Nesse momento podemos fazer nosso begin
for i := 1 to Qtde do
primeiro teste. Lembre-se antes de com- begin
ReplaceText := ReplaceText + ‘<a href=”http://localhost/Scripts/’ +
pilar, de mudar o diretório de saída do ‘Relatorios.exe/Rel?Form=’ + IntToStr(i) + ‘” target=”_blank”>’ +
Menu[I]+’</a><br>’;
projeto para ficar dentro de C:\Inetpub\ end;
Scripts que é o diretório padrão do IIS que end;
end;
executa scripts CGI.
Listagem 5. Código HTML do PageProducer ppRelatorio
Importante: É necessário dar permissão <html>
de execução ao diretório Scripts para <head>
<title>Clube Delphi - Exportação PDF</title>
que ocorra o funcionamento correto da <script language=”JavaScript”>
<!--
aplicação, além de ter o IIS instalado cla- function MM_goToURL() {
var i, args=MM_goToURL.arguments;
ro. Para isso, acesse o Adicionar/Remover document.MM_returnValue = false;
Programas no Painel de Controle. Clique for (i=0; i<(args.length-1); i+=2) eval(args[i]+”.location=’”+args[i+1]+”’”);
}
em Adicionar/remover componentes do //-->
</script>
Windows, escolha Internet Information </head>
Services (IIS) e clique em Detalhes. Na ja- <body bgcolor=”#FFFFFF”
onLoad=”MM_goToURL(‘parent’,’<#rel>’);
nela seguinte, escolha Serviço da World return document.MM_returnValue”>
</body>
Wide Web e clique em Detalhes. Marque </html>
a opção Diretório Virtual Scripts e clique
em OK nas janelas abertas.

Para fazer essa mudança acesse o menu


Project>Options, aba Directories/Conditio-
nals e Output directory. Compile a aplica-
ção com o CTRL+F9 e abra no browser o
seguinte endereço:

http://localhost/scripts/Relatorios.exe

Nota: Se você tiver um programa para


acelerar downloads, ele pode tentar sal-
var o arquivo ao invés de executar. Para
contornar, digite juntamente o PathInfo
da ação. Nesse caso o link ficaria:
http://localhost/scripts/Relatorios.
Figura 2. Menu de relatórios da aplicação em funcionamento
exe/Menu.

Será mostrada uma tela semelhante a informando o código da Listagem 5 na a função Exporta, conforme o seguinte
Figura 2. propriedade HTMLDoc. código:
A idéia do ppRelatorio é redirecionar a
function Exporta(Classe: TFormClass;
Exportando para PDF página para o PDF exportado, através Formulario: TForm): string;
Para exportar os relatórios escolhido no de uma função JavaScript. Em tempo de
menu, crie uma nova ação no WebModule execução vamos alterar a tag <#rel> para Pressione SHIFT+CTRL+C para que o
(clicando duas vezes sobre o mesmo) e o caminho do PDF. Para isso, no evento Delphi implemente a estrutura da função
depois no Add New. Configure a proprie- OnHTMLTag do ppRelatorio, adicione o e codifique-a conforma é apresentado na
dade Name para “waiRelatorio” e PathInfo seguinte código: Listagem 6.
para “/Rel”. A função será responsável por criar o
if TagString = 'rel' then
Adicione ao WebModule um ExportQR. ReplaceText := 'http://localhost/'+Link+'.PDF'; formulário em tempo de execução, pre-
Coloque também mais um PageProducer, parar o relatório e exportá-lo. Você pode
alterando seu Name para “ppRelatorio” e Na seção private do WebModule, declare exportar para qualquer diretório que

44 ClubeDelphi - Publique na Web seus relatórios do QuickReport

Clube86.indb 44 02.07.07 15:01:52


MÃO NA MAS S A

esteja disponível no IIS. No exemplo foi


Listagem 6. Código da função Exporta
exportado para C:\inetpub\wwwroot que é
uses QuickRpt; o diretório padrão de páginas do IIS.
...
function TWebModule1.Exporta(Classe: TFormClass; Formulario: TForm): string;
begin
try Nota: Ao publicar a aplicação em um
Formulario := Classe.Create(WebModule1);
ExportQR1.Report:= Formulario.FindComponent(‘QuickRep1’) as TQuickrep;
servidor, lembre-se que no diretório
(Formulario.FindComponent(‘QuickRep1’) as TQuickrep).Prepare; onde serão exportados os PDFs, a conta
ExportQR1.ExportQRPDF(‘C:\inetpub\wwwroot\’+Formulario.Name, True);
Result := Formulario.Name; IUSR deverá ter permissão para gravar
except
Result := ‘’; os arquivos.
end;
end;
Para finalizar, vamos codificar a wai-
Listagem 7. OnAction da ação waiRelatorio Relatorio. No evento OnAction coloque o
procedure TWebModule1.WebModule1waiRelatorioAction(Sender: TObject; Request: TWebRequest; código da Listagem 7.
Response: TWebResponse; var Handled: Boolean);
var
Agora compile a aplicação e abra no
Consulta: string; browser o endereço: http://localhost/scripts/
Indice: Integer;
begin Relatorios.exe/Menu. No menu de relató-
Consulta := Request.QueryFields.Values[‘Form’];
if Consulta <> ‘’ then rios, clique em link um e confira a abertura
begin do relatório em PDF (Figura 3). Lembre-se
Indice := StrToInt(Consulta);
Link := Exporta(Classes[Indice], Formularios[Indice]); que é necessário ter instalado algum sof-
if Link <> ‘’ then
Response.Content := ppRelatorio.Content tware leitor de PDF na sua máquina.
else
Response.Content := ‘Não foi possível gerar o relatório!’;
end;
end;
Conclusão
Neste artigo vimos uma forma interes-
sante de reutilizar relatórios desenvolvi-
dos em QuickReport, disponibilizando-os
para serem acessados pela Web, dando
mobilidade aos usuários que poderão
acessá-los de qualquer ponto de internet.
Lembre-se de criar um controle de
usuários que poderão ter acesso aos rela-
tórios e ainda podendo definir grupos de
relatórios que cada usuário pode acessar.
Cuidado com o número de usuários onli-
ne que utilizarão o sistema. Para permitir
mais conexões ao mesmo tempo opte por
uma aplicação ISAPI ao invés de CGI.
Pense nisso. Coloque a mão na massa e
disponibilize seus relatórios na Web.

Figura 3. Relatório exportado na Web

Edição 86 - ClubeDelphi 45

Clube86.indb 45 02.07.07 15:01:54


Boas práticas para trabalhar em equipe

A s metodologias de desenvolvimento
foram criadas visando coordenar
esforços, estabelecer ordem, padrões e
que visam facilitar o trabalho em grupo
e, mais do que isso, garantir que o grupo
trabalhe de forma correta e gere ao final
técnicas. Entre as melhores metodologias do processo um produto de qualidade.
estão O Guia do Conjunto de Conhecimentos
Fernando Sarturi Prass em Gerenciamento de Projetos (PMBOK) do Divisão de tarefas
(fernando@dotbr.com.br) Project Manager Institute (PMI), o Capabi- O fator fundamental para que o trabalho
é Mestre em Ciência da Computação pela UFSC. lity Maturity Model (CMM) e o modelo em equipe obtenha sucesso é a correta
Professor da Universidade Luterana do Brasil brasileiro desenvolvido pela SOFTEX ba- divisão das tarefas. A forma mais correta
(ULBRA) nos campus de Santa Maria e Cacho-
seado no CMMI chamado de Melhoria de a ser seguida na divisão é tentar atender a
eira do Sul. Sócio-diretor da dotBR Soluções em
TI (www.dotbr.com.br), empresa que presta Processo do Software Brasileiro (MPS.BR). duas afirmações: cada um deve fazer aquilo
serviços de desenvolvimento de sistemas e de Por essas metodologias serem bastante que sabe (assim não haverá necessidade
consultoria em Bancos de Dados e Metodolo- completas, o seu uso, muitas vezes, se de tempo de aprendizado) e sempre que
gias de Desenvolvimento. torna difícil em pequenas empresas ou possível deve fazer sozinho (a divisão de
pequenos grupos de programadores. uma tarefa entre duas ou mais pessoas,
Cristiano Caetano
Entretanto, por menor que seja o grupo muitas vezes obriga um dos membros
(c_caetano@hotmail.com)
é certificado pela Associação Latino-Americana ou a empresa, o desenvolvimento de da equipe a ficar parado esperando pela
de Teste de Software e autor do livro CVS: Con- sistemas em equipe requer uma série de finalização do trabalho do outro).
trole de Versões e Desenvolvimento Colaborativo cuidados para que o trabalho de um não Por exemplo, suponha um determinado
de Software. Com 10 anos de experiência, já tra- seja refeito ou alterado erroneamente por sistema que usa funções que estão num
balhou na área de qualidade e teste de software
outro membro do time. Web Service. Pode-se designar um pro-
para grandes empresas como Zero G, DELL e HP
Invent. O autor também pode ser contatado em Este artigo mostrará uma série de gramador experiente para codificar as
spaces.msn.com/softwarequality. pequenas dicas e ferramentas, muitas classes e exportá-las como Web Services
delas retiradas dos modelos já citados, e passar a construção das telas e dos mó-

46 ClubeDelphi - Boas práticas para trabalhar em equipe

Clube86.indb 46 02.07.07 15:01:58


BOAS PRÁTIC AS

dulos locais (processos bem mais simples) Para ganhar tempo, sem perder qualida-
para programadores com menor experi- de, os autores aconselham que a equipe
ência ou até mesmo para estagiários. faça ao menos os seguintes documentos:
A divisão correta das atividades do pro- • Estrutura Analítica do Projeto (EAP);
jeto traz benefícios como a economia com • Casos de uso e Diagramas de Caso de
programadores, pois não é necessário Uso (Use Cases);
manter um time inteiro de profissionais • Diagramas de Classes;
experientes, e facilidades no controle de • Modelo de Dados.
versão e na gerência da segurança (no
exemplo dado, os profissionais com me- A Estrutura Analítica do Projeto (Figura
nor experiência não precisariam ter aces- 1), também conhecida como Work Breakdo-
so aos fontes das classes por exemplo). wn Structure (WBS), é uma estrutura em
Para finalizar, é preciso que os membros forma de árvore que organiza e define o
da equipe saibam exatamente quem faz o escopo total do projeto. É uma ferramenta
que no projeto. Esse artigo traz uma seção de decomposição do trabalho, cujo obje-
intitulada Matriz de Designação de Respon- tivo principal é identificar os itens que
sabilidades que trata sobre o assunto. serão desenvolvidos (PMBOK, 2004).
Aqueles que estão habituados ao uso de
Documentação da Análise softwares de gestão de projetos, como o
O projeto de um novo software começa Project ou Open WorkBench não encon- Figura 1. Parte da EAP de um projeto de desenvolvimento
sempre pela análise. Bem, ao menos de- trarão nenhuma dificuldade para montar
veria começar! É por essa razão que se diz uma. Maiores informações sobre a EAP informações contida nele.
que a documentação é a base de qualquer podem ser obtidas em en.wikipedia.org/ Existem motivos que praticamente obri-
trabalho de desenvolvimento. wiki/wbs. gam a documentação do código-fonte,
Os softwares possuem um ciclo de vida Os Diagramas de Caso de Uso e de vale destacar os seguintes:
com constantes alterações. A documen- Classes não serão explicados aqui por • Evita o retrabalho (exemplo: um pro-
tação é um importante instrumento a ser serem amplamente conhecidos pelos gramador escrever uma classe ou função
utilizado para manter o histórico, pois desenvolvedores, além do mais, esse não já desenvolvida por outro);
conta toda a história do aplicativo, desde é o foco do artigo. Maiores informações • Diminui o número de interrupções
sua concepção, passando pelo desenvol- sobre eles pode ser obtidos em Spínola e para sanar dúvidas (ao invés de pergun-
vimento e chegando até o acompanha- Araújo (2006) na seção Links. tar para quem escreveu determinado
mento da manutenção. método quais parâmetros que ele aceita,
Pode parecer desnecessário fazer tais Documentação do código-fonte basta que olhe o documento);
afirmações já que elas são de senso co- Embora essa seção também trate sobre • Mantém o histórico do projeto em todo
mum, mas muitas vezes essa fase não é documentação, ela foi separada da anterior o seu ciclo de vida;
concluída em sua plenitude para que sobre por ser a fase de desenvolvimento mais • Facilita a substituição e a incorporação de
mais tempo para a fase de programação. longa do projeto, logo é nela que estão con- membros à equipe de desenvolvimento;
Entretanto para que a documentação centrados os maiores esforços da equipe. • Facilita a visualização do tamanho do
possa cumprir o seu papel, ela necessita Pode-se afirmar que a documentação produto, uma vez que todos os módulos
ser elaborada durante cada uma das fa- do código-fonte é um dos artefatos que podem ser reunidos em um único docu-
ses de desenvolvimento e ser atualizada tornam o desenvolvimento e manutenção mento;
sempre que houver uma modificação, de sistemas, seja ele individual ou coleti- • Acrescenta qualidade ao produto final
independente da metodologia adotada vo, mais simples e eficiente. entregue ao cliente.
pela empresa. Deve ser vista como uma O problema é que normalmente a docu-
ferramenta de auxílio a ser utilizada por mentação do código não é feita ou, quando Além dos motivos citados, existe um que
todas as pessoas envolvidas no processo feita, não é atualizada. As razões (para não é o principal: facilita a manutenção do sis-
e não como uma obrigação a mais. escrever as desculpas) mais comuns são: tema. No site da SoftConsult (www.softcon-
Para que o processo de documentação falta de tempo, indisposição ou até mesmo sult.se) tem um provérbio que diz: If it was
não se torne um entrave ao andamento o desconhecimento de suas vantagens. hard to write, it should be hard to read ou
do projeto é preciso que a equipe escolha Por causa da complexidade e prin- algo como se foi difícil codificar, deve ser
corretamente que tipos de documentos cipalmente da alta mutabilidade do mais difícil ler e entender o código.
serão gerados na fase de análise. Não é código-fonte, não basta apenas elaborar Essa frase define bem a dificuldade
necessário dizer que o ideal seria gerar o o documento inicial, é preciso também encontrada pelos programadores no
máximo de documentos úteis possíveis, que ele acompanhe todas as manuten- momento em que são obrigados a dar
entretanto a realidade (leia-se: a falta de ções e alterações efetuadas no sistema, manutenção num determinado sistema.
tempo) não permite que isso seja feito. para que seja mantida a integridade das Entretanto não basta que exista um do-

Edição 86 - ClubeDelphi 47

Clube86.indb 47 02.07.07 15:02:00


cumento, é preciso que ele tenha uma
formatação clara e padronizada a fim de
que sua leitura seja facilitada.
Uma vez escolhido um padrão para títu-
los, subtítulos, corpo, comandos e comen-
tários, ele deve ser seguido durante todo
o documento. Outro ponto importante é o
formato do documento, deve-se dar prio-
ridade a um que possua fácil distribuição
e recursos que facilitem a ligação entre as
seções. O formato ideal é o HTML, pois
é multi-plataforma e permite o uso de
imagens e outros recursos.
Felizmente hoje a documentação do
código-fonte pode ser feita de maneira
quase automática, através de softwares
específicos como, por exemplo, o Delphi-
Doc (Figura 2).
Desenvolvido pela empresa sueca Sof-
tConsult baseado no JavaDoc da Sun (que
documenta códigos Java como o próprio
nome sugere), o DelphiDOC gera um
site HTML com toda a documentação Figura 2. Tela inicial do DelphiDOC
do sistema (classes, métodos, funções,
arquivos etc.).
Listagem 1. Exemplos de comandos

Padrões de Projetos { Exemplo 1 }


if variavel = 1 then begin
Observe os dois códigos na Listagem 1, meu_contador := 1;
eles fazem exatamente a mesma coisa. Na valor_total := 0;
while meu_contador < 10 do begin
verdade são praticamente idênticos, mas valor_total := valor_total + meu_contador;
meu_contador = meu_contador +1;
estão formatados de maneira diferente. Se end;
end
num código tão pequeno a diferença já é else valor_total := 10;
gritante, imagine num código-fonte com
{ Exemplo 2 }
duzentas linhas ou mais. if Variavel = 1 then
begin
Para piorar a situação, suponha que a MeuContador := 1;
ValorTotal := 0;
equipe seja formada por três pessoas, cada while MeuContador < 10 do
uma escrevendo a sintaxe de uma forma. begin
ValorTotal := ValorTotal + MeuContador;
Se, por acaso, algum programador tiver MeuContador := MeuContador +1;
end;
que dar manutenção em um código escrito end
por outro, antes mesmo de aprender as else
begin
regras de negócio do módulo que necessita ValorTotal := 10;
end;
de manutenção, ele terá de aprender a ler o
programa escrito pelo colega, aumentando
Listagem 2. Exemplo de padrões para comandos
assim o tempo de trabalho.
Os padrões de projetos existem, entre variavel
variavel_composta
outras coisas, justamente para evitar for <variavel> := <inicio> to <fim> do
begin
isso. A Listagem 2 traz alguns exemplos <comandos>;
de padrões a serem seguidos por todos end;

os programadores durante a codificação if <condicao> then


begin
para tornar o código o mais homogêneo, <comandos>;
end
facilitando assim a leitura e a manutenção
do programa. else
begin
O leitor pode adaptar a sintaxe dos co- <comandos>
end;
mandos para que fique da forma como já
(...)
está acostumado (por exemplo: ao invés
de usar a forma variavel_composta pode

48 ClubeDelphi - Boas práticas para trabalhar em equipe

Clube86.indb 48 02.07.07 15:02:01


BOAS PRÁTIC AS

usar VariavelComposta). O importante é PROJETO XYZ


que toda a equipe receba um documen- EQUIPE
to com o formato que deve escrever o FASE
Ana José Paula Pedro ...
código-fonte (if, while, for, try..except, nome Análise ...
de arquivos, nome de classes etc.) e use Elaboração de Casos de Uso R R I ...
esse padrão durante todo o projeto. Elaboração de Diagramas de Classe R A ...
Modelo de Dados I C R ...
Matriz de Designação de ... ... ... ... ... ...
Responsabilidades Desenvolvimento ...
Tão importante quanto escrever tudo Módulo de Segurança C A, I R ...
da mesma forma, é saber quem é o res- Cadastros Básicos A R ...
ponsável pelo que no projeto. Para tanto, ... ... ... ... ... ...
existe um artefato chamado de Matriz Tabela 1. Matriz de Designação de Responsabilidades do Projeto XYZ
de Designação de Responsabilidades
(RAM - Responsibility Assignment Matrix)
que define as responsabilidades de cada
membro da equipe durante o projeto.
Uma matriz de responsabilidades (MR)
é usada para ilustrar as conexões entre
um trabalho que precisa ser realizado
e membros da equipe do projeto. Em
projetos maiores, é possível desenvolver
as MRs em vários níveis.
Por exemplo, uma MR de alto nível pode
definir os grupos ou unidades da equipe
do projeto responsáveis pelos compo-
nentes da EAP, enquanto MRs de nível
mais baixo são usadas dentro do grupo
para atribuir funções, responsabilidades
e níveis de autoridade para atividades
específicas (PMBOK, 2004).
O formato matricial, às vezes chamado Figura 3. TortoiseCVS
de tabela, permite visualizar todas as
atividades associadas a uma pessoa e código-fonte, a primeira tratava da impor- Sem discutir em detalhes, o funcio-
também o contrário, ou seja, todas as tância da documentação e a segunda da namento do CVS se dá por meio da
pessoas associadas a uma atividade. padronização. Falta ainda tratar do controle importação dos arquivos de um projeto
É comum utilizar um tipo de MR denomi- de versões do código-fonte, para tanto exis- para um repositório de arquivos. Cabe
nado gráfico RACI onde são atribuídas as te o Concurrent Versions System (CVS). observar que as demais operações são
seguintes funções aos membros da equipe: O CVS é uma ferramenta open source compostas pela transferência de arquivos
Responsável, Reporta-se, Consultoria e que implementa as principais funções entre o servidor que abriga o repositório
Informa (Responsible, Accountable, Consult pertinentes ao processo de controle de e o computador cliente.
e Inform, em inglês). Entretanto, conforme versões, armazenando em seu repositório Dessa forma, assim que o projeto for
mostra a Tabela 1 é possível adaptar a as modificações realizadas num arquivo importado, o programador pode iniciar a
matriz para outras atividades, como por ao longo do tempo. transferência dos arquivos armazenados
exemplo: Analisa (A), Modela (M), Desen- Cada modificação é identificada por um no repositório para a área de trabalho e
volve (D), Testa (T) e Instala (T). número chamado Revisão. Toda Revisão ar- realizar as modificações necessárias.
mazena as modificações realizadas, quem Tão logo essas modificações sejam
Controle de Versões realizou as modificações, quando foram concluídas, deverá realizar a entrega
Como já foi dito antes, a programação é realizadas, entre outras informações. dos arquivos a fim de submeter as mo-
a fase mais longa do desenvolvimento de Qualquer Revisão poderá ser rastreada dificações realizadas sob o controle de
um software. Por ser a mais longa, obvia- para fins de consulta, comparação ou união versões, como pode ser visto no exemplo
mente é a fase que consome mais recursos com outras Revisões. O CVS conta com um apresentado na Figura 3.
e também a mais sujeita a falhas, logo ela mecanismo capaz de controlar os acessos
deve receber uma atenção especial por simultâneos e as modificações paralelas, Nota: para saber mais sobre o Tortoise
parte da gerência do projeto. garantindo a integridade das modificações CVS veja a edição 76 da ClubeDelphi.
Esse artigo já dedicou duas seções sobre o e a atomicidade das operações.

Edição 86 - ClubeDelphi 49

Clube86.indb 49 02.07.07 15:02:02


O programador pode realizar a trans- requisito imprescindível para garantir a produto o mais rápido possível.
ferência dos arquivos armazenados no sobrevida de um software no mercado. Revisões formais, estratégias de garan-
repositório que foram modificados para Portanto, nesse contexto, podemos con- tia de qualidade, planos de testes, gerên-
a sua área de trabalho por meio de uma cluir que as empresas mais competitivas cia de riscos, entre outras atividades estão
operação de sincronização. são as empresas que trabalham sob a ótica se tornando cada vez mais comuns no co-
Por último, o CVS também oferece da melhoria contínua dos processos para tidiano das empresas. Em contrapartida,
recursos para criar ramos de desen- aumentar a qualidade do processo de para dar sustentação a essa mudança de
volvimento paralelos, assim como, um desenvolvimento e, conseqüentemente, paradigma podemos notar a proliferação
mecanismo para designar um rótulo para aumentar a qualidade do produto final. de ferramentas open source e comerciais
identificar um conjunto de Revisões em Estima-se que o custo decorrente da cor- para a execução e gerência das atividades
determinado ponto no tempo. reção de um bug cresce dramaticamente à de teste e qualidade de software.
A qualidade de um projeto de software medida que ele é descoberto em fases mais Essa tendência também pode ser obser-
é diretamente proporcional a qualidade adiantadas no processo de desenvolvimento. vada na evolução das suítes de desenvol-
dos processos adotados nas diversas No entanto, ainda existe uma forte tendência vimento disponíveis no mercado. Tanto a
fases do seu ciclo de vida. O controle nos desenvolvedores em negligenciar essa Microsoft quanto a Borland estão apostando
de versões é visto como uma extensão realidade e não dedicar o tempo mínimo todas as fichas em suítes de desenvolvimen-
natural do processo de desenvolvimento, necessário para a realização das atividades to que suportam e fornecem ferramentas
permitindo que se possa paralelizar o de qualidade e testes de software. para todo o ciclo de vida do desenvolvimen-
desenvolvimento de forma coerente e pa- A necessidade de utilizar mecanismos to, assim como ferramentas para a criação e
dronizada, especialmente em se tratando de garantia de qualidade de software gerência de testes funcionais, testes de carga, 9

de um conjunto de desenvolvedores. tem crescido à medida que as aplicações unidade, entre outros.
Por essa razão, sem uma ferramenta ou tornaram-se mais complexas. Esse cres- 1

metodologia adequada, esse controle torna- cimento tem sido exponencial em virtude Conclusão
se extremamente complexo e improdutivo. da nossa dependência cada vez maior de Esse artigo trouxe uma série de dicas e 1

É importante ressaltar que o CVS fornece aplicativos que controlam atividades de ferramentas, algumas delas baseadas em 1

uma abordagem consistente, controlada missão crítica; seja num hospital, na bolsa metodologias e outras obtidas na vivên-
e flexível para gerenciar a evolução das de valores ou no ônibus espacial. cia profissional dos autores, que visam 1

mudanças, garantindo a integridade e a ras- Em termos práticos, isso significa que facilitar o desenvolvimento em equipe.
treabilidade dos arquivos modificados. cada vez mais cedo as empresas estão Procurou-se mostrar técnicas para todas 1

Entre as motivações para usar o CVS, introduzindo atividades de qualidade de as fases do ciclo de vida de um projeto de 1

pode-se destacar as seguintes: software nas diversas fases do desenvol- desenvolvimento de software, da análise
• É uma ferramenta open source, madura vimento. Mesmo os bons softwares são aos testes, dando um maior destaque evi- 1

e amplamente utilizada em projetos ao repletos de defeitos. dentemente para a programação.


redor do mundo; Para ilustrar, uma taxa de 0.51 defeitos a
2
• Existe uma grande quantidade de cada mil linhas de código é considerada www.devmedia.com.br/clubedelphi/portal.asp
documentação disponível na internet; aceitável pela indústria de TI. Ou seja, to-
Acesse agora o mesmo o portal do assinante ClubeDelphi e assista a uma
• É uma ferramenta multi-plataforma. dos os softwares têm defeitos, no entanto,
vídeo-aula de Guinther Pauli que mostra como trabalhar com o DUnit
Existem clientes e servidores do CVS porta- os softwares realmente bons têm poucos para testes unitários no Delphi.
dos para Windows, DOS, Linux, e outras. defeitos críticos não corrigidos; promo- www.devmedia.com.br/articles/viewcomp.asp?comp=540 9
vendo assim uma operação mais estável
Qualidade e Teste de Software no ponto de vista do usuário final.
Para finalizar, cabe falar sobre a neces- Mas afinal, nos softwares atuais, por Referências 1

sidade de garantir a qualidade do pro- que existem tantos defeitos? Poderíamos PMBOK - Guia do Conjunto de Conhecimentos em
1
duto que está sendo entregue ao cliente. escrever um livro para tentar responder Gerenciamento de Projetos. Project Management Institute
1
Desenvolver software de qualidade não a essa pergunta e, ainda assim, talvez (PMI). 3ª ed. 2004.
é mais um requinte para poucos, trans- não seriam identificadas todas as causas
SPÍNOLA, Rodrigo Oliveira. ARAÚJO, Marco Antônio P. UML na
formou-se num fator de competitividade possíveis. No entanto, pode-se elencar 1
Prática: Construindo Diagramas de Classes. SQL Magazine,
num mercado cada vez mais exigente. algumas causas como as mais prováveis,
ed. 34, 2006.
O filósofo Nietzsche, no século passado, como as destacadas abaixo:
1
alertava: Com o aumento da competição, a • Falta de comunicação entre os mem-
1
qualidade se torna mera propaganda. Vence bros da equipe; Links
aquele que melhor engana. Essa receita é • A complexidade do software;
Home page do TortoiseCVS
muito simples e fácil de seguir, todavia, • Erros de programação; www.tortoisecvs.org
1

quem tomar esse tipo de postura estará • Mudança de requerimentos no meio


DelphiDoc
fadado ao fracasso. do projeto e requerimentos ambíguos; www.softconsult.se/delphidoc.html
Nos dias de hoje, a qualidade tornou-se • Pressão da gerência para terminar o

50 ClubeDelphi - Boas práticas para trabalhar em equipe

Clube86.indb 50 02.07.07 15:02:04


31 de Agosto e
01 de Setembro
em São Paulo / SP
O Evento do desenvolvedor
Web e Wireless
Palestras 100% técnicas!
Garanta sua participação e conheça o que as
tecnologias Java e .net trazem de
melhor para este mercado!
Para mais informações acesse:
www.devmedia.com.br/eventos/webmobiletechweek
Realização: Confira abaixo a Grade de Palestras:

Salas de Java Salas de .NET


1º dia - 31 de Agosto 1º dia - 31 de Agosto
Sala 1 Sala 2 Sala 3 Sala 1 Sala 2 Sala 3
9:00-10:20 Interfaces ricas na Web com Padrões de Projeto Java EE - WEB 2.0: Conceitos e Desenvolvimento para Linq - veja na prática os Ciclo de vida de desenvolvimento
Ajax - Parte 1 Parte 1 Tecnologias dispositivos móveis na fundamentos deste projeto com VSTS
plataforma .NET 2.0 - Parte 1

10:30-11:50 Interfaces ricas na Web com Padrões de Projeto Java EE - Mercado de Jogos para Celulares Desenvolvimento para Mini-curso de C# - Parte 1 ASP.NET Caching
Ajax - Parte 2 Parte 2 dispositivos móveis na
plataforma .NET 2.0 - Parte 2

12:00-13:20 Almoço Almoço Almoço Almoço Almoço Almoço

13:30-14-50 Mini Curso 1 - Java ME Struts2 - A Evolução do Utilizando SVG (Scalable Vector Introdução ao ASP.NET AJAX Mini-curso de C# - Parte 2 XAML
Primeiros Passos - Parte 1 framework - Parte Graphics) com Java ME

15:00-16:20 Mini Curso 1 - Java ME Struts2 - Desenvolvendo uma Otimização de Aplicações Java ME Aprenda na prática a criar um Acionamento de dispositivos ASP.NET 2.0 na prática: Profiles e
Primeiros Passos - Parte 2 aplicação com ajax e validação - sistema de enquetes em eletro-mecanicos com Pocket Membership
Parte 1 ASP.NET - Parte 1 PC

16:20-17:00 BREAK BREAK BREAK BREAK BREAK BREAK

17:00-18:20 Além do básico: explorando Struts2 - Desenvolvendo uma iReport/JasperReport - Criando Aprenda na prática a criar um Novidades no Windows Mobile Criação de um portal com ASP.NET
APIs sofisticadas com Java ME aplicação com ajax e validação relatórios para web sistema de enquetes em 6 2.0 e WebParts
nas plataformas Nokia - Parte1 Parte 2 ASP.NET - Parte 2

18:30-19:50 Além do básico: explorando JavaFX: interfaces em Java de Google Web Toolkit passo-a-passo Desenvolvendo programa para Introdução ao Silverlight Desenvolvendo Gadgets
APIs sofisticadas com Java ME forma fácil e portável - Parte 1 - Parte 1 PocketPC com rede sem fio e Corporativos para Windows Vista
nas plataformas Nokia - Parte2 WebServices - Parte 1

JavaFX: interfaces em Java de Google Web Toolkit passo-a-passo Desenvolvendo programa para Criando aplicações WAP com .net 3.0 - Xaml Browser
20:00-21:20 Interfaces ricas para a Web forma fácil e portável - Parte 2 - Parte 2 PocketPC com rede sem fio e ASP.NET Applications
com Flex 2.0 e Java WebServices - Parte 2

2º dia - 01 de Setembro 2º dia - 01 de Setembro


Sala 1 Sala 2 Sala 3 Sala 1 Sala 2 Sala 3
9:00-10:20 Implementando uma loja Desenvolvendo um Game em Planejamento e Execução de Testes Aplicações com ASP.NET 2.0 e Otimizando aplicações em Aplicações Web para dispositivos
virtual em Java EE 5 com JPA Java ME - Parte 1 em Aplicações Web - Parte 1 AJAX - Parte 1 Windows Mobile 5.0 móveis
e EJB3 - Parte 1
Construindo uma aplicação Linq para Compact Framework
10:30-11:50 Implementando uma loja Desenvolvendo um Game em Planejamento e Execução de Testes Aplicações com ASP.NET 2.0 e Mobile passo a passo com
virtual em Java EE 5 com JPA Java ME - Parte 2 em Aplicações Web - Parte 2 AJAX - Parte 2 Visual Studio 2005
e EJB3 - Parte 2

12:00-13:20 Almoço Almoço Almoço Almoço Almoço Almoço

13:30-14-50 Implementando uma loja Desenvolvendo um Game em Java Wireless: O que nós precisamos Construa uma aplicação 100% Envio e recebimento de dados Silverlight na prática
virtual em Java EE 5 Java ME - Parte 3 saber sobre MIDP3? OO com ASP.NET - Parte 1 no Pocket usando FTP
utilizando JSF para a interface
com o usuário.- Parte 1

15:00-16:20 Implementando uma loja Introdução ao uso do bluetooth Mini Curso 2 – Conectividade em Java Construa uma aplicação 100% Windows Mobile no Windows WorkFlow Foundation no ASP.NET
virtual em Java EE 5 em J2ME ME - Do básico ao avançado - Parte 1 OO com ASP.NET - Parte 2 CE
utilizando JSF para a interface
com o usuário.- Parte 2

16:20-17:00 BREAK BREAK BREAK BREAK BREAK BREAK

17:00-18:20 Desenvolvendo e implantando Desenvolvendo aplicações Mini Curso 2 – Conectividade em Java AJAX Control Toolkit SQL Server 2005 Compact Desenvolvimento WEB: Uma
uma força de vendas em Palm Web com o Spring Framework ME - Do básico ao avançado - Parte 2 Edition - Parte 1 Abordagem Utilizando OO e
- Parte 1 – Parte 1 Técnicas de Teste de Software

18:30-19:50 Desenvolvendo e implantando Desenvolvendo aplicações Multimídia em J2ME RenatoFaria e Net Framework 3.0 - Conheça SQL Server 2005 Compact Testes automatizados em
uma força de vendas em Palm Web com o Spring Framework eduardo peixoto o WPF e surpreenda-se Edition - Parte 2 aplicações ASP.NET
- Parte 2 – Parte 2

Patrocínio: Apoio Cultural: Apoio:


Cursos Online
A Revista ClubeDelphi oferece para seus assinantes uma
série de Cursos Online de alto padrão de qualidade .
Conheça abaixo os cursos já disponíveis..

Curso em destaque

Aplicações
Client/Server com
dbExpress e Firebird
Confira neste curso online como criar uma
aplicação client/server completa no Delphi 7,
utilizando dbExpress e Firebird.
Aprenda também: Como trabalhar com um
driver dbExpress específico e gratuito para o
Firebird ; Como utilizar Orientação a Objetos,
e técnicas como herança visual de formulários e
relatórios ; saiba como colocar regras de
negócios no banco de dados, usando Triggers
e Stored Procedures; E crie relatórios com o
Quick Report, Rave Reports e Report
Builder, e muito mais.
Confira o plano de aula completo:
www.devmedia.com.br/clienteserver

A sua melhor opção de aprendizagem!


Assine a ClubeDeplhi e comece já seu treinamento!
www.devmedia.com.br/assine

Outros cursos disponíveis: www.devmedia.com.br/curso


Curso Online - Sistema SysCash Criando uma Aplicação multi-camadas Completa com Delphi
Aplicações client/server com Windows Aplicações WEB com IntraWeb e Delphi 7 (Delphi Win32)
Forms no Delphi 2006

Mais Informações: www.devmedia.com.br/central - Tel.: 21 2220-5375 / 2220-5435

Você também pode gostar