Revista The Club Megazine - 10/2003
A utilização, reprodução, apropriação, armazenamento em banco de dados,
sob qualquer forma ou meio, de textos, fotos e outras criações intelectuais
em cada publicação da revista “The Club” são terminantemente proibidos
sem autorização escrita dos titulares dos direitos autorais.
Copyright© The Club® 2003
EDITORIAL
Editorial
THE CLUB
Av. Celso Ferreira da Silva, 190
Jd. Europa - Avaré - SP - CEP 18.707-150
Informações: (0xx14) 3732-3689
Suporte: (0xx14) 3733-1588 - Fax: (0xx14) 3732-0987
Internet
http://www.theclub.com.br
Cadastro: [email protected]
Suporte: [email protected]
Informações: [email protected]
Dúvidas
Correspondência ou fax com dúvidas devem ser
enviados ao - THE CLUB, indicando "Suporte".
Opinião
Se você quer dar a sua opinião sobre o clube em
geral, mande a sua correspondência para a seção
"Tire sua dúvida".
Reprodução
A utilização, reprodução, apropriação,
armazenamento em banco de dados, sob qualquer
forma ou meio, de textos, fotos e outras criações
intelectuais em cada publicação da Revista
“The Club” são terminantemente proibidos sem
autorização escrita dos titulares dos direitos
autorais.
Copyright© The Club® 2003
Olá amigos,
É com muito prazer que trazemos para vocês mais uma edição da revista The Club
Megazine. Antes de mais nada gostaria de agradecer a todos os nossos associados que
nos enviam temas de matérias que gostariam de vê-las publicadas em nossa revista. E
aproveito também a oportunidade para convidar você que gostaria que abordassemos
um determinado assunto para que envie um e-mail para [email protected], que
teremos o maior prazer em lhe atender.
Nesta edição temos várias informações vitais para o desenvolvimento de qualquer
programa, como por exemplo o trabalho com campos lookup.
Uma das grandes preocupações, senão a maior, de cada programador é em relação
a performance da sua aplicação.
E todos sabem que um campo lookup pode fazer com que a sua aplicação fique
lenta, mas nesta edição você verá os macetes para não comprometer a sua aplicação.
Outra preocupação dos programadores é em relação ao visual da aplicação.
Quem trabalha com o Windows XP pôde notar que o formato das caixas de texto,
caixas de seleção mudaram, mas quando você roda a sua aplicação que foi compilada
principalmente em versões anteriores ao Delphi 7 o visual continua como era no
Windows 2000, Windows 98. A partir de agora você poderá fazer com que a sua
aplicação fique com a cara do Windows XP. Aprenda nesta edição a utilizar o XP
Manifest.
Voltando ao tema sobre acesso a dados, vamos falar mais sobre este poderoso
componente chamado ClientDataSet. Veja nesta edição como trabalhar com tipo
Aggregate e GroupState. Você aprenderá como efetuar cálculos simples no Aggregate e
também melhorar a apresentação dos dados utilizando o GroupState.
Você já imaginou incluir dentro do seu executável um arquivo de som? Nesta
edição você verá uma matéria que mostra como é extremamente simples fazer este tipo
de trabalho.
Falando sobre internet nós estamos trazendo nesta edição uma matéria sobre envio
de e-mails utilizando os excelentes componentes da palheta Indy. Estes componentes
começaram a vir com o Delphi a partir da versão 6.
E como não poderia deixar de ser também estamos trazendo as colunas de Dicas &
Truques e também Perguntas & Respostas, como sempre com informações muito
interessantes.
Por enquanto é só, até o próximo mês com mais novidades !
Abraços e até lá.
Impressão e acabamento:
Impressos Gril - Gril Gráfica e Repr. Ind. Ltda.
Tel.: (0xx14) 3762.1345 - Fax: (0xx14) 3762.1259
Rua São Paulo, 447 - Cep 18.740-000
Taquarituba - SP
Tiragem: 5.000 exemplares
Celso Jefferson Paganelli
Presidente - The Club
Diretor - Presidente
Celso Jefferson M. Paganelli
Diretor Técnico
Mauro Sant’Anna
Delphi é marca registrada da Borland International, as
demais marcas citadas são registradas pelos seus
respectivos proprietários.
Editorial ........................................................................
Perguntas & Respostas ......................................................
Lookup - Trabalhando com campos virtuais no dbExpress Macetes
para não comprometer a performance ....................................
Usando o XP Manifest no Delphi ...........................................
Enviando Emails via Delphi .................................................
Retrospectiva .................................................................
Usando Aggregates e GroupState no ClientDataset ......................
Distribuindo um arquivo dentro do executável ...........................
Dicas & Truques ..............................................................
MeGAZINE
3
03
04
06
11
13
16
18
26
28
Perguntas & Respostas
Pergunta: Gostaria de saber como capturar o conteúdo
digitado em um célula de um StringGrid e jogar para a área de
transferência, porém, isso com a célula ainda em edição.
Resposta: Quando uma célula do StringGrid está em
edição, automaticamente é instanciado um objeto do tipo
TinplaceEditor, através do qual é possível obter o texto que está
sendo digitado, veja abaixo como proceder:
Pergunta: Gostaria de saber como mudar o tamanho de um
campo no Sql-Server.
Por exemplo: o campo NOME tem 20 posições e eu gostaria de
passar para 30 posições usando Delphi.
Resposta: No caso do SQLServer, poderá executar a
instrução abaixo utilizando um componente Query, por exemplo.
ALTER TABLE CADGRUPOS
ALTER COLUMN DESCRICAO VARCHAR(100)
type
TSGrid = class(TStringGrid)
public
property InplaceEditor;
end;
var
Form1: TForm1;
Dúvida enviada por João Roberto, São José do Rio Preto/SP.
Pergunta: Como faço para ir para um determinado
diretório através de um comando do Delphi (o diretório que está o
executável que está rodando)?
implementation
{$R *.dfm}
Resposta:
procedure TForm1.SpeedButton1Click(Sender: TObject);
begin
{Transfere do StringGrid para a area de transf.}
TSGrid(StringGrid1).InplaceEditor.CopyToClipboard;
{ Descarrega da area de transferencia para o Memo }
Memo1.PasteFromClipboard;
end;
Dúvida enviada por Informenge Processamento de Dados
Ltda., Sorocaba/SP.
4
Poderá utilizar as seguintes funções:
ChDir(ExtractFilePath(Application.ExeName));
Onde a função ChDir, faz muda a pasta corrente, e a função
ExtractFilePath extrai um path, neste caso o path do executável.
Dúvida enviada por PHN Consultoria, Rio do Sul/SC.
Pergunta: Quando estou no evento
DBNavigator.BeforeAction é possível cancelar ou abortar a ação
MeGAZINE
Perguntas & Respostas
que seria tomada, por exemplo, Refresh?
Resposta: No evento OnBeforeAction do DBNavigator
poderá fazer o seguinte tratamento:
if Button = nbRefresh then
Abort;
Pergunta: Há como tornar a propriedade Ctrl3D visível no
Object Inspector no Delphi5?
Resposta: Sim, para isso, clique com o botão direito do
mouse sobre o Object Inspector, vá em “View” e marque o ítem
“Legacy”.
Dúvida enviada por RDC Informática, Gramado/RS.
Dúvida enviada por Moked do Brasil, São Paulo/SP.
Pergunta: Estou começando a usar o IntraWeb agora e
achei no Suporte On-Line do TheClub uma dica entitulada de
“INTRAWEB - UTILIZANDO ARQUIVO DE ESTILO CSS”.
Fiz todos os passos como descritos na dica, funcionou tudo
direitinho (usei um memo para testes), porém ele some com a
scrollbars do browser! O que fazer para aplicar o estilo e ao
mesmo tempo mostrar a scrollbar do browser?
Resposta: O que está ocorrendo é que o Sr. está alterando
as configurações de “BODY” e com isso, irá afetar diretamente o
browser. Caso necessite apenas alterar as configurações de
“Memos”, deverá criar o css alterando apenas as configurações
referentes “TextArea”, como mostra o exemplo abaixo:
Pergunta: Estou desenvolvendo uma função em meu
aplicativo onde necessito alterar o foco da janela ativa entre o
meu aplicativo Delphi e uma janela de um documento aberto no
word.
Gostaria de saber como posso alternar via programação o foco
da janela, entre o Delphi é o word (no meu documento) pois o
usuario pode ter algum outro documento do word aberto também.
Resposta: Isso é possível executando um FindWindow para
pesquisa a Handle da janela do Word e depois enviar uma
mensagem para colocá-la em evidência:
a:link, a:active, a:visited {
color: #CBAC57;
text-decoration: underline}
var
W: Hwnd;
begin
W := FindWindow(‘OpusApp’,
if W <> 0 then
BringWindowToTop(W)
else
Caption := ‘Não achei’;
end;
a:hover {
color: #B28816;
text-decoration: none}
Input {border: 0}
TextArea
{
border: 0
scrollbar-face-color: #000000;
scrollbar-shadow-color: #CBAC57;
scrollbar-highlight-color: #CBAC57;
scrollbar-3dlight-color: #FFFFFF;
scrollbar-darkshadow-color: #FFFFFF;
scrollbar-track-color: #FFFFFF;
scrollbar-arrow-color: #CBAC57;
overflow: hidden
}
nil);
{ Se souber o caption da janela. }
var
W: Hwnd;
begin
W := FindWindow(‘OpusApp’,
if W <> 0 then
BringWindowToTop(W)
else
Caption := ‘Não achei’;
‘Document9’);
end;
Dúvida enviada por Concentro Marcas Ltda., Santa
Dorotéira/MS.
Dúvida Virtual Technology, Belo Horizonte/MG.
MeGAZINE
5
Delphi
Lookup
Trabalhando com campos virtuais no dbExpress
Macetes para não comprometer a performance
por Alessandro Ferreira ([email protected])
Introdução
Temos recebido uma grande quantidade de emails de
programadores que estão migrando suas aplicações desktop para
ambiente Client/Server ou mesmo N-Tier com dúvidas em
relação a campos virtuais que até então eram criados no objeto
TTable utilizando o prático recurso de “Lookup”.
Sem dúvidas, um campo lookup é bastante simples e prático
para trabalhar, mas em um ambiente Client/Server, onde as
tabelas tendem a ter um volume expressivo de dados, o
comprometimento da performance é inevitável, visto que para
cada registro da tabela principal, será necessário uma pesquisa na
tabela detalhe, afim de trazer o valor correspondente para o campo
lookup.
Neste pequeno artigo, iremos demonstrar uma técnica
simples, porém, não muito utilizada por falta de conhecimento,
mas que sem dúvidas traz ótimos resultados principalmente em
relação a performance, vamos lá!
Banco de dados e Conexão
Para este artigo, vamos utilizar o banco de dados Interbase/
Firebird, aproveitando o banco Employee.gdb que é instalado
juntamente com este servidor, mas especificamente as tabelas
Sales, Customer e Employee, as quais possuem relacionamento entre
si, conforme apresentado na Figura 1:
Figura 1 Relacionamentos
6
MeGAZINE
Delphi
Para começarmos, crie um novo projeto (File | New |
Application) e salve-o. Após isso, adicione um DataModule (File |
New | DataModule) ao mesmo, nomeando-o como dmPrincipal, e
save a unit como unDM. Continuando, adicione um componente
sqlConnection, dê um duplo clique sobre o mesmo e adicione uma
conexão para o banco Employee.gdb, conforme sugere a Figura 2.
Continuando, iremos detalhar o Select utilizado no
componente sdsSales. Observe, que na tabela SALES, temos apenas
o código do cliente (CUST_NO) e o código do empregado
(SALES_REP), até aqui, nenhuma novidade... Porém, nossa
necessidade é trazer o nome do cliente e o nome do empregado,
contudo, sem utilizar campo Lookup, visto os mesmos não serem
recomendados neste ambiente. Para isso,
fizemos uma junção entre as tabelas através
da cláusula LEFT OUTER JOIN, onde
informamos o nome da tabela onde iremos
buscar a informação e na cláusula “ON”
informamos quais os campos de
relacionamento. Dessa forma,
automaticamente serão criados campos
adicionais, o quais nomeamos de
NOMECLIENTE e NOMEEMPREG, como o
próprio nome sugere, irão trazer o “nome do
cliente” e “nome do empregado”, respectivamente.
DataSetProvider e ProviderFlags e
ClientDataSet
Adicione agora, três componentes
DataSetProvider fazendo as configurações
sugeridas no Quadro 2.
Figura 2 – Conexão sqlConnection.
Clique em OK, depois altere a propriedade LoginPrompt para
False, Connected para True e Name para cnxEmployee, com isso,
temos pronta a nossa conexão.
Para fazer acesso as tabelas, adicione três componentes
sqlDataSet e configure as propriedades conforme sugere o
Quadro 1.
Para prosseguirmos, adicione os TFields no sdsSales (clicando
com o botão direito e selecionando “Add All Fields”). Agora, iremos
fazer a configuração da propriedade ProviderFlags dos campos
resultantes de joins (nossos campos “virtuais). Quando
trabalhamos com componente DataSetProviders, estes componentes
são responsáveis em gerar as instruções de atualização ao banco
de dados (Inserts, Updates e Deletes) e para isso, estes analisam os
campos disponíveis no componente sqlDataSet ao qual
está ligado.
Observe que entre os campos que foram
adicionados ao sdsSales, os campos resultantes de joins
também foram adicionados, dessa forma, quando o
DataSetProvider tentar atualizar a tabela SALES, ele irá
tentar atualizar os campos NOMECLIENTE e
Quadro 1 – Configurações dos sqlDataSets.
Quadro 2 – Configurações nos DataSetProviders.
MeGAZINE
7
Delphi
NOMEEMPREG, visto não ter como diferenciar se estes campos
existem fisicamente na tabela ou não, e consequentemente
gerando uma exceção avisando que não encontrou os referidos
campos.
Felizmente temos uma forma bastante simples para
informar ao DataSetProvider quais campos deverão ser atualizados.
Para isso, selecione os campos NOMECLIENTE e NOMEEMPREG e
marque False para todas as opções da propriedade ProviderFlags
destes dois campos, pois, dessa forma o DataSetProvider saberá que
não deve envolver estes campos no processo de atualização da
tabela SALES.
Por último, vamos adicionar três componentes ClientDataSets
através dos quais iremos fazer a manutenção dos dados, etc...
Confira as configurações no Quadro 3.
Quadro 3 – Configuração dos ClientDataSets
Implementando o Formulário de Edição
Continuando, iremos implementar agora o formulário
responsável em fazer a edição dos dados da tabela SALES, veja a
sugestão deste formulário na Figura 3.
Dica:
Poderá encontrar um artigo sobre
implementação de formulários de
pesquisa em nossa revista de
Setembro/2003
no
artigo
“Pesquisa em Banco de Dados –
Aprenda a implementar um
formulário
genérico
para
pesquisas”, a qual também está
disponível em nosso site,
www.theclub.com.br.
Estando pronto o nosso
formulário para edição,
vamos implementar a
validação do código do cliente
e empregado, o qual o
usuário poderá digitar
diretamente nos DBEdits ou,
caso não os saiba, poderá
clicar no botão de pesquisa
para localizar os mesmos.
Iremos fazer a validação
no evento OnExit dos próprio DBEdits, como demonstra o código
apresentado na Listagem 1.
procedure TfmSales.DBEdit2Exit(Sender: TObject);
begin
with dmPrincipal do
begin
if (dsSales.State in [dsInsert, dsEdit]) then
{ Valida código digitado }
if not LocateSQL(‘CUSTOMER’, cdsCustomer,
[‘CUST_NO’], [DBEdit2.Text]) then
begin
ShowMessage(‘Cliente não cadastrado!’);
DBEdit2.SetFocus;
end
else
{ Atualiza o campo “Virtual” }
cdsSales.FieldByName(‘NOMECLIENTE’).
AsString := dsCustomer.FieldByName
(‘CUSTOMER’).AsString;
end;
end;
procedure TfmSales.DBEdit3Exit
(Sender: TObject);
begin
with dmPrincipal do
begin
if (dsSales.State in
[dsInsert, dsEdit]) then
{ Valida código digitado }
if not LocateSQL(‘EMPLOYEE’,
dmPrincipal.cdsEmployee,
[‘EMP_NO’],
[DBEdit3.Text]) then
begin
ShowMessage(‘Empregado não
cadastrado!’);
DBEdit3.SetFocus;
Figura 3 – Formulário edição da tabela SALES.
8
MeGAZINE
Delphi
end;
finally
fmPesquisa.Free;
end;
end;
end;
end
else
{ Atualiza o campo “Virtual” }
dmPrincipal.cdsSales.FieldByName
(‘NOMEEMPREG’).AsString :=
dmPrincipal.cdsEmployee.FieldByName
(‘FULL_NAME’).AsString;
end;
procedure TfmSales.sbtnEmpregadoClick(Sender:
TObject);
begin
with dmPrincipal do
begin
fmPesquisa := tfmPesquisa.Create
(Self, dmPrincipal.cdsEmployee,
‘EMPLOYEE’);
try
if (fmPesquisa.ShowModal=mrOK) and
(dsSales.State in
[dsInsert,dsEdit]) then
begin
{ Atribui valores retornados da Pesquisa }
cdsSales.FieldByName(‘SALES_REP’).
AsString := cdsEmployee.FieldByName
(‘EMP_NO’).AsString;
{ Atualiza o campo “Virtual” }
cdsSales.FieldByName(‘NOMEEMPREG’).
AsString := cdsEmployee.FieldByName
(‘FULL_NAME’).AsString;
end;
finally
fmPesquisa.Free;
end;
end;
end;
Listagem 1 – Validações no OnExit.
Neste exemplo, utilizamos uma função chamada LocateSQL a
qual criamos exatamente para fazer validação de valores,
passando como parâmetro o nome da tabela, o componente
ClientDataSet (referente a tabela “lookup”), a lista de campos e a
lista de valores, e tendo como resultado um tipo boolean. Caso a
função encontre os valores pesquisados, já teremos o ClientDataSet
posicionado no referido valor, basta pegar por exemplo o nome do
cliente e atribuir ao campo “virtual” (resultante do join)., visto a
atualização do campo “virtual” não ser automática no caso de
novos registros...
Vamos agora para a implementação do código dos botões de
pesquisa, onde estamos utilizando a abordagem sugerida no
artigo da revista de Setembro/2003, conforme sugerido
anteriormente. Confira a Listagem 2.
procedure TfmSales.sbtnClienteClick(Sender:
TObject);
begin
with dmPrincipal do
begin
fmPesquisa := tfmPesquisa.Create
(Self, cdsCustomer, ‘CUSTOMER’);
try
if (fmPesquisa.ShowModal=mrOK) and
(dsSales.State in
[dsInsert,dsEdit]) then
begin
{ Atribui valores retornados da Pesquisa }
cdsSales.FieldByName(‘CUST_NO’).
AsString :=
cdsCustomer.FieldByName(‘CUST_NO’).
AsString;
{ Atualiza o campo “Virtual” }
cdsSales.FieldByName(‘NOMECLIENTE’).
AsString := cdsCustomer.FieldByName
(‘CUSTOMER’).AsString;
end;
Listagem 2 – Código dos botões de pesquisa.
Na Listagem 3 estamos apresentando o código da função
LocateSQL que utilizamos para fazer as validações dos valores
digitados nos DBEdits.
MeGAZINE
function LocateSQL(TableName: String; DataSet:
TClientDataSet; const KeyFields,
KeyValues: Array of Variant; const SQLPadrao:
string = ‘’): Boolean;
var
i: Integer;
aSql, aWhere: String;
9
Delphi
begin
try
with DataSet do
begin
// monta cláusula where.
for i := 0 to High(KeyFields) do
begin
aWhere := aWhere+’UPPER(‘+KeyFields[i]+’) =
‘+QuotedStr(UpperCase(KeyValues[i]))+’ ‘;
if i < High(KeyFields) then
aWhere := aWhere+’ AND ‘;
end;
// monta Select.
if SQLPadrao<>’’ then
aSql := Format(‘%s where %s ‘,
[PreparaSQLPadrao(SQLPadrao), aWhere])
else
aSql := Format(‘Select * From %s where %s’,
[TableName, aWhere]);
Close;
CommandText := aSql;
Open;
Result := not IsEmpty;
end;
except on E: Exception do
begin
ShowMessage(‘Erro no LocateSQL: ‘+E.Message);
Result := False;
end;
end;
end;
Quadro 4 – Parâmetros da função LocateSQL.
para a substituição de campos Lookup dentro do ambiente Client/
Server ou mesmo N-Tier.
A princípio, isso pode parecer um pouco mais complicado para
quem está vindo de aplicações desktop, mas podemos afirmar que
o resultado apresentado utilizando a técnica aqui exposta traz
ótimos resultados e compensa este trabalho. Outro detalhe a
ressaltar, é o fato que esta abordagem pode ser extendida à
aplicações que utilizam BDE, IBX, ADO, ou qualquer outra engine de
acesso que esteja trabalhando.
Forte abraço à todos e até a próxima,
Listagem 3 – Função LocateSQL.
Download
Acompanhe no Quadro 4 a explicação dos parâmetros
requeridos pela função LocateSQL.
O projeto de exemplo utilizado neste artigo está disponível
para download em:
Em resumo, a função apenas monta uma instrução SQL com
uma cláusula where, fazendo uso dos parâmetros informados,
atribuiu esta instrução à propriedade CommandText do
ClientDataSet (também recebido como parâmetro), abre o mesmo e
caso retorne algum registro, resulta True, caso contrário, False.
Conclusão
Tentamos demonstrar neste simples artigo uma abordagem
10
http://www.theclub.com.br/revista/download/dbxLookup.zip.
Sobre o autor
Alessandro Ferreira,
Consultor Técnico do The Club
[email protected]
MeGAZINE
Delphi
Usando o XP Manifest no Delphi
Por Claudinei Rodrigues – [email protected]
Aqui você vai encontrar as informações sobre como deixar a
sua aplicação compilada em versões anteriores ao Delphi 7, com
os controles iguais ao Windows XP.
O Windows XP tem um gerente de temas que muda a
aparência da maioria dos controles e padrão das janelas. A
Microsoft afirma que existe uma versão mais velha do arquivo
comctl32.dll que contém o código para manter as diferentes
plataformas do Windows.
A introdução de “temas” no Windows XP foi incluída dentro
do código contido no arquivo comctl32.dll. Agora são mantidas as
duas versões no mesmo controle. A mais antiga (versão 5.8) é a
versão que é compatível com todas as versões do Windows
incluindo a versão XP, e a mais nova (versão 6) é compatível
apenas com o XP (e algumas futuras plataformas do Windows).
Por default, todos os programas escritos no Windows XP
optarão pela versão 5.8. Para incorporar a versão 6.0, você deve
incluir em sua aplicação uma declaração, que chamamos de
Manifest, para que o sistema operacional leia o gerente dos temas
e assim adote o ambiente do Windows XP.
O que seria este Manifest? O Manifest é um documento XML
que deve ser incluído dentro do arquivo de recursos do seu
executável. Normalmente este recurso é reservado para outras
coisas como imagens, ícones e cursores de mouse.
Qualquer informação pode ser incluída em um arquivo de
recurso. O documento XML quando colocado de forma correta
dentro de um arquivo de recurso permitirá ao Windows XP
decidir por qual versão da comctl32.dll ele irá utilizar.
Para incluir este código XML em sua aplicação você deve
primeiro conhecer as constantes apresentadas pela Microsoft.
Quando incluído um novo recurso no executável, existe um grupo
de números e itens associados a este recurso.
O grupo de números é normalmente classificado com um
nome amigável. Por exemplo, se você usar um demo que
acompanha o Delphi chamado ResXPlor que está disponível no
diretório C:\Program Files\Borland\Delphi5\Demos\Resxplor,
você poderá ver os grupos Strings, Bitmaps, Icons ou Cursors.
Estes nomes são apenas representações amigáveis de um grupo
de números.
O grupo de números para o Manifest é 24, conforme o
cabeçalho em C distribuído pela Microsoft. O número do item do
Manifest para determinar a versão do comctl32.dll é 1. Esta
informação vem a ser necessária quando você for construir o seu
MeGAZINE
11
Delphi
novo arquivo de recurso para ser ligado aos seus executáveis.
Para criar um arquivos .RES nós vamos precisar primeiro
criar um arquivo .RC que designa o seu documento XML para
um grupo apropriado e para uma linha apropriada. No arquivo
zip contido neste documento, você irá encontrar dois arquivos:
WindowsXP.rc e WindowsXP.Manifest.
O arquivo WindowsXP.rc contém suas instruções para
incluir o documento (XML) WindowsXP.Manifest como item 1
ligado ao grupo 24. O conteúdo do WindowsXP.RC é o seguinte:
</dependentAssembly>
</dependency>
</assembly>
Agora que já temos os dois arquivos, nós vamos precisar usar
o compilador de arquivos de recurso do Delphi. Fazendo isto
teremos um arquivo .RES que poderemos incluir em nossas
aplicações. A partir de uma linha de comando digite o seguinte:
C:\ProjetoTeste\brcc32 WindowsXP.rc
Depois de você ter compilado o arquivo WindowsXP.rc, você
verá o arquivo WindowsXP.res no mesmo diretório.
1 24 “WindowsXP.Manifest”
O manifest, é um documento XML que contém informações
sobre a aplicação que você está escrevendo e também informação
a respeito da versão da comctl32.dll para uso.
A seguir você poderá ver um exemplo de um código XML que
você poderá fazer algumas modificações para ser utilizado na sua
aplicação.
<?xml version=”1.0" encoding=”UTF-8"
standalone=”yes”?>
<assembly
xmlns=”urn:schemas-microsoft-com:asm.v1"
manifestVersion=”1.0">
<assemblyIdentity
name=”www.theclub.com.br”
processorArchitecture=”x86"
version=”5.1.0.0"
type=”win32"/>
<description>Windows Shell</description>
<dependency>
<dependentAssembly>
<assemblyIdentity
type=”win32"
name=”Microsoft.Windows.Common-Controls”
version=”6.0.0.0"
processorArchitecture=”x86"
publicKeyToken=”6595b64144ccf1df”
language=”*”
/>
12
O ultimo passo para fazer com que a sua aplicação se torne
compatível com o Windows XP é incluir este recurso em sua
aplicação. O caminho mais fácil para fazer isto é incluir em seu
arquivo .DPR ou no formulário principal da sua aplicação a
seguinte diretiva:
{$R WindowsXP.RES}
O melhor local para se fazer isto é logo após a linha {$R
*.DFM}. Uma vez incluída esta declaração em sua aplicação,
basta compilar e executar. O gerenciador de tema do Windows
vai fazer com que a sua aplicação fique com os controles iguais do
Windows XP.
Até aqui a única discrepância encontrada foi com o
componente TListView.
Se você utilizar o componente TListView com a propriedade
ViewStyle como vsReport você terá um problema com a
propriedade TColumns. Em tempo de execução ocorrerá um erro
se você tentar utilizar o cabeçalho de colunas, mas apenas com a
propriedade ViewStyle com vsReport.
Download do exemplo:
http://www.theclub.com.br/revista/download/demoxp.zip
Sobre o autor
Claudinei Rodrigues,
Consultor Técnico do The Club
[email protected]
MeGAZINE
Delphi
Enviando Emails via Delphi
Enviando e anexando arquivos com Indy
por Alessandro Ferreira, [email protected]
Introdução
TIdSMTP
Desde o Delphi 6, a Borland introduziu uma suíte de
componentes chamada Indy, a qual possui uma grande variedade
de componentes, para as mais diversas tarefas possíveis,
principalmente relacionados a internet.
Finalidade
Apesar de estar disponível apenas a partir do D6, você poderá
baixar gratuitamente este pacote de componentes e instalar em
seu Delphi 5, bastando para isso acessar o site do fabricante,
http://www.indyproject.org/.
Componente responsável pela
implementação do protocolo SMTP
(Simple Mail Transfer Protocol Client),
ou seja, o protocolo utilizado no envio
de emails.
Propriedades principais
Neste pequeno artigo, demonstrarei como criar uma
aplicação para enviar emails via Delphi, com a possibilidade de
enviar arquivos anexos ao mesmo, vamos lá...
Componentes
AuthenticationType
Tipo de autenticação requerida pelo
host SMTP
AuthSchemesSupported
Esquemas de autenticação aceitas
pelo host SMTP
Password
Senha para conexão/autenticação
UserName
Usuário para conexão/autenticação
TidMessage
Para este exemplo, iremos utilizar os componentes TIdSTMP
(aba Indy Clients) e o componente TIdMessage (aba Indy Misc),
os quais serão explicados no Quadro 1.
Finalidade
Propriedades principais
Implementação
Bem, agora vamos partir para a implementação do nosso
projeto de exemplo.
Para começar, crie um novo projeto (menu File | New |
Application) e adicione alguns componentes, como sugere a
Figura 1.
Encapsular o conteúdo e definições da
mensagem
From
Informar "quem" está enviando o email
Recipients
Informar destinatários
Subject
Informar assunto do email
Body
Corpo/texto da mensagem
Quadro 1 – Componentes Indy utilizados.
MeGAZINE
13
Delphi
Figura 1 – Layout sugerido
A seguir, na Listagem 1 apresentamos o código do sbtnAnexar,
através do qual iremos selecionar arquivos em disco e ir
adicionando ao ListArquivos (TListBox) responsável em
armazenar todos os arquivos que iremos anexar ao nosso email.
procedure TForm1.sbtnAnexarClick(Sender: TObject);
begin
if OpenDialog1.Execute then
ListaArquivos.Items.Add(OpenDialog1.FileName);
end;
Listagem 1 – Código do botão Anexar.
Agora, vamos para a implementação do código para envio do
email no evento OnClick do botão BT_Envia, confira a Listagem 2.
procedure TForm1.BT_EnviaClick(Sender: TObject);
var
i: integer;
begin
BT_Envia.Enabled := not BT_Envia.Enabled;
try
{ configura o Email. }
with IdMsgSend do
begin
14
MeGAZINE
From.Text := EdFrom.Text;
Recipients.EMailAddresses := EdTo.Text;
Subject := EdSubject.Text;
Body.Assign(MTexto.Lines);
end;
{ Configura os anexos, caso houver... }
for i := 0 to ListaArquivos.Items.Count-1 do
TIdAttachment.Create
(IdMsgSend.MessageParts,
ListaArquivos.Items[i]);
{ Envia o Email. }
with IdSMTP1 do
begin
{ configura com o seu servidor SMTP. }
Host := ‘smtp.seuprovedor.com.br’;
try
Connect;
Send(IdMsgSend);
finally
if Connected then
Disconnect;
end;
end;
finally
Delphi
BT_Envia.Enabled := not BT_Envia.Enabled;
ListaArquivos.Clear;
end;
end;
with IdSMTP1 do
begin
AuthenticationType := atLogin;
Host := ‘smtp.provedor.com.br’;
UserId := ‘sua_conta’;
Password := ‘sua_senha’;
try
Connect;
Send(IdMsgSend);
finally
if Connected then
Disconnect;
end;
end;
Listagem 2 – Código completo do botão Enviar.
A primeira linha do código, deixa o BT_Envia desabilitado
durante o processo de envio do email. Após isso, partimos para as
configurações de Origem do Email, Destinátário, Assunto e o texto de nosso
email. Feito isso, partimos para adicionar os arquivos à serem
anexado ao email (caso existam), fazendo um looping nos ítens do
componente ListaArquivos, criando uma “parte” dentro do IdMsgSend
(TidMessage) através do objeto TidAttachment, o qual descreve
ao IdMsgSend as informações sobre cada arquivo à ser anexado,
recebendo como parâmetro um MessagePart e o caminho+nome do
referido arquivo.
Estando a definição da mensagem definida no componente
IdMsgSend, iremos partir para a configuração da conexão SMTP,
que será responsável em enviar o email, utilizando-se de uma
conexão pré-estabelecida. Confira no Quadro 2.
Observe que adicionamos algumas linhas em relação ao
código original apresentado anteriormente, confira o Quadro 3.
IdSmtp. AuthenticationType
Quandro informado o valor atLogin,
prevê que serão informados
usuário e senha para autenticação
IdSmtp.Host
Endereço do servidor SMTP de seu
provedor
IdSmtp.UserId
Nome do usuário para
autenticação
IdSmtp.Connect
Conectar ao servidor SMTP (*)
IdSmtp.Password
Senha do usuário para
autenticação
IdSmtp.Send()
Envia o email, passando como parâmetro
a mensagem encapsulada no componente
IdMsgSend
IdSmtp.Disconnect
Desconecta do servidor SMTP
Quadro 2 – Parâmetro no IdSmtp
(*)
Antes de chamar o método
“Connect” do componente
IdSMTP, você já deve estar
conectado a internet, ou seja, o
método “Connect” do referido
componente não faz a
“discagem” para o provedor, e
sim somente, conecta ao servidor
SMTP utilizando-se de uma
conexão pré-estabelecida.
Conclusão
Procurei neste artigo, demonstrar uma forma bastante
simples de enviar emails com arquivos anexados, utilizando
componente da suite Indy. No site do fabricante, http://
www.indyproject.org/, poderá encontrar documentação e alguns
projetos de exemplo, os quais abordam basicamente as
funcionalidades do Indy. Caso tenha sugestões a respeito de
temas que gostaria ver publicado aqui na The Club Megazine, sintase a vontade em nos enviar que iremos fazer o possível para
atendê-lo!
Forte abraço à todos e até a próxima,
Download
O projeto de exemplo utilizado neste artigo está disponível
para download em http://www.theclub.com.br/revista/download/
EmailIndyAttach.zip.
Servidores x Autenticação
Geralmente, a autenticação não é exigida no envio de emails e
sim somente no recebimento dos mesmos. Contudo, existem
alguns provedores exigindo a autenticação também no envio de
emails e felizmente isso está previsto no componente IdSMTP,
conforme mostra a Listagem 3.
MeGAZINE
Sobre o autor
Alessandro Ferreira,
Consultor Técnico do The Club
[email protected]
15
The Club Megazine
Nossa retrospectiva continua.
Este mês, você pode relembrar da revista de 2001, onde
publicamos um artigo das ações que os usuários de sua
aplicação podem iniciar pressionando um botão em um
toolbar.
Também foi publicado como ver o tipo de conexão de
internet que o usuário possui, e outros truques na nossa
seção de dicas. Também tivemos várias páginas com
perguntas enviadas pelos nossos leitores.
Também importante, publicamos um artigo sobre XML
e ADO.NET, escrita por Mauro Sant´anna, já nos dando
um vislumbre do mundo .NET, onde o Delphi 8 (Octane) já
estará trabalhando como ambiente padrão.
O nosso colaborador Mario Bohm, também escreveu um
artigo interessante sobre Queries e SubQueries.
Todas estas matérias relacionada aqui, e outras
publicadas na mesma revista, podem ser encontradas em
nosso site: http://www.theclub.com.br.
O mês que vem iremos lembrar da revista do mês de
Setembro de 2000.
16
MeGAZINE
Delphi
Usando Aggregates e
GroupState no ClientDataset
por André Colavite - [email protected]
Este artigo descreve como usar Aggregates para efetuar
simples calculos e também como usar o Group State para
melhorar a apresentação dos dados ao usuário.
No total cinco estatisticas são suportadas pelo aggregate. São elas
count (Contador), minimum (Menor), maximum (Maior), sum
(Somatória) e average (Média).
Aggregates são objetos que podem executar automaticamente
calculos básicos baseando-se nos dados armazenados no
ClientDataSet. Group State é a informação que identifica a
relativa posição do registro dentro de um grupo de registros,
baseando-se no índice. Essas duas características juntas
permitem que você adicione habilidades para facilitar a
manutenção em seus aplicativos.
São de dois tipos os objetos que você pode usar para criar os
aggregates: TAggregate e TAggregateFields. Um TAggregate é
descedente da classe TCollectionItem e o TAggregateFields é
descendente da classe TField.
Se você não está familiarizado com o Aggregate ou Group
State, você pode estar se perguntando por que estou abordando
essas duas características no mesmo artigo. A resposta é simples.
Ambos estão associados com o groupping level, que é uma
característica relacionada ao índice. Como a discussão do
Aggregate necessariamente envolve o Grouping Level, a
abordagem do group state é natural.
Entendendo o Aggregate
O Aggregate é um objeto que pode executar automaticamente
calculos básicos através de um ou mais registros do
ClientDataSet. Por exemplo, imagine que você tem um
ClientDataset que contem uma lista de compras para seus
clientes. Se cada registro contem um campo que identifica o
cliente, o numero de items comprado, e o valor total da compra,
um aggregate pode calcular a soma de todas as compras através
de todos os registros da tabela. Contudo outro aggregate pode
calcular o numero médio de itens comprados para cada cliente.
18
Enquanto esses dois tipos de Aggregates são semelhantes em
sua configuração, eles diferem em seu uso. Especificamente o
TAggregatefield, por ser descendente de TField, pode ser
associado com um controle data-ware, permitindo que o valor do
aggregate possa ser mostrado automaticamente. Para
comparação, o TAggregate é um objeto cujo valor deve ser lido
explicitamente em tempo de execução.
Criando campo Aggregate
Campos agregado são campos virtuais, persistentes. Apesar
de serem semelhantes a outros campos virtuais, como os campos
calculados e campos de lookup, há uma diferença muito
importante.
Especificamente, adicionar um ou mais campos Aggregates
não impedem a criação dinâmica de campos em tempo de
execução, isso quer dizer que não nos obriga a incluir os demais
campos na lista de Tfields. Por outro lado se você adicionar
qualquer outro tipo de campo tipo Data, Lookup ou Calculado,
esses impedem o ClientDataSet de criar outros campos em tempo
MeGAZINE
Delphi
de execução. Sendo assim os campos
Aggregates podem ser criados em
tempo de desenvolvimento, tendo ou
não outros campos criados na lista
de Tfields.
A criação de campos Aggregates
requer vários passos específicos em
sua configuração, sendo assim
veremos agora quais são esses
passos:
o Adicionar um campo Aggregate no ClientDataSet. Isso poderá
ser feito em tempo de
desenvolvimento através da lista de
TFields ou em tempo de execução
através da propriedade Agregates do
ClientDataSet;
* Propriedade Expression, desse
campo Aggregate, definir o calculo
que o campo irá executar;
* Propriedade Indexname, indicar o índice que irá controlar o
agrupamento;
* Propriedade GroupingLevel, indicar qual o nível de
agrupamento dos registros;
* Propriedade Active, selecionar o valor True para ativá-lo;
* Propriedade Visible, selecionar o valor True;
* Configurar a propriedade AggregatesActive do
ClientDataSet para True.
Para um melhor entendimento sobre a criação de campo
Aggregate, vamos criar um simples projeto de exemplo, seguindo
os passos abaixo:
Imagem 1
Nos próximos passos, criaremos o campo Aggregate através
do Fields Editor do ClientDataSet.
1. Clique com o botão direito do mouse sobre o ClientDataSet
e selecione a opção Fields Editor;
2. Clique com o botão direito do mouse sobre o Fields Editor e
selecione a opção New Field, assim será apresentado o form New
Field;
3. Configure a propriedade Name para CustomerTotal e
depois na opção Field Type selecione o Aggregate, conforme
Imagem2;
1. Crie um novo projeto;
2. Adicione no form principal um DBNavigator, um DBGrid,
um ClientDataSet e um DataSource;
3. Configure a propriedade Align do DBNavigator para alTop,
e a propriedade Align do DBGrid para alClient;
4. Configure a propriedade DataSource dos componentes
DBNavigator e DBGrid para DataSource1 e a propriedade
DataSet do DataSource para ClientDataSet1;
5. Configure a propriedade FileName do ClientDataSet para
a table Orders.cds (or Orders.xml) localizado no diretório
\Program Files\Common Files\Borland Shared\Data;
Neste instante seu form principal deve ficar parecido com a
Imagem1:
MeGAZINE
Imagem 2
19
Delphi
4. Clique sobre o OK para fechar a janela e você poderá ver o
novo campo Aggregate adicionado no Fields Editor, veja que ele
foi adicionado numa lista separada, conforme Imagem3.
MAX( ShipDate ) + 30
Expressões Incorretas
Algumas expressões são incorretas, como por exemplo, não
podemos incluir uma função agregada como uma expressão de
outra função agregada. Veja um exemplo incorreto:
SUM( Max( AmountPaid ) ) <-- incorreta
Também não podemos usar um calculo entre uma função
agregada e um campo da tabela. Por exemplo, se Quantity é o
nome de um campo, a seguinte expressão está incorreta:
SUM( Price ) * Quantity <-- incorreta
Em nosso exemplo em particular, queremos calcular o Total
do campo AmountPaid e para isso usaremos os seguintes passos:
Imagem 3
Definindo o Expression do Aggregate
A propriedade Expression do Aggregate define o cálculo que o
campo Aggregate irá executar. Esta expressão pode consistir de
constantes, campos de valor e funções agregadas. As funções
agregadas são AVG, MIN, MAX, SUM e COUNT.
Para definir um cálculo que totaliza o campo AmountPaid da
tabela Orders.cds, iremos montar uma expressão usando a
função SUM, conforme o exemplo a seguir:
SUM( AmountPaid )
O argumento de uma função agregada pode incluir dois ou
mais campos em cada expressão. Por exemplo, temos dois campos
na tabela, um nomeado Quantity e o outro nomeado Price, você
poderá montar uma expressão da seguinte forma:
SUM( Quantity * Price )
Numa expressão também podemos incluir constantes. Por
exemplo, se a taxa de imposto é 8.25%, você pode criar um campo
Aggregate que calculará o total do imposto, usando uma
expressão da seguinte forma:
1. Selecione o campo Aggregate no fields Editor;
2. Usando o Object Inspector, configure a propriedade
Expression com a expressão SUM(AmountPaid) e depois a
propriedade Currency para True.
Configurando o índice e o GroupingLevel
Um Aggregate necessita saber sobre quais registros
executará o cálculo. Isso será feito usando as propriedades
IndexName e GroupingLevel do Aggregate. No caso, se você
deseja executar um cálculo entre todos os registros do
ClientDataSet, você pode deixar o IndexName em branco e o
GroupingLevel configurado para 0 (Zero).
Se você deseja que o Aggregate execute calculos entre um
grupo de registros, você deve ter um índice cujo campo inicial
determine o grupo. Por exempo, se deseja calcular a somatória do
campo AmountPaid separado por cada cliente, sendo o cliente
identificado pelo campo CustNo, você deve configurar o
IndexName com o nome do índice cujo primeiro campo seja o
CustNo. Se deseja executar o calculo por cada cliente e por cada
data de compra, tendo os campos CustNo e SaleDate, você deve
configurar no IndexName o índice que tem os campos CustNo e
SaleDate como os dois primeiros campos.
SUM( Total * 1.0825 )
Você pode configurar a propriedade Expression para executar
uma operação entre duas funções agregadas, veja exemplo:
MIN( SaleDate )-MIN( ShipDate )
Como também podemos executar uma operação entre uma
função e uma constante, veja exemplo:
20
O índice atribuido na propriedade IndexName pode ter mais
campos que o número de campos que você deseja agrupar e é
nesse momento que o GroupingLevel entra. Você configura o
GroupingLevel para o número de campos do índice que deseja
considerar como grupo. Por exemplo, imagine que você configure
o IndexName para um índice criado pelos campos CustNo,
SaleDate e PurchaseTypes nessa ordem. Se configurar o
GroupingLevel para 0, o cálculo do Aggregate será executado
MeGAZINE
Delphi
sobre todos os registros do ClientDataSet, sem agrupamento.
Configurando o GroupingLevel para 1, o cálculo será executado
por cada cliente (lembre-se que o campo CustNo é o primeiro
campo do índice). Configurando o GroupingLevel para 2 o
cálculo será executado por cada cliente e por cada data da
compra.
propriedade IndexName para CustIdx.
5. Depois, usando o Fields Editor, selecione o campo Aggregate, configure a propriedade IndexName para CustIdx e a
propriedade GroupingLevel para 1.
O Object Inspector deve ficar parecido com a Imagem5.
É interessante notar que a classe TIndexDefs, sendo a classe
usada para definir o índice, também tem a propriedade
GroupingLevel. Se você configurar essa propriedade para cada
índice, o índice conterá informações adicionais sobre cada
agrupamento de registro. Contanto que você esteja configurando
o GroupingLevel de um Aggregate para um valor maior que 0,
você pode melhorar o desempenho do Aggregate configurando o
GroupingLevel do índice pelo menos a um valor tão alto quanto o
GroupingLevel do Aggregate. Note, porém, que um índice cuja
propriedade GroupingLevel esteja configurada para um valor
maior que 0, leva um tempo um pouco maior para gerar uma
atualização, já que isto também deve produzir informações de
agrupamento. Esta sobrecarga é mínima, mas deve ser
considerada caso a velocidade de geração e manutenção do índice
seja uma preocupação.
O próximo passo encaminha você através do processo de
criação de um índice do campo CustNo, e então configurar o
campo Aggregate para usar este índice com o GroupingLevel
igual a 1.
1. Selecione o ClientDataSet e no object inspector selecione a
propriedade IndexDefs. Clique no botão da propriedade IndexDefs
para mostrar o IndexDefs Editor.
Veja Imagem4
Imagem5
Trabalhando o campo Aggregate
O campo aggregate está quase pronto. Para que ele possa
trabalhar, devemos ainda configurar as suas propriedades Active
e Visible e também a propriedade AggregatesActive do
ClientDataSet para True. Veja os passos a seguir:
1. Selecione o campo Aggregate e, através do object inspector,
configure as propriedades Active e Visible para True;
2. Agora, selecione o ClientDataSet e configure a propriedade
AggregatesActive para True.
Depois disso o aggregate será calculado automaticamente
quando o ClientDataSet estiver ativo.
O campo Aggregate está quase pronto, este é mais um passo,
que ligará o campo Aggregate com um componente de controle de
dados. Os próximos passos demonstrarão como deixar o campo
Aggregate visível num DBGrid.
Imagem 4
2. Click no botão Add New do IndexDefs Editor para
adicionar um novo índice;
3. Selecione o novo índice e usando o object inspector
configure a propriedade Name para CustIdx, a propriedade Fields
para CustNo, e a propriedade GroupingLevel para 1. Feche o
IndexDefs Editor.
4. Com o ClientDataSet ainda selecionado, configure a
1. Selecione o ClientDataSet e configure a sua propriedade
Active para True;
2. Selecione agora o componente DBGrid, clique com o botão
direito do mouse e selecione a opção Columns Editor;
3. Clique no botão Add All Fields para adicionar as colunas
dos campos do ClientDataSet. Neste momento podemos observar
que a coluna do campo aggregate não foi adicionada, conforme
Imagem6;
MeGAZINE
21
Delphi
6. Feche o Columns Editor;
7. Se você seguiu todos os passos, o campo Aggregate foi
adicionado e deve estar visível como a terceira coluna do DBGrid,
conforme Imagem8.
Dicas adicionais sobre o Aggregate
Imagem 6
4. Agora iremos criar uma nova coluna para mostrar o
campo aggregate, portanto clique no botão Add New para criar a
nova coluna;
5. Nesta nova coluna configure a propriedade FieldName
para CustomerTotal. Para tornar a visualização desse campo
mais fácil, mova esta nova coluna dentro da lista deixando-a
como a terceira coluna do DBGrid, conforme Imagem7.
1. A propriedade AggregatesActive do ClientDataSet é usada
para ligar e desligar os Aggregates em tempo de execução.
Configurando o AggregatesActive para False é extremamente
útil, quando você precisa adicionar, remover ou alterar inúmeros
registros e essas alterações afetarem os calculos do Aggregate.
Pois se você efetuar as alterações nos dados do ClientDataSet, e a
cada alteração os calculos forem atualizados, essas alterações
ficarão mais lentas. Sendo assim deixe o AggregatesActive como
False e após efetuar suas alterações volte para True, para que
assim o campo Aggregate seja recalculado.
2. Outra dica, no lugar de ligar e desligar todos os Aggregates, a propriedade Active de cada Aggregate permite manipulálo individualmente em tempo de execução. Isso pode ser útil se
você tem muitos Aggregates, mas somente um ou dois serão
atualizados durante as alterações no ClientDataSet.
Subsequentemente reativando o Aggregate o recalculo será
executado automaticamente. Em tempo de execução você pode ler
a propriedade ActiveAggs do ClientDataSet para visualizar quais
Aggregates estão atualmente ativos para um determinado nível
de agrupamento.
Criando itens Aggregate
Itens Aggregates são criados através da propriedade Aggregates do ClientDataSet.
Esses itens Aggregates são semelhantes ao campos
Agregates, pois executam automaticamente cálculos simples.
Porém, os valores dos itens Aggregates somente podem ser lidos
em tempo de execução e não podemos ligá-los a componentes de
controles de dados (DBGrid).
A configuração dos itens Aggregates são iguais as dos
campos Aggregates. Os passos seguintes demonstram como
adicionar e usar os items Aggregates no projeto:
Imagem 7
22
1. Selecione o ClientDataSet e no object inspector selecione a
propriedade Aggregates; Clique no botão da propriedade para
abrir a janela do Editor; conforme Imagem9.
2. Clique no botão Add New e adicione dois itens Aggregates;
3. Selecione o primeiro Aggregate e usando o object inspector
configure as propriedades:
Expression: AVG(AmountPaid)
AggregateName: CustAvg
IndexName: CustIdx
GroupingLevel: 1
Active: True
MeGAZINE
Delphi
Imagem 8
Visible: True.
4. Selecione o segundo Aggregate
e através do object inspector
configure as propriedades:
Expression: MIN(SaleDate)
AggregateName: FirstSale
IndexName: CustIdx
GroupingLevel: 1
Active: True
Visible: True.
5. Adicione um componente
PopupMenu em seu projeto. Dê duplo
click sobre o popupmenu e crie um
item colocando em seu Caption o
seguinte texto:
Sobre este Cliente.
6. Configure a propriedade
popupmenu do DBGrid para
Popupmenu1;
7. Finalizando, crie o evento
onclick do item do popupmenu e
inclua a seguinte instrução:
procedure
TForm1.SobreesteCliente1Click
(Sender: TObject);
begin
Imagem 9
Imagem 10
ShowMessage(‘A média de
venda para este Cliente é ‘
+ FormatFloat(‘###,##0.00’,
ClientDataSet1.
Aggregates[0].Value) +#13+‘A
primeira venda para este
Cliente foi em ‘+
DateToStr(ClientDataSet1.
Aggregates[1].Value));
end;
8. Executando o seu projeto neste
momento, o seu Form deverá ficar
igual a Imagem10.
9. Para visualizar o valor dos
itens Aggregates criados, pressione o
botão direito do mouse sobre o
DBGrid e selecione a opção “Sobre
este Cliente”, onde será apresentado
o ShowMessage contendo as
informações dos dois itens
MeGAZINE
23
Delphi
Como deve ser, se o registro atual for o primeiro registro no
grupo, GetGroupState retornará o valor gbFirst. Se o registro for
o último registro no grupo, retornará gbLast. Quando
GetGroupState é chamado para um registro em algum lugar no
meio do grupo, o retorno será gbMiddle. Finalmente, se o registro
atual for o único registro do grupo, GetGroupState devolverá os
valores gbFirst e gbLast.
Imagem 11
Aggregates. Conforme Imagem11.
Pronto, aqui terminamos a configuração dos Aggregates em
nosso projeto.
Entendendo o GroupState
Group State recorre à posição relativa de um determinado
registro dentro do seu grupo.
Usando Group State você pode saber se um determinado
registro é o primeiro registro dentro de seu grupo (dado o índice
atual), o último registro de seu grupo, nem o primeiro nem o
último registro do seu grupo ou ainda o único registro no grupo.
Você verifica o estado de um registro em particular dentro do
grupo, chamando o método GetGroupState do ClientDataSet.
Este método tem a seguinte sintaxe:
function GetGroupState(Level: Integer): TGroupPosInds;
GetGroupState pode ser particularmente útil para suprimir
informações redundantes, ao exibir os dados de um
ClientDataSet em um componente de multiplos registros, como o
DBGrid. Por exemplo, veja o form principal do nosso projeto.
Observer que o campo Aggregate CustomerTotal exibe o valor em
todos os registros, embora esteja sendo calculado para cada
Cliente separadamente. Não é só redundante como desnecessário,
e torna a leitura dos dados mais difícil.
Usando GetGroupState você pode testar se o registro em
particular é o primeiro registro do grupo, e nesse caso exibir o
valor do campo CustomerTotal. Para os registros que não são os
primeiros registros do grupo (baseado no índice de CustIdx), você
simplesmente omite a apresentação do valor.
Determinando o Group state a omissão ou exibição dos dados
podem ser controladas através do evento OnGetText do campo
CustomerTotal. Abaixo podemos observar um simples exemplo
deste evento onGetText:
Quando você chamar o
GetGroupState, você passa um valor
inteiro que indica o nível de
agrupamento. Passando o valor 0
(zero) o GetGroupState retornará a
posição relativa do registro corrente
dentro do dataset completo. Passando
o valor 1 retornará o group state do
registro corrente respeitando o
primeiro campo do índice atual,
enquanto que passando o valor 2
retornará o group state do registro
corrente respeitando os dois
primeiros campos do índice atual, e
assim por diante.
GetGroupState devolve as flags
da classe TGroupPosInd.
TGroupPosInd é declarada da
seguinte forma:
TGroupPosInd = (gbFirst,
gbMiddle, gbLast);
Imagem12
24
MeGAZINE
Delphi
procedure
TForm1.ClientDataSet1CustomerTotalGetText(Sender:
TField;
var Text: String; DisplayText: Boolean);
begin
if gbFirst in ClientDataSet1.GetGroupState(1) then
Text := FormatFloat(‘###,##0.00’, Sender.Value)
else
Text := ‘’;
end;
Se você também deseja omitir o campo CustNo dentro do
grupo, então deve adicionar todos os campos na lista de TFields
do ClientDataSet e depois criar o evento onGetText do campo
CustNo, onde iremos incluir a seguinte instrução:
procedure TForm1.ClientDataSet1CustNoGetText(Sender:
TField;
var Text: String; DisplayText: Boolean);
begin
if gbFirst in ClientDataSet1.GetGroupState(1) then
Text := Sender.Value
else
Text := ‘’;
end;
A Imagem12 mostra o resultado do nosso projeto em execução.
Observe que os campos CustNo e CustomerTotal são
apresentandos somente no primeiro registro de cada grupo.
Conclusão
Neste artigo podemos conhecer um pouco dos Aggregates e do
GroupState do ClientDataSet e com essas informações espero
poder lhes ajudar a criar várias formas de visualização dos dados
em seu projeto. Um abraço a todos e até o próximo artigo.
Download
O projeto de exemplo deste artigo está disponível para
download em:
http://www.theclub.com.br/revista/download/Aggregates.zip
MeGAZINE
25
Delphi
Distribuindo um arquivo
dentro do executável
por André Colavite - [email protected]
Nesta matéria iremos descrever como distribuir um
determinado arquivo dentro do executável do projeto, pois assim
ao distribuir a aplicação levaremos somente o executável para o
cliente e o projeto se encarregará de extrair o determinado
arquivo quando este for necessário. O determinado arquivo pode
ser de qualquer tipo imagem, som, dll, doc ou até outro
executável.
O processo consiste em transformar o determinado arquivo
em um arquivo de recurso .res e depois incluí-lo em nosso projeto.
Dentro do projeto criaremos a rotina que permitirá descarregar o
arquivo num diretório no momento em que for utilizá-lo.
Transformando o arquivo num Res
Nos passos seguintes iremos descrever como gerar um
arquivo .RES a partir de um arquivo comum, neste caso iremos
criar a partir de um arquivo WAV.
* No diretório do projeto crie um arquivo texto e coloque a
extensão .RC, exemplo SOM.RC. Este arquivo poderá ser criado
através do NOTEPAD;
* No arquivo .RC criaremos uma linha onde colocaremos
informações sobre o arquivo a ser transformado em recurso.
Podemos incluir mais de um arquivo como recurso, neste caso
iremos criar uma linha para cada arquivo dentro deste .RC;
* A linha criada dentro do RC ficará da seguinte forma:
26
SOMWAV WAVE MUSICA.WAV
O arquivo .RC tem a seguinte estrutura:
SOMWAV é o nome utilizado pelo projeto para achar o
recurso;
WAVE somente identifica o tipo de arquivo;
MUSICA.WAV indica o arquivo a ser transformado em
recurso. Caso o arquivo esteja num diretório diferente, teremos
que indicar o caminho completo e o nome do arquivo, por exemplo
C:\WINDOWS\MUSICA.WAV;
O próximo passo será compilar o arquivo .RC gerando um
arquivo .RES, e para isso iremos utilizar um compilador que vem
junto com o Delphi chamado BRCC32.EXE, que normalmente
está no diretório Bin do Delphi.
Para executar o compilador abra a janela do DOS vá até o
diretório onde se encontra o arquivo .RC e execute a seguinte
instrução: BRCC32 SOM.RC
Após executar a instrução um arquivo .RES será gerado,
contendo o mesmo nome do arquivo .RC..
Pronto, estando com o arquivo .RES criado, o próximo passo
será criar o projeto.
Trabalhando com o novo arquivo de recurso
Nos passos seguintes criaremos um simples projeto contendo
duas partes. A primeira parte terá uma procedure para
MeGAZINE
Delphi
descarregar o arquivo de SOM no diretório do executável e a
segunda parte irá executar o arquivo de SOM sem descarregar o
arquivo no diretório.
Crie um novo projeto;
Abra a unit do form principal e logo abaixo da instrução {$R
*.dfm} inclua uma instrução {$R SOM.RES}
Essa instrução indica ao compilador do Delphi que o arquivo
SOM.RES será compilado junto ao projeto.
Descarregando o arquivo de SOM
Para descarregar o arquivo de Som de dentro do executável
utilizaremos uma classe chamada TResourceStream. Essa classe
permite acesso ao recursos compilados do projeto.
Criaremos agora a primeira procedure ao qual irá
descarregar o arquivo de Som no diretório do projeto. Veja abaixo
a instrução da procedure:
procedure DescarregaArquivo;
var nArq: string;
Res: TResourceStream;
Begin
nArq := ExtractFilePath(Application.ExeName)+
’\MUSICA.WAV’;
if not FileExists(nArq) then
begin
Res := TResourceStream.Create
(Hinstance, ‘SOMWAV’, ‘WAV’);
try
Res.SavetoFile(nArq);
finally
Res.Free;
end;
end;
end;
No Form coloque um componente Button e no evento onclick
desse Button coloque a instrução para chamar a procedure
criada. Como estamos utilizando um arquivo de SOM iremos
tocar esse arquivo após descarregá-lo, sendo assim utilizaremos
mais uma instrução dentro do evento onClick.Veja a instrução a
seguir:
sndPlaySound(‘MUSICA.WAV’, snd_ASync);
end;
Pronto, a primeira parte do projeto já está pronto, vamos
testá-lo.
Compile o projeto e depois copie o arquivo .exe do projeto para
um novo diretório. Em seguida execute o projeto e pressione o
botão, neste momento o arquivo será descarregado no diretório do
executável e será tocado em seguida.
Criando a segunda parte do projeto
Nesta segunda parte do projeto, iremos tocar o arquivo de
som presente no executável sem descarregá-lo no diretório. Para
isso iremos incluir em nosso projeto mais um componente
Button, e no evento onClick do novo button coloque a seguinte
instrução:
procedure TForm1.TocarClick(Sender: TObject);
begin
if not PlaySound(‘SOMWAV’, Hinstance, SND_RESOURCE
or
SND_NODEFAULT or SND_SYNC) then
ShowMessage(‘Erro ao tocar o SOM’);
end;
Pronto, agora recompile o seu projeto e faça o mesmo teste
realizado anteriormente.
Copie o executável para outro diretório e execute o projeto.
Pressione o novo botão e veja que o som será tocado sem
descarregar o arquivo no diretório.
Espero que tenham gostado dessa matéria, um grande
abraço a todos e até a próxima.
O projeto dessa matéria está disponível para donwload em
nosso site:
http://www.theclub.com.br/revista/download/RecursoWAV.ZIP
procedure TForm1.DescarregaClick(Sender: TObject);
begin
DescarregaSom;
{ Toca o arquivo WAV }
MeGAZINE
Sobre o autor
André Colavite
Consultor Técnico do The Club
[email protected]
27
Dicas & TTruques
ruques
Treeview – Foco
uses
Veja neste exemplo como mandar o foco à um ítem do
treeview quando clicar no botão de expansão (+):
// evento OnMouseDown do Treeview.
procedure TForm1.TreeView1MouseDown
(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
var
T: TTreeNode;
begin
// Pega o ítem através das coordenadas do mouse.
T := Treeview1.GetNodeAt(X,Y);
if T <> nil then
Treeview1.Selected := T;
RVClass, RVProj, RVCsStd;
procedure TForm1.btnChamaRelClick
(Sender: TObject);
var
Pagina: TRavePage;
Report: TRaveReport;
QualPagina: String;
begin
// Nome da página dentro do projeto Rave.
QualPagina := ‘Page2’;
// Abre RvProject.
RvProject1.Open;
// Pega referência do “Report”
// dentro do projeto Rave.
Report := RvProject1.ProjMan.ActiveReport;
// Pega referência da “Page” dentro do “Report”.
Pagina := RvProject1.ProjMan.FindRaveComponent
(‘Report1.’+QualPagina,nil) as TRavePage;
// Indica a página inicial.
Report.FirstPage := Pagina;
// Executa o relatório.
RvProject1.Execute;
end;
Rave Report – Indicar página inicial
O Rave Reports permite ter várias páginas de relatório (cada
página com um layout) para um mesmo relatório. Neste exemplo,
iremos demonstrar como definir via programação qual página
será apresentada como inicial.
28
end;
MeGAZINE
Dicas & TTruques
ruques
dbExpressPlus
{$R *.dfm}
Na sessão Dicas & Truques da revista de Setembro/2003,
publicamos uma dica a respeito da suíte de componentes
dbExpressPlus, a qual possui vários componentes adicionais para
incrementar a dbExpress. Contudo, logo após a publicação
detectamos um problema de instalação da dbExpressPlus com
uma outra suíte de componentes bastante utilizada chamada
RxLib. O que ocorre é que ambas suítes possuem um componente
com a mesma classe, o TSQLScript, e quando a dbExpressPlus
vai ser instalada, ocorre um erro de compilação em seu pacote,
visto já haver a classe TSQLScript registrada.
Uma forma que encontramos para resolver o problema, foi
abrir a unit SQLScript (dbExpressPlus) e alterar o nome da
classe de TSQLScript para TSQLScriptPlus.
Para fazer essa alteração abra o pacote DbExprPlus_r70.dpk
e dentro desse pacote dê duplo click sobre o arquivo
SQLScript.pas, ao qual será aberto...
function GetDesktopListViewHandle: THandle;
var
S: String;
begin
Result := FindWindow(‘ProgMan’, nil); Result :=
GetWindow(Result,GW_CHILD);
Result := GetWindow(Result, GW_CHILD);
SetLength(S, 40);
GetClassName(Result, PChar(S), 39);
if PChar(S) <> ‘SysListView32’ then
Result := 0;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
SendMessage(GetDesktopListViewHandle,
LVM_ARRANGE, 0, 0);
end;
Estando com o .Pas aberto selecione no menu do Delphi a
opção Search / Replace, onde iremos configurá-lo da seguinte
forma:
Na propriedade “Text to find” indique o TSQLScript
Na propriedade “Replace with” indique o novo nome
TSQLScriptPlus
Depois clique sobre o botão Replace All para alterar todas as
palavras TSQLScript pela TSQLScriptPlus automaticamente. O
Delphi irá perguntar se deseja alterar, nesse momento click no
botão All.
Pronto Salve a unit e depois compile essa pacote, lembre-se
essa pacote somente será compilado.
Agora abra o pacote DbExprPlus_d70.dpk e compile, ele irá
indicar que não encontrou a classe TSQLScript, nesse momento
altere a linha dentro do SQLPlusEditors.Pas ao qual está
mencionando o TSQLScript pelo TSQLScriptPlus e salve o
arquivo, volte na janela do pacote DbExprPlus_d70.dpk, compile e
instale o pacote. Pronto agora não irá apresentar a mensagem de
erro e será instalado corretamente.
Cálculo de Parcelas
Neste simples exemplo iremos demonstrar como fazer a
divisão de um valor em parcelas, fazendo o tratamento da
diferença, jogando para a primeira ou última parcela, na figura 1
poderá verificar o layout sugerido para o formulário deste
exemplo.
Desktop do Windows – auto-arranjar
icones
Esta dica pode ser útil após a instalação de sua aplicação,
onde geralmente é disponibilizado um ícone na área de trabalho
do Windows e em muitos casos sendo necessário arranjá-los após
a instalação.
Figura 1 – Layout sugerido
implementation
uses CommCtrl;
Acompanha agora o código do botão “Gerar”:
MeGAZINE
29
Perguntas & Respostas
Parcelas[High(Parcelas)] :=
Parcelas[High(Parcelas)] + (Total - TotParc);
procedure TForm1.btnGerarClick(Sender: TObject);
var
Total, ValorParc, TotParc: Extended;
NParc: Integer;
Parcelas: Array of Extended;
i: Integer;
begin
{ Variáveis auxiliares }
Total := 0;
ValorParc := 0;
TotParc := 0;
NParc := 0;
ListParcelas.Clear;
{ mostra parcelas no ListBox }
for i := Low(Parcelas) to High(Parcelas) do
ListParcelas.Items.Add(FormatFloat(‘###,##0.00’,
Parcelas[i]));
end;
O exemplo referente esta dica está disponível para download
em: http://www.theclub.com.br/revista/downloads/
CalcParcelas.zip
DataSetProvider – Não carregar “Details”
Quando trabalhamos com ClientDataSet/DataSetProvider na
manutenção em tabelas relacionadas (master/detail), na maioria
dos casos utilizamos campos do tipo TDataSetFields no dataset
master, o qual traz o conteúdo da tabela detalhe juntamente com
o pacote de dados da master. Contudo, existem situações que por
questões de performance por exemplo não queremos que os dados
das tabelas detalhes sejam carregados juntamente com os dados
da master. Felizmente existe no componente DataSetProvider
várias configurações e entre elas o controle dos datasets detalhes.
Para que os TDataSetFields não sejam incluídos nos pacotes de
dados, faça o seguinte:
1. Configure no DataSetProvider a propriedade Options |
poFetchDetailsOnDemand para “True”;
2. Configure no ClientDataSet detalhe (que está ligado ao
ClientDataSet master via DataSetField), a propriedade
FetchOnDemand para “False”;
Pronto, com isso, somente será incluido no pacote de dados, os
registros referentes a tabela master. Quando necessitar
visualizar os registros detalhes, deverá executar o método
FetchDetails do ClientDataSet master:
cdsMaster.FetchDetails;
{ tenta converter o valor do Edit }
try
Total := StrToFloat(edValor.Text);
except
ShowMessage(‘Valor Inválido!’);
Abort;
end;
{ Verifca número de parcelas }
if speParcelas.Value < 2 then
Exit
else
NParc := speParcelas.Value;
{ Pega somente a parte inteira da divisão }
ValorParc := Trunc(Total / NParc);
{ Ajusta array que guardará o }
{valor de cada parcela }
SetLength(Parcelas, NParc);
{ Atribui valor de cada parcela e acumula total }
for i := Low(Parcelas) to High(Parcelas) do
begin
Parcelas[i] := 0;
Parcelas[i] := ValorParc;
TotParc := TotParc + Parcelas[i];
end;
{ Verifica em qual parcela será “jogada” a }
{ diferença }
if rg_diferenca.ItemIndex = 0 then
Parcelas[Low(Parcelas)] :=
Parcelas[Low(Parcelas)] + (Total - TotParc)
else
30
QRChart no Delphi 7
Alguns associados têm nos consultado a respeito do
componente QRChart no Delphi 7, informando não estar
encontrando este componente no D7. O que ocorre, é que o
QuickReport não vem mais instalado por padrão na IDE do D7, e
consequentemente o QRChart também não, visto trabalharem
em conjunto. Porém, ambos componentes podem ser adicionados
sem problemas, bastando para isso acessar o menu “Components
| Install Packages | Add” e adicionar os seguintes pacotes:
QuickReport
QRChart
MeGAZINE
->
->
\Delphi7\bin\dclqrt70.bpl
\Delphi7\bin\dcltqr70.bpl
Download

Delphi - The Club