Você está na página 1de 84

Com grande satisfação apresentamos a primeira edição da

Revista DicasBCB

Agradecimentos:

Agradecemos ao Carlos Alberto Rodrigues, nosso colaborador mais anônimo; agradecemos aos
moderadores Diones Fernandes, Luiz Sérgio e Paulo Lima pelo gigantesco apoio; agradecemos a todos
os participantes dos fóruns e das listas de discussões que sempre nos ajudaram com uma solução, uma
dica ou uma palavra de estímulo, induzindo-nos a seguir em frente com este trabalho; agradecemos a
todos os mestres anônimos que, sem pedir nada em troca, compartilham o conhecimento em tantos sites
espalhados por este mundo afora; agradecemos ao Ivan Tarcísio por ajudar-nos a concretizar este sonho
e, mais uma vez, ao Paulo Lima pela capa (e novo visual da página inicial do site). Agradecimentos
especiais à família pela enorme paciência quando nossa presença é furtada de suas companhias, em
virtude das horas e mais horas diante de uma tela de computador; agradecemos a você, caro leitor!

Apresentação:

A revista DicasBCB é uma iniciativa do DicasBCB - o Site dos Programadores C++Builder.


Observe que iniciativa não significa exclusividade, uma vez que auxílio chegaram de muitas formas
diferentes. A presente edição contou, ainda, com a parceria direta do site:

http://geocities.yahoo.com.br/ivantgmoraes/

A revista DicasBCB pode ser distribuída livremente e usada para fins educativos em quaisquer lugares.
Trata-se de um trabalho voltado a auxiliar programadores C++ nacionais no aprimoramento de suas
técnicas. Esperamos que o presente trabalho contribua para o desenvolvimento e o crescimento da
indústria nacional de software.

Programadores iniciantes podem encontrar alguma dificuldade para compreender os conceitos


abordados nos artigos, uma vez que partimos do princípio de que nossos leitores já possuem algum
contato com o a linguagem de programação C++ e com o C++Builder. Se este não for o seu caso,
sugerimos que se dirija às páginas do site DicasBCB onde mantemos cursos e tutoriais online com
todos os conceitos necessários ao aprendizado básico.

Os artigos publicados levam a assinatura de seus autores, a quem remetemos eventuais esclarecimentos
de dúvidas. Todos os conceitos apresentados nesta revista, que levam nossa assinatura, foram
exaustivamente testados e nos sentimos relativamente seguros em apresentá-los. Mas, mesmo assim, se
você não se sentir seguro com respeito à alguma informação contida no presente trabalho
(independente de quem seja seu autor), não a execute. Não nos responsabilizamos por quaisquer
espécies de danos ou problemas advindos da má utilização, bem como não garantimos, de forma
alguma, que as informações contidas nesta revista conduzirão sempre ao fim desejado. Mas, como
sempre, garantimos que fizemos o possível para fornecer-lhes o melhor, a nosso alcance.

Bom proveito a todos!

Thérbio de Lima Alves


Coordenador da presente edição
http://www.dicasbcb.com.br
Índice:

1.Caixas de diálogos
2.TStringList – listas de strings sem dificuldades
3.TList – programação avançada
4.TObect - Visual Component Library - primeira parte
5.Truques e dicas BCB

Caixas de diálogos
Considero extremamente ingrata a tarefa de traduzir termos técnicos. Por exemplo, como traduzir
string? Creio que é melhor não tentar traduzir. Logo incorporamos, na língua pátria, a palavra string!
Estamos, então, diante de um barbarismo (estrangeirismo - anglicismo)? Bem, barbarismo ou não,
string continua sendo uma palavra. Logo, "gramaticalmente" falando, pode ser classificada! Muito
bem, vamos arriscar uma análise.

String seria um substantivo, não tenho dúvidas (errr)! Substantivo comum, concreto, simples, ... errr ...
complicou! Gênero? Qual seria o gênero da palavra string? (melhor dizendo: string é menino ou
menina? he, he, he!)

Já vi construções do tipo: o string ... Também já vi: a string ... Pior: já usei ambos os tipos de
construção! Pior: não sei qual é o correto! Pior: Nem sei se alguma dessas construções é correta! Para
ser sincero, nem sei se não estou delirando ao tentar organizar tal assunto! O dicionário do Word sugere
a string. Mas, não confio muito...

Veja outros exemplos: context ID. Ou a propriedade Items. Acha pouco? Que tal: edit box, label,
caption, default ou dialog box!

Complico-me todo com esses termos! No presente artigo, por exemplo, às vezes mantenho o termo
original em inglês: dialog box; às vezes arrisco um pouco: caixas de diálogo; às vezes deliro: diálogo;
e, às vezes, filosofo: caixas de mensagens!

Querem saber! Não sei a resposta! Se alguém souber, por favor, ensine-ma!

Então, muitas vezes, usarei um termo ou outro indiscriminadamente, conforme minha imaginação
trabalhava no momento que eu elaborava o artigo!

Se tudo isso aí em cima está muito esquisito, não se preocupe. Vá em frente pois o resultado será o
mesmo. É que eu me encontrei num daqueles raros momentos de reflexão ortográfica!

****************************************
Usamos as caixas de mensagens (ou de diálogos) para estabelecer comunicação entre nossos
aplicativos e os usuários. Podemos separá-las em dois grupos: de "entrada" e de "saída" de dados.

ShowMessage
As caixas de diálogo ShowMessage exibem mensagens com apenas um botão de OK na janela:

extern PACKAGE void __fastcall ShowMessage(const AnsiString Msg);

O parâmetro Msg corresponde à mensagem na caixa de diálogos, sendo que o nome do arquivo
executável da aplicação será o título da caixa de diálogos. Muito usadas para emitir um aviso simples
para o usuário, são também uma mão-na-roda para os programadores. Imagine uma função com muitas
linhas de código que, a partir de determinado instante, comporta-se de modo não esperado, mas o
desenvolvedor não sabe exatamente onde se encontra tal erro. Inserir ShowMessage's no código pode
nos auxiliar na localização do erro.

Ou seja, como, via de regra, o programa vai executando as linhas de código seqüencialmente, tais
caixas de mensagens vão sendo exibidas na medida da execução. Uma caixa exibida antes do erro e
outra depois podem assinalar a provável localização do erro.

Sua sintaxe:

ShowMessage("Reinicie o Windows...");

Sendo void, as caixas de diálogo ShowMessage não retornam valor algum. Isso significa que não se
estabelece, efetivamente, uma comunicação entre o programa e o usuário. Apenas uma mensagem é
disparada a fim de que o usuário receba alguma informação.

MessageBox
Quando se deseja oferecer alternativas ao usuário, tais como aceitar, não aceitar ou cancelar uma ação,
deve usar outros tipos de dialog box como as MessageDlg ou MessageBox. Essas caixas de mensagens
retornam um valor inteiro, possibilitando-nos criar uma opção para cada valor retornado. Não é
possível mudar diretamente o idioma dos botões das caixas de diálogos MessageDlg, que sempre
aparecem em inglês. Contudo a função MessageBox da API do Windows, ou o método de igual nome
encapsulado pela classe TApplication, exibe o texto dos botões no idioma da versão do Windows
instalada.

Eis a sintaxe:

int MessageBox(

HWND hWnd, // Handle da janela owner


LPCTSTR lpText, // address de text na caixa de mensagens
LPCTSTR lpCaption, // address do título na caixa de mensagens
UINT uType // Flags - estilo da caixa de mensagens
);
Onde lpText e lpCaption são variáveis do tipo LPCTSTR (char*). Portanto, se usadas, variáveis do tipo
AnsiString pedirão o complemento .c_str()

O valor do parâmetro lpText é a mensagem, que pode conter mais de 255 caracteres. O valor do
parâmetro Caption é o título que aparece na barra de títulos da caixa de diálogos.

As Flags são as seguintes:

Para definir os botões que serão mostrados:


Nota: Podemos escrever as Flags por extenso ou apenas o valor numérico (int) correspondente.

0 * MB_OK (Ok)
1 MB_OKCANCEL (Ok/Cancelar)
2 MB_ABORTRETRYIGNORE (Anular/Repetir/Ignorar)
3 MB_YESNOCANCEL (Sim/Não/Cancelar)
4 MB_YESNO (Sim/Não)
5 MB_RETRYCANCEL (Repetir/Cancelar)

Para escolher o ícone:

16 MB_ICONSTOP (ícone de parada crítica - geralmente para erros)


32 MB_ICONQUESTION (ícone com sinal de interrogação)
48 MB_ICONEXCLAMATION (ícone com sinal de exclamação)
64 MB_ICONINFORMATION (ícone com um 'i')

Para definir o botão default quando a tecla Enter for clicada:

0 * MB_DEFBUTTON1 (1º)
256 MB_DEFBUTTON2 (2º)
512 MB_DEFBUTTON3 (3º)

As FLAGS são separadas com | (OR), ou por +


O exemplo a seguir mostra uma caixa de diálogos com um ícone de parada crítica, com os botões
Anular, Repetir e Ignorar estando a opção Repetir assinalada por default:

MessageBox(
Handle,
"Mensagem",
"Caption",
MB_ABORTRETRYIGNORE|MB_ICONSTOP|MB_DEFBUTTON2);

Os valores devolvidos segundo os botões incluídos são os seguintes:

1 IDOK (Se clicar Ok)


2 IDCANCEL (Se clicar cancelar)
3 IDABORT (Se clicar anular)
4 IDRETRY (Se clicar repetir)
5 IDIGNORE (Se clicar ignorar)
6 IDYES (Se clicar sim)
7 IDNO (Se clicar não)
Nota 1: Os asteriscos acima demonstram as flags que a função tomará por default em cada grupo, caso
não efetuemos alguma alteração.

Nota 2: Conforme exposto acima, flags são valores inteiro. Portanto aceitam construções do tipo:

MB_ABORTRETRYIGNORE + MB_ICONSTOP + MB_DEFBUTTON2


ou
2+16+256
ou
274

Em qualquer dos casos acima, o resultado seria o mesmo.

Lembremo-nos de que o valor retornado será um inteiro (entre 1 e 7), ou um valor na forma de flag.

Existem outras flags de menor importância como os que indicam a modalidade da caixa de mensagens
(MB_APPLMODAL, MB_SYSTEMMODAL e MB_TASKMODAL) ou as flags adicionais
MB_DEFAULT_DESKTOP_ONLY, MB_HELP, MB_RIGHT, MB_RTLREADING,
MB_SETFOREGROUND, MB_TOPMOST e MB_SERVICE_NOTIFICATION. Para mais detalhe
sobre essas flags adicionais, consulte o arquivo de ajuda do Win32 Developer's References.

Vejamos um exemplo simples onde o usuário terá a opção de encerrar, ou não, a aplicação:

void __fastcall TForm1::Button1Click(TObject *Sender)


{
int alternativa;
alternativa=MessageBox(Handle,
"Deseja realmente sair?",
"Confirmação",
4+32);
if(alternativa==6)
Application->Terminate();
else// if (alternativa==7)
ShowMessage("O programa continuará funcionando...");

MessageDlg
Veja uma possível caixa de mensagens:
Eís o código que usei para criar essa coisa feia aí em cima:

void __fastcall TForm1::Button1Click(TObject *Sender)


{
MessageDlg("Mensagem", mtWarning,
TMsgDlgButtons()<<mbIgnore<<mbYes<<mbNo
<<mbCancel<<mbNoToAll<<mbYesToAll<<mbHelp, 0);
}

Depois que você acabar de ler este tópico sobre MessageDlg visualize novamente a imagem e releia o
código. Temos por certo que a compreensão será imediata.

O C++Builder possui sua própria caixa de mensagens no estilo da MessageBox, a MessageDlg, que
exibe uma caixa de mensagens no centro do screen.

extern PACKAGE int __fastcall


MessageDlg(const AnsiString Msg,
TMsgDlgType DlgType,
TMsgDlgButtons Buttons,
int HelpCtx);

Assim como MessageBox, MessageDlg pode ser usada para exibir uma mensagem e obter uma
resposta. O parâmetro Msg corresponde à mensagem na caixa de diálogos, sendo que o parâmetro o
DlgType indica o propósito (título) da mensagem e o ícone que será exibido. São eles:

mtWarning - Título: Warning - ícone: exclamação;


mtError - Título: Error - ícone: parada crítica
mtInformation - Título: Information - ícone: i
mtConfirmation - Título: Confirm - ícone: interrogação
mtCustom - Título: nome do exe - sem ícone

O parâmetro Buttons indica os botões que devem aparecer na caixa de diálogos. Notamos aqui sensível
diferença com a caixa de diálogos MessageBox pois enquanto nesta temos um estilo pré-definido e em
português, na MessageDlg podemos escolher quais e quantos botões colocaremos na caixa, o que pode
representar uma vantagem. Como desvantagem temos o fato de os botões exibirem seu texto
exclusivamente em inglês. Há meios para contornar esse problema como, por exemplo, modificar nos
fontes da livraria do C++Builder as constantes que fazem referência aos textos desses botões.
Questionável é se valeria a pena tal trabalho. Outro meio, seria contornar o problema criando-se uma
caixa de mensagens semelhante a MessageDlg através da função CreateMessageDialog().
Analisaremos essa segunda possibilidade no momento oportuno.

O parâmetro HelpCtx serve para especificar o context ID para o tópico de ajuda que deveria aparecer
quando o usuário clica o botão help ou pressiona F1 enquanto a mensagem estiver sendo exibida.

MessageDlg retorna o valor do botão que o usuário selecionar. Alguns valores possíveis: mrNone,
mrAbort, mrYes, mrOk, mrRetry, mrNo, mrCancel, mrIgnore, mrAll. Para mais detalhe, veja o
tópico sobre CreateMessageDialog.
MessageDlgPos
Pode ser que você queira especificar as coordenadas da tela onde a caixa de mensagens será exibida.
Neste caso, use MessageDlgPos.

extern PACKAGE int __fastcall MessageDlgPos(


const AnsiString Msg, TMsgDlgType DlgType,
TMsgDlgButtons Buttons, int HelpCtx, int X, int Y);

Trata-se, basicamente, de uma caixa de diálogos idêntica à MessageDlg, com a particularidade de


possuir mais dois parâmetros, onde serão fornecidas as coordenadas X e Y do screen. Veja um
exemplo:

void __fastcall TForm1::Button1Click(TObject *Sender)


{
MessageDlgPos("Deseja salvar as alterações?",mtConfirmation,
TMsgDlgButtons()<<mbYesNoCancel, 0, 200, 200);
}

MessageDlgPosHelp

Não é incomum o usuário ficar confuso em decorrência de determinadas situações (ou opções) que se
lhe apresentam numa caixa de mensagens. Associar um arquivo de ajuda nessas situações pode ser
crucial. O BCB nos fornece a função MessageDlgPosHelp que facilmente associa um arquivo de ajuda
à caixa de mensagens.

extern PACKAGE int __fastcall MessageDlgPosHelp(const AnsiString Msg,


TMsgDlgType DlgType, TMsgDlgButtons Buttons, int HelpCtx,
int X, int Y, const AnsiString HelpFileName);

Já conhecemos a maioria dos parâmetros da função acima. De novidade temos apenas o parâmetro:
const AnsiString HelpFileName. Além desse parâmetro, nesta lição vamos entender, também, o
parâmetro int HelpCtx. Vejamos um exemplo:

MessageDlgPosHelp(
"Atenção! Error Crítico! Salve as informações e feche o programa.",
mtError, TMsgDlgButtons()<<mbOK<<mbHelp, 4, 200, 200,
ExtractFilePath(Application->ExeName)+ "\Ajuda.hlp");

Até agora vínhamos assinalando o valor 0 (zero) para o parâmetro HelpCtx. Observe que o alteramos
para 4 (quatro) no exemplo atual. Este parâmetro serve para especificar o context ID de um tópico de
ajuda. O número quatro significa que, ao ser clicada a tecla Help ou ser pressionada a tecla F1, o
arquivo de ajuda associado por intermédio do parâmetro HelpFileName será aberto em sua quarta
página. Ou seja, HelpCtx pode ser usado para que o programa abra exatamente a página que contém a
informação necessária ao usuário naquele instante decisivo.
O parâmetro const AnsiString HelpFileName serve para especificar o arquivo de ajuda, ou seja, o
caminho do arquivo. Por exemplo: "C\\Meus Documentos\\HELP.hlp". Em nosso código exemplo
acima, especificamos o arquivo Ajuda.hlp no diretório do programa.

CreateMessageDialog
extern PACKAGE Forms::TForm* __fastcall
CreateMessageDialog(const System::AnsiString Msg,
TMsgDlgType DlgType,
TMsgDlgButtons Buttons);

CreateMessageDialog retorna uma caixa de diálogos do tipo especificado pelo parâmetro DlgType

mtWarning A caixa de mensagens contém um ponto de exclamação amarelo.


mtError A caixa de mensagens contém um X vermelho de parada crítica.
mtInformation A caixa de mensagens contém um "i" azul.
mtConfirmation A caixa de mensagens contém um ponto de interrogação azul.
mtCustom A caixa de mensagens não contém bitmap.

O Título depende do código usado para criar a caixa e o(s) botão(s) depende(m) daquilo que foi
indicado no parâmetro Buttons:

mbYes Um botão com "Yes" na face


mbNo Um botão com "No" na face
mbOK Um botão com "OK" na face
mbCancel Um botão com "Cancel" na face
mbAbort Um botão com "Abort" na face
mbRetry Um botão com "Retry" na face
mbIgnore Um botão com "Ignore" na face
mbAll Um botão com "All" na face
mbNoToAll Um botão com "No to All" na face
mbYesToAll Um botão com "Yes to All" na face
mbHelp Um botão com "Help" na face

Trata-se de um recurso bastante versátil do C++Builder. Através de CreateMessageDialog podemos


criar caixas de mensagens personalizadas. São inúmeras as possibilidades. Podemos colori-las, escolher
a font e, até mesmo, substituir o idioma dos botões.

void MinhaShowMsg(String msg)


{
TForm *ShowMsg = CreateMessageDialog(msg, mtError, TMsgDlgButtons() << mbOK);
ShowMsg->Caption = "ERRO DE EXECUÇÃO...";
ShowMsg->Color = clYellow;
ShowMsg->Font->Color = clRed;
ShowMsg->Font->Style = TFontStyles()<<fsBold<<fsItalic;
ShowMsg->Font->Name = "Courier New";
ShowMsg->Font->Size = 9;
ShowMsg->ShowModal();
delete ShowMsg;
ShowMsg = NULL;
}

//---------------------------------------------------------------------------

...
try
...
catch(...)
{
MinhaShowMsg("Erro durante a execução... O computador será reiniciado...");
}
...
//---------------------------------------------------------------------------

Veremos, agora, um meio de simular o uso dos recursos de MessageDlg, em nosso idioma, por
intermédio de CreateMessageDialog que, como sabemos, cria a caixa de mensagens específicada pelo
parâmetro DlgType e com os botões indicados no parâmetro Buttons. Conforme o botão que receber o
clique do mouse, a função retornará um dos valores int a seguir:

Inglês Português Valor de retorno

Help Ajuda
Ok Ok 1
Cancel Cancelar 2
Abort Anular 3
Retry Repetir 4
Ignore Ignorar 5
Yes Sim 6
No Não 7
All Todos 8
No To All Não para todos 9
Yes To All Sim para Todos 10

Abaixo implementamos a função:

int __fastcall MessageDLG(const AnsiString Msg, TMsgDlgType DlgType,


TMsgDlgButtons Buttons, int HelpCtx)
{
// criamos o dialogo
TForm* __fastcall Dialogo=CreateMessageDialog(Msg, DlgType, Buttons);

// Este código atualiza o Caption


// tratando o parâmetro DlgType
switch(DlgType)
{
case mtCustom:
Dialogo->Caption="Nome do executável"; break;
case mtConfirmation:
Dialogo->Caption="Confirmação"; break;
case mtError:
Dialogo->Caption="Erro"; break;
case mtInformation:
Dialogo->Caption="Informação"; break;
case mtWarning:
Dialogo->Caption="Aviso"; break;
}
// matriz para os textos originais (inglês)
// dos botões (parâmetro Buttons)
const AnsiString OrigIngl[11] =
{"Help","OK","Cancel","Abort","Retry",
"Ignore","Yes","No","All","NoToAll","YesToAll"};
// matriz para os textos usados na
// tradução (Português)
const AnsiString TraduzBR[11] =
{"Aj&uda","Ok","Cancelar","&Anular","&Repetir",
"&Ignorar","&Sim","&Não","&Todos",
"Nã&o para todos", "Sim para &todos"};
// Loop para substituir o texto dos botões
for (int i=0; i<11; i++)
{
// Identificamos botão a botão
TButton* Botao=dynamic_cast<TButton*>(
Dialogo->FindComponent(OrigIngl[i]));
// inserimos o texto em português nos botões
if(Botao)
Botao->Caption=TraduzBR[i];
}
// guardamos o valor do botão pressionado
int valRtrn = Dialogo->ShowModal();
// deletamos o dialogo
delete Dialogo;
// retornamos o valor lido no botão
return valRtrn;

//===========================================================================

void __fastcall TForm1::Button5Click(TObject *Sender)


{
// lemos o valor retornado pela função
int valRtrndo=
MessageDLG("Mensagem", mtWarning,
TMsgDlgButtons()<<mbYes<<mbNo<<mbOK<<mbCancel<<mbAbort
<<mbRetry<<mbIgnore<<mbAll<<mbNoToAll<<mbYesToAll<<mbHelp, 0);
// Exibimos o valor retornado
ShowMessage(valRtrndo);

}
//---------------------------------------------------------------------------

InputBox
Agora vamos conhecer um tipo especial de caixas de diálogo: o InputBox, ou caixa para entrada de
dados. InputBox possui um campo de edição onde o usuário pode inserir valores string.

extern PACKAGE AnsiString __fastcall InputBox(


const AnsiString ACaption,
const AnsiString APrompt,
const AnsiString ADefault);
InputBox é um tipo especial usado para ler dados (string) inseridos pelo usuário no campo de edição
(um edit box) da caixa de diálogos. O parâmetro ACaption é o título da caixa de diálogos; o parâmetro
APrompt é a mensagem dirigida ao usuário; e o parâmetro ADefault é a string que aparece no campo
de edição da caixa de diálogos no momento em que a mesma é ativada.

void __fastcall TForm1::Button5Click(TObject *Sender)


{
AnsiString InputString =
InputBox("Título", "Mensagem", "String default");

ShowMessage(InputString);

Se o usuário escolher o botão Cancel ou fechar essa caixa de diálogos (Botão X ou tecla Esc), o
InputBox retornará a String default (mesmo que tal string tenha sido deletada ou modificada - use o
código acima para fazer uns testes). Se o usuário escolher o botão OK, o InputBox fechará retornando
a string atual do edit box.

Também é possível um InputBox sem uma string default:

InputBox("Título", "Mensagem", "");

Bem... na verdade, trata-se de uma string vazia.

InputQuery
Com a mesma aparência de um InputBox, também se trata de uma caixa de diálogos que admite
entrada de dados pelo usuário. A diferença básica entre esses dois diálogos é o valor de retorno, pois
enquanto InputBox retorna String, InputQuery retorna bool, possibilitando, ainda, capturar a String
(default ou a inserida pelo usuário), só que não como valor de retorno.

extern PACKAGE bool __fastcall InputQuery(


const AnsiString ACaption,
const AnsiString APrompt,
AnsiString &Value);

Use InputQuery para criar uma caixa de diálogos em cujo edit box o usuário poderá entrar com uma
string. O parâmetro ACaption é o caption; APrompt é o texto da caixa; e Value a string que aparece no
edit box. Se o usuário entrar com uma string no edit box e escolher OK, o parâmetro Value assumirá
esse novo valor. InputQuery retornará true se o usuário escolher OK, e false se o usuário escolher X,
Cancelar ou pressionar a tecla Esc.

void __fastcall TForm1::Button1Click(TObject *Sender)


{
bool Bol;
AnsiString Str = "String default";
Bol=InputQuery("Título", "Mensagem", Str);
Label1->Caption = Str;
if(Bol==true)
ShowMessage("true");
else
ShowMessage("false");

Se você precisar de uma caixa de mensagens com strings como valor de retorno, use InputBox.

SelectDirectory
Trata-se de um tipo especial de caixa de diálogos que permite ao usuário escolher um diretório. Duas
sintaxes são possíveis.

Primeira sintaxe:

extern PACKAGE bool __fastcall SelectDirectory(


const AnsiString Caption,
const WideString Root,
AnsiString &Directory);

Esta primeira sintaxe pode ser usada para exibir uma espécie de navegador de diretórios do Windows,
mas somente a partir de determinado diretório raiz. O parâmetro Caption especifica uma mensagem
que será exibida num label desse diálogo; o parâmetro Root especifica o diretório raiz (somente
diretórios internos a esse diretório serão exibidos) para o explorador. Por fim, o diretório selecionado
será retornado por intermédio do parâmetro Directory:

// inclua essa linha


#include <FileCtrl.hpp>

void __fastcall TForm1::Button1Click(TObject *Sender)


{
AnsiString Dir = "";
if (SelectDirectory("Mensagem", "C:\\", Dir))
Label1->Caption = Dir;
}
A segunda sintaxe:

extern PACKAGE bool __fastcall SelectDirectory(


AnsiString &Directory,
TSelectDirOpts Options,
int HelpCtx);

Esta sintaxe pode ser usada para chamar uma caixa de diálogos Select Directory. O diretório passado
para a função por intermédio do parâmetro Directory apresenta-se selecionado quando a caixa de
diálogos aparece. O nome do diretório que o usuário houver selecionado se torna o valor de Directory
quando a função retornar. O diálogo não muda o valor do diretório corrente.

O parâmetro HelpCtx serve para especificar o context ID de um tópico de ajuda (help file). Se você for
uma pessoa observadora, já deve ter notado, desde a primeira vez que este parâmetro apareceu em
nossos estudos, que, excetuando o caso de MessageDlgPosHelp onde havia o parâmetro adicional
HelpFileName especialmente voltado para tal finalidade, nunca mostramos como chamar o arquivo de
ajuda. Quero dizer, como chamar um arquivo de ajuda cujo caminho não possuo parâmetros para
especificar?

Na verdade é bem fácil. Basta ir ao menu Project, escolher Options..., selecionar a guia Application e
o mostrar o caminho do help file para o programa (Browse -> Help file). A partir daí, nossa caixa de
mensagens poderá chamar o arquivo de ajuda por intermédio da tecla F1 ou do botão Help.

O parâmetro Options guarda um grupo de valores. Se Options estiver vazio (TSelectDirOpts(),), o


usuário poderá selecionar somente um diretório pré-existente, já que o campo de edição não estará
disponível neste modo de exibição. Se o campo de edição (edit box) estiver presente na caixa de
diálogos, o usuário poderá inserir nele um novo nome de diretório (não existente ainda). Isso,
dependendo das opções constantes de tal parâmetro, fará que lhe será oferecida a oportunidade de criar,
ou não, este novo diretório.

#include <FileCtrl.hpp>

const SELDIRHELP = 1000;

void __fastcall TForm1::Button2Click(TObject *Sender)


{
AnsiString Dir = "C:\\Meus Documentos\\BCB6";
if (SelectDirectory(Dir,
TSelectDirOpts()<<sdAllowCreate<<sdPerformCreate<<sdPrompt,
SELDIRHELP))
Label2->Caption = Dir;

Em ambas as sintaxes, SelectDirectory retorna true se o usuário selecionar um diretório e escolher


OK, e false se o usuário escolher Cancel ou fechar a caixa sem selecionar um diretório.

A título de informação, estaremos traduzindo, a seguir, trechos do tópico TSelectDirOpt,


TSelectDirOpts type do Help do C++Builder:

Determina como um diálogo de seleção de diretórios reage quando o usuário entra com uma caixa de
diálogos não existente.

enum TSelectDirOpt { sdAllowCreate, sdPerformCreate, sdPrompt };


typedef Set<TSelectDirOpt, sdAllowCreate, sdPrompt> TSelectDirOpts;

Estes são os possíveis valores a serem adicionados ao grupo de opções:

sdAllowCreate Um edit box permitirá que o usuário insira um nome de diretório não existente.
Esta opção não cria um diretório: o próprio programa deverá ler o nome do diretório selecionado e
criá-lo, se necessário.

SdPerformCreate Usado somente em combinação com sdAllowCreate. Se o usuário inserir o


nome de um diretório não existente, tal diretório, se selecionado, será criado.

SdPrompt Usado somente em combinação com sdAllowCreate e sdPerformCreate. Exibe


uma caixa de mensagens que informa ao usuário que o diretório, cujo nome foi inserido no edit box,
não existe. Na mesma mensagem pergunta se deseja criá-lo. Em caso afirmativo o diretório é criado
e a função encerra retornando tal valor. Em caso negativo, a caixa de mensagens é fechada, o
diretótio não é criado e o diálogo SelectDirectory não é fechado.

****************************************

TStringList
listas de strings sem dificuldades

Alguns anos atrás, no início da difusão da Internet no Brasil, tive uma idéia. Escrever um dicionário
jurídico. Aperfeiçoei um pouco a idéia: "Não seria um dicionário de papel, mas um software". Como
não entendia nada de programação, procurei um profissional da área para desenvolver tal software para
mim, enquanto eu fosse escrevendo o conteúdo do dicionário propriamente dito. Não consegui
encontrar tal programador. Arregacei as mangas e comecei a estudar algumas linguagens de
programação (Visual Basic, Delphi, C, C++). Chegar ao C++Builder foi mera questão de comodidade.
Comprei livros, peguei tutoriais na internet, traduzi vários pontos do Help do BCB e fui à luta. À
medida que ia estudando tive uma idéia: "Por que não compilar todos os meus estudos, ensaios e
anotações num curso?" O Conteúdo (html) do DicasBCB é fruto dessa idéia. Por fim, abandonei a
advocacia, já que litigar em nome de terceiros não era exatamente algo que eu pudesse chamar de
prazeroso, e dediquei-me à programação.

Bem, chega de blá, blá, blá!

Lá no começo dos meus estudos, comecei a fazer alguns ensaios daquilo que um dia, talvez, viria a ser
o código (software) do Dicionário. Vejamos:

Coloque três Memos um Button e um RichEdit no Form. No RichEdit você poderá digitar ou
arrastar a palavra que quer o significado (cuidado pois os espaços são considerados).

Marque a propriedade FormStyle para fsStayOnTop;


a propriedade MaxLength do RichEdit para uns 50;
a propriedade Visible de Memo1 e Memo3 para false;
a propriedade WordWrap do Memo3 para false;
E digite o seguinte código no evento OnClick de Button1:

//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
/* cria a variável test para acumular o
conteudo da primeira linha de RichEdit*/
AnsiString test = RichEdit1 -> Lines -> Strings[0];
/* através da função IndexOf() faz uma busca
em Memo1, procurando string indêntica àquela
acumulada em test. Uma vez que a string seja
encontrada, identifica o número da linha
(Lines) e o acumula na variável NumStr*/
int NumStr = Memo1->Lines->IndexOf(test);
/*Se a variável NumStr for maior do que -1
(um negativo) - Porque a primeira linha
tem valor igual a zero e as seguintes +1, 2, 3, ...
Isso quer dizer: Se a string procurada foi
encontrada em alguma linha de Memo1 ...*/
if (NumStr > -1)
/*Temos que ter o cuidado de colocar,
em Memo3, o significado da palavra a ser
traduzida na mesma linha que o colocamos a
palavra a ser traduzida em Memo1, pois o
programa fará uma busca por essa linha
(NumStr) em Memo3, uma vez que ele já
conhece o número da linha. O programa
encontrará a linha e copiará o seu conteúdo
em Memo1, exibindo o resultado para o usuário*/
Memo2->Lines->Strings[0] = Memo3->Lines->Strings[NumStr];
// Limpa o RichEdit para receber novos valores
RichEdit1 -> Lines -> Strings[0] = "";

}
//---------------------------------------------------------------------------

Nota: O programa acima está exatamente como foi concebido. Não alterei nenhuma linha do programa
(melhorando-o ou piorando-o) para inseri-lo no presente artigo.

Jamais conclui o dicionário (nem o programa, nem o conteúdo do dicionário propriamente dito), pois
percebi que o trabalho seria demasiadamente complexo para ser desenvolvido por apenas uma pessoa.
Abandonei a idéia, mas guardei o código acima. Pelo menos ele nos servirá para que possamos ver que,
às vezes, por não conhecermos todos os recursos que temos em mãos, podemos estar partindo de
conceitos completamente errados na construção de nossos aplicativos.

Muito bem, na época que escrevi esse código, nem imaginava que existia um objeto chamado
TStringList. Se você achou legal o código acima, creio que mudará de opinião quando acabar de ler
este pequeno estudo sobre esse objeto que nos permite criar tal software com muito mais beleza e
eficiência.

TStringList é um objeto que possui habilidades de manipular listas de strings. As listas devem ser
criadas em tempo de execução. Veremos duas maneiras. Você pode escolher qualquer uma, pois o
resultado será equivalente. Mas um fato importante deve ser considerado. Se a lista deverá ser vista por
parte ou por toda a aplicação, ou se será enxergada por apenas um evento. Ou seja, a lista será global
ou local?

Nota: Para fins de facilitar a compreensão, recomendamos que você implemente todos os códigos-exemplos propostos no
presente artigo.
void __fastcall TForm1::Button1Click(TObject *Sender)
{
// criamos o objeto dinamicamente
TStringList *Str = new TStringList;

// adicionamos algumas strings na lista


// não ordenada (alfa-símbolo-numérica)
Str->Add("Terceira string");
Str->Add("Primeira string");
Str->Add("Segunda string");

// Count guarda o número de strings da lista


// NOTA: Count começa contar no zero e
// NOTA: Str->Strings[0] é a primeira string da lista
for(int i=0; i<Str->Count; i++)
// exibimos as strings da lista
ShowMessage(Str->Strings[i]);

// precisamos deletar o objeto


// TStringList criado dinamicamente
delete Str;
}

Primeiro vamos saber quais são as propriedades, os métodos e eventos que interessam a este objeto:

Propriedades

Em TStringList

Capacity
Count
Duplicates
Objects
Sorted
Strings

Derivadas de TStrings

CommaText
Names
StringsAdapter
Text
Values

Métodos

Em TStringList

~TStringList
Add
Clear
Delete
Exchange
Find
IndexOf
Insert
Sort
TStringList

Derivados de TStrings

AddObject
AddStrings
Append
Assign
BeginUpdate
EndUpdate
Equals
GetText
IndexOfName
IndexOfObject
InsertObject
LoadFromFile
LoadFromStream
Move
SaveToFile
SaveToStream
SetText

Derivado de TPersistent

GetNamePath

Derivado de TObject

AfterConstruction
BeforeDestruction
ClassInfo
ClassName
ClassNameIs
ClassParent
ClassType
CleanupInstance
DefaultHandler
Dispatch
FieldAddress
Free
FreeInstance
GetInterface
GetInterfaceEntry
GetInterfaceTable
InheritsFrom
InitInstance
InstanceSize
MethodAddress
MethodName
NewInstance
SafeCallException

Eventos

Em TStringList

OnChange
OnChanging

Estaremos estudando detalhadamente ou, ao menos, conhecendo os detalhes (Help BCB) de cada um
dos itens acima.

Continuemos. No exemplo acima, abordamos o uso de TStringList, Add(), Count, Strings[], new e
delete (note que este delete é diferente do método Delete). Vamos conhecê-los melhor. Diretamente do
HELP do BCB...

TStringList

TStringList mantém uma lista de strings. Use um objeto string list para guardar e manipular uma lista
de strings. TStringList além de implementar as propriedades e métodos introduzidos por TStrings,
introduz novas propriedades, eventos e métodos para:

Ordenar as strings na lista;


Impedir a duplicação de strings numa lista ordenada;
Responder a mudanças no conteúdo da lista.

TStringList::Add

Adiciona uma nova string na lista.

virtual int __fastcall Add(const System::AnsiString S);

Chame Add para adicionar uma string na lista. Se a lista for ordenada (ordem alfabética - veja a
propriedade Sorted), a string será adicionada na posição apropriada dentro da lista. Se a lista não for
ordenada, a string será adicionada no fim da lista. Add retorna a posição do item na lista, onde o
primeiro item da lista possui o valor 0.
Nota: Para listas ordenadas, Add pode disparar uma exceção EListError se a string a ser
adicionada já existir na lista e a propriedade Duplicates estiver assinalada para dupError. Se
Duplicates estiver assinalada para dupIgnore, tentativas de adicionar uma string duplicada na lista não
surtirão efeito. A propriedade dupAccept permitirá a duplicação de strings na lista.

BeginUpdate e EndUpdate devem ser usados sempre em conjunto com um enunciado try...catch para
assegurar que EndUpdate será chamado se ocorrer uma exceção. Um bloco que usa BeginUpdate e
EndUpdate geralmente possui a aparência a seguir:

void __fastcall TForm1::Button1Click(TObject *Sender)

{
ListBox1->Items->BeginUpdate();
try{
ListBox1->Items->Clear();
ListBox1->Items->Add("Some foo");
ListBox1->Items->Add("Some data");
ListBox1->Items->EndUpdate();
}
catch(...){
//Executado no caso de exceção
ListBox1->Items->EndUpdate();
throw;
}
}

TStringList::Count

Indica o número de strings na lista.


__property int Count = {read=GetCount, nodefault};

Use Count para saber o número de strings na lista, ou para localizar a posição de determinada string
em relação à última string da lista.

TStringList *MyStringList = new TStringList();

try
{
Session1->GetAliasNames(MyStringList);
// preenche um list box
for (int I = 0; I < MyStringList->Count; I++)
ListBox1->Items->Add(MyStringList->Strings[I]);
}
__finally
{
delete MyStringList;
}

TStringList::Strings
Enumera as strings, referenciando-as por um índice que, iniciado do 0, vai até Count – 1.

__property System::AnsiString Strings[int Index] = {read=Get, write=Put};

Use Strings para ler ou modificar a string de uma particular posição. Index devolve a posição da string,
onde 0 é a posição da primeira, 1 da segunda e assim por diante. Para achar a posição de uma string em
especial na lista, use o método IndexOf.

TStringList::TStringList

Cria um novo objeto TStringList.


inline __fastcall TStringList(void) : TStrings() { }

Para criar um objeto TStringList, use o operador new antes do construtor a fim de obter uma referência
indireta para a string list.

TStringList::~TStringList

Destrói uma instância de TStringList.

__fastcall virtual ~TStringList(void);

Não chame ~TStringList diretamente numa aplicação. Em vez disso, use a palavra-chave delete.
~TStringList libera a memória alocada para armazenar a lista de strings e os objetos referenciados
antes de chamar o destruidor da classe parente.

****************************************

Agora nós vamos avançar nos estudos, acrescentando alguns recursos de TStringList (IndexOf, Find,
Sort) em nosso código inicial:

void __fastcall TForm1::Button1Click(TObject *Sender)


{
// criamos o objeto dinamicamente
TStringList *Str = new TStringList;

// adicionamos algumas strings na lista


// não ordenada (alfa-símbolo-numérica)
Str->Add("Terceira string");
Str->Add("Primeira string");
Str->Add("Segunda string");

// Count guarda o número de strings da lista


// NOTA: Count começa contar no zero e
// NOTA: Str->Strings[0] é a primeira string da lista
for(int i=0; i<Str->Count; i++)
// exibimos as strings da lista
ShowMessage(Str->Strings[i]);

// Numa lista não ordenada, usamos


// IndexOf para localizar o índice
// da string
int NumStr = Str->IndexOf("Primeira string");
// se a string for encontrada na lista
if(NumStr!=-1)
// damos algumas informações sobre ela
ShowMessage((AnsiString)
"IndexOf responde, o índice da String procurada é\n"+
+ NumStr+"\n"+
Str->Strings[NumStr]);

// ordenamos a lista (alfabeticamente).


// Nota: Por coincidência, o alfabeto
// dará sentido à ordem nesta lista
Str->Sort();
// exibimos a lista, agora ordenada
for(int i=0; i<Str->Count; i++)
// exibimos as strings da lista
ShowMessage(Str->Strings[i]);

// Numa lista ordenada usamos Find()


// para localizar o índice da
// string na lista
Str->Find( "Primeira string", NumStr);

// se a string for encontrada


if(NumStr!=-1)
// damos algumas informações sobre ela
ShowMessage((AnsiString)
"Find responde, agora o índice da String procurada é\n"+
+ NumStr+"\n"+
Str->Strings[NumStr]);

// precisamos deletar o objeto


// TStringList criado dinamicamente
delete Str;

/*****************************************************
OBSERVE ALGUMAS DIFERENÇAS DA BUSCA REALIZADA POR
IndexOf e Find, FAZENDO O SEGUINTE TESTE.
SUBSTITUA int NumStr = Str->IndexOf("Primeira string");
POR int NumStr = Str->IndexOf("s");
E SUBSTITUA Str->Find( "Primeira string", NumStr);
POR Str->Find( "S", NumStr);
E CONCLUA O RESULTADO
*****************************************************/

TStringList::IndexOf
Retorna a posição de uma string na lista.

virtual int __fastcall IndexOf(const System::AnsiString S);

Chame IndexOf para obter a posição da primeira ocorrência da string S. IndexOf retorna o índice
(iniciado em 0) da string. Desse modo, se S estiver na primeira string da lista, IndexOf retornará 0, se S
for a segunda string, IndexOf retorna 1, e assim por diante. Se a string não existir na lista, IndexOf
retorna -1.

O exemplo a seguir usa um file list box, um directory list box, e um label no form. Quando o usuário
usa o directory list box para alterar diretórios, uma mensagem aparece e a cor do form muda se o
arquivo AUTOEXEC.BAT estiver localizado no novo diretório. O código deve ser escrito no evento
OnChange do directory list box:

void __fastcall TForm1::DirectoryListBox1Change(TObject *Sender)

{
FileListBox1->Directory = DirectoryListBox1->Directory;
if (FileListBox1->Items->IndexOf("AUTOEXEC.BAT") > -1)
{
Color = clYellow;
Label1->Caption = "Você está no diretório procurado!";
}
}

TStringList::Find

Encontra o índice (index) de determinada string numa lista ordenada e indica se uma string com tal
valor já existe na lista.

virtual bool __fastcall Find(const System::AnsiString S, int &Index);

Use Find para obter o index (índice) numa lista ordenada onde uma string S deve ser adicionada. Se a
string S já existir na lista, Find retorna true. Se a lista não contiver S, Find retorna false. O index onde
S se encontra deve ser retornado no parâmetro Index. O valor de Index inicia com 0, onde a primeira
string possui o index 0; a segunda, 1 e assim por diante.

Nota: Use Find somente em listas ordenadas. Em listas não-ordenadas use o método IndexOf.

Este exemplo usa um list box e um label no form. Quando a aplicação executa, um objeto string list é
criado e três strings são adicionadas a ele. O método Find faz a busca pela string 'Flowers'. Se a string
for encontrada, todas as strings da lista são adicionadas ao list box e o valor do índice da string
'Flowers' aparece no caption do controle label.

void __fastcall TForm1::Button1Click(TObject *Sender)

{
int Index;
TStringList* MyList = new TStringList();
MyList->Add("Animals");
MyList->Add("Flowers");
MyList->Add("Ocean");
MyList->Sort();
if (MyList->Find("Flowers", Index)){
ListBox1->Items->AddStrings(MyList);
Label1->Caption = "Flowers tem um índice valor de " +
AnsiString(Index);
}
delete MyList;
}

TStringList::Sort

Organiza as strings na lista em ordem ascendente.

virtual void __fastcall Sort(void);

Chame Sort para ordenar (alfabeticamente) as strings numa lista que possui a propriedade Sorted
assinalada para false. String lists com a propriedade Sorted assinalada para true são ordenadas
automaticamente.

Nota: Sort usa AnsiCompareStr para organizar as strings. Esta ordem leva em consideração o
sistema do local onde a aplicação está executando.

****************************************

Agora vamos aprender a criar listas globais, bem como a trabalhar com as mesmas em disco.
Inicialmente veremos dois modos distintos de criar as listas. Escolha qualquer um deles para seus
trabalhos, pois o resultado final será equivalente. Coloque um Memo e um Button no Form. Assinale a
propriedade ScrollBars do Memo para ssVertical. O objeto TStringList será criado de forma a ser visto
por toda a aplicação (na verdade, por todo o form). Será criado no evento OnCreate do Form e
destruído no evento OnDestroy do mesmo. Eis o código completo da Unit1.cpp:

//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"

// declaramos objeto TStringList


TStringList *Str;

TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------

void __fastcall TForm1::FormCreate(TObject *Sender)


{
// criamos o objeto dinamicamente
Str = new TStringList;

}
//---------------------------------------------------------------------------

void __fastcall TForm1::Button1Click(TObject *Sender)


{
// adicionamos strings na lista
Str->Add("AAA");
Str->Add("BBB");
Str->Add("CCC");
Str->Add("DDD");
Str->Add("EEE");
// limpamos o memo
Memo1->Clear();
// colocamos as strings da lista no memo
for(int i=0; i<Str->Count; i++)
{
Memo1->Lines->Add(Str->Strings[i]);
}
}
//---------------------------------------------------------------------------

void __fastcall TForm1::FormDestroy(TObject *Sender)


{
// destruímos o objeto
delete Str;
}
//---------------------------------------------------------------------------

Dê vários cliques em Button1 e confira o resultado no Memo. Perceba que a lista vai crescendo à
medida que esse botão é pressionado. Isso não ocorreria se a lista tivesse sido criada e destruída dentro
do evento OnClick do botão, como foi feito nos primeiros exemplos.

Vamos fazer a mesma coisa de um modo diferente. No cabeçalho Unit1.h, incluímos memory:

#include<memory>

e acrescentamos a seguinte linha como declaração privada:


std::auto_ptr<TStringList>Str;

Modificamos o construtor do Form na unit.cpp:


__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner), Str(new TStringList)

No evento OnClick de Button1:

// adicionamos strings na lista


Str->Add("AAA");
Str->Add("BBB");
Str->Add("CCC");
Str->Add("DDD");
Str->Add("EEE");
// limpamos o memo
Memo1->Clear();
// colocamos as strings da lista no memo
for(int i=0; i<Str->Count; i++)
{
Memo1->Lines->Add(Str->Strings[i]);
}

Vejamos o código completo:

Unit1.h
//---------------------------------------------------------------------------
#ifndef Unit1H
#define Unit1H
//---------------------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include <Buttons.hpp>
#include<memory>

//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published: // IDE-managed Components
TButton *Button1;
TButton *Button2;
TMemo *Memo1;
void __fastcall Button1Click(TObject *Sender);
private: // User declarations
// adicionamos essa declaração
std::auto_ptr<TStringList>Str;
public: // User declarations
__fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif

Unit1.cpp
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"

TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner), Str(new TStringList)

}
//---------------------------------------------------------------------------

void __fastcall TForm1::Button1Click(TObject *Sender)


{
// adicionamos strings na lista
Str->Add("AAA");
Str->Add("BBB");
Str->Add("CCC");
Str->Add("DDD");
Str->Add("EEE");
// limpamos o memo
Memo1->Clear();
// colocamos as strings da lista no memo
for(int i=0; i<Str->Count; i++)
{
Memo1->Lines->Add(Str->Strings[i]);
}
}
//---------------------------------------------------------------------------

Dê vários cliques em Button1 e confira o resultado no Memo. Perceba que a lista vai crescendo à
medida que esse botão é pressionado. Isso não ocorreria se a lista tivesse sido criada e destruída dentro
do evento OnClick do botão, como foi feito nos primeiros exemplos.

Agora que sabemos como criar as listas, vamos aprender a trabalhar com elas em disco. Basta usar os
métodos SaveToFile para salvar e LoadFromFile para carregar. Coloque um segundo botão no Form.

Veja o código:

void __fastcall TForm1::Button1Click(TObject *Sender)


{
// adicionamos strings na lista
Str->Add("AAA");
Str->Add("BBB");
Str->Add("CCC");
Str->Add("DDD");
Str->Add("EEE");

// limpamos o memo
Memo1->Clear();
// colocamos as strings da lista no memo
for(int i=0; i<Str->Count; i++)
{
Memo1->Lines->Add(Str->Strings[i]);
}

// aguardamos dois segundos


Sleep(2000);
// "FFF" será a terceira string da lista
Str->Insert(2, "FFF");
// agora "HHH" será a terceira
Str->Insert(2, "HHH");

// limpamos o memo
Memo1->Clear();
// colocamos as strings da lista no memo
// perceba que a lista não está ordenada
for(int i=0; i<Str->Count; i++)
{
Memo1->Lines->Add(Str->Strings[i]);
}

// salvamos a lista na pasta da aplicação


// no arquivo Data.txt
Str->SaveToFile(
ExtractFilePath(
Application->ExeName)+ "\Data.txt");

// limpamos a lista
Str->Clear();
}
//---------------------------------------------------------------------------

void __fastcall TForm1::Button2Click(TObject *Sender)


{
// a partir de agora, alteramos a propriedade
// Sorted da lista Str para true. Isso significa
// que a lista permanecerá ordenada e não aceitará
// mais a inclusão de strings por intermédio de
// Insert
Str->Sorted=true;

// Carregamos a lista do disco


Str->LoadFromFile(
ExtractFilePath(
Application->ExeName)+ "\Data.txt");

// limpamos o memo
Memo1->Clear();

// colocamos as strings da lista no memo


// perceba que a lista está ordenada
for(int i=0; i<Str->Count; i++)
{
Memo1->Lines->Add(Str->Strings[i]);
}

// aguardamos cinco segundos


// veja bem as posições das
// strings na lista
Sleep(5000);
// como a lista está ordenada, Add
// inserirá a string em seu devido
// lugar na lista
Str->Add("GGG");

// limpamos o memo
Memo1->Clear();

// colocamos as strings da lista no memo


// perceba que a lista está ordenada
for(int i=0; i<Str->Count; i++)
{
Memo1->Lines->Add(Str->Strings[i]);
}
}

Como funciona:

Dê um clique em Button1 e fique observando o que acontece no memo (deve demorar dois segundos).
Depois vá até a pasta do projeto e observe que o programa criou um arquivo ali: "Data.txt". Abra esse
arquivo e observe o seu conteúdo, comparando-o com o conteúdo do memo.

Feito isso, dê um clique em Button2 e observe, atentamente, o que acontecerá com o conteúdo do
memo (deve demorar cinco segundos). Agora dê um novo clique em Button1. O programa disparará
uma exceção exibindo uma caixa de mensagens com o seguinte aviso: Operation not allwed on
sorted string list. Sabe o motivo dessa mensagem? Lembra-se de que a lista não aceita mais inclusão
por intermédio de insert?

Faça o seguinte. Coloque um a seguinte linha no evento OnClick de Button1 (tem que ser antes da
chamada a Insert):

Str->Sorted=false;

A mensagem de erro desaparecerá e o evento será executado integralmente.

No exemplo atual conhecemos LoadFromFile, SaveToFile, Clear(), Insert e Sorted. Vejamos cada um
deles.

TStrings::LoadFromFile

Preenche a lista com as linhas de texto de um arquivo especificado.

virtual void __fastcall LoadFromFile(const System::AnsiString FileName);

Chame LoadFromFile para encher a lista de um objeto TStrings com o arquivo especificado por
FileName (lembre-se de que este método é derivado de TStrings). Cada linha no arquivo, indicada por
caracteres carriage return ou linefeed, é anexada como uma string na lista.

Nota: LoadFromFile usa o método Add para adicionar as strings que são lidas desde o arquivo.
Este exemplo usa um memo e um button no form. Quando o button recebe um clique o memo é
preenchido com o conteúdo do arquivo.

void __fastcall TForm1::Button1Click(TObject *Sender)

{
Memo1->Lines->LoadFromFile("Unit1.cpp");
}

Este exemplo requer dois controles TRichEdit colocados no form. Quando o form se tornar visível, o
primeiro controle Rich Edit exibirá o conteúdo do arquivo não formatado, apresentando, porém, o
código de formatação deste componente. O segundo RichEdit exibirá o conteúdo devidamente
formatado.

void __fastcall TForm1::FormCreate(TObject *Sender)

{
// você precisa criar e especificar o caminho correto do arquivo
char const *Path = "C:\\Meus Documentos\\Teste.rtf";
RichEdit1->PlainText = true;
RichEdit1->Lines->LoadFromFile(Path);
RichEdit2->PlainText = false;
RichEdit2->Lines->LoadFromFile(Path);
}

TStrings::SaveToFile

Salva a string list no arquivo especificado.

virtual void __fastcall SaveToFile(const System::AnsiString FileName);

Chame SaveToFile para salvar as strings da lista no arquivo especificado em FileName. Cada string na
lista é escrita em uma linha em separado no arquivo.

Este exemplo usa um memo e um button no form. Quando o button recebe um clique, o memo é
preenchido com o conteúdo do arquivo e salvado em um novo arquivo.

void __fastcall TForm1::Button1Click(TObject *Sender)

{
Memo1->Lines->LoadFromFile("Unit1.cpp");
Memo1->Lines->SaveToFile("Unit1.txt");
}

Este exemplo exibe uma caixa de diálogos de Imprimir com um check box "Imprimir em arquivo"
assinalado. O Conteúdo do rich edit será impresso no lugar indicado na caixa de mensagens:
void __fastcall TForm1::Button1Click(TObject *Sender)
{
PrintDialog1->Options << poPrintToFile;
PrintDialog1->PrintToFile = true;
if (PrintDialog1->Execute())
{
if (PrintDialog1->PrintToFile)
{
SaveDialog1->Title = "Imprimir no arquivo:";
if (SaveDialog1->Execute())
RichEdit1->Lines->SaveToFile(SaveDialog1->FileName);
else
RichEdit1->Print("");
}
}
}

TStringList::Clear

Deleta todas as strings da lista.

virtual void __fastcall Clear(void);

Use Clear para esvaziar a lista de strings. Todas as referências a objetos associadas também são
removidas. De outra forma, os objetos não seriam, por si mesmos, removidos.

TStringList::Insert

Insere uma string na lista na posição especificada por Index.

virtual void __fastcall Insert(int Index, const System::AnsiString S);

Use Insert para adicionar a string S na posição especificada por Index na lista. Se Index for 0, a string
será inserida no início da lista. Se Index for 1, a string será colocada na segunda posição da lista, e
assim por diante.

Se a string possuir algum objeto associado, em vez de Insert, use o método InsertObject.

Nota: Se a lista for ordenada, eventual chamada a Insert ou a InsertObject deve disparar uma
exceção EListError. Use Add ou AddObject com listas ordenadas.

Este exemplo usa um list box e um button no form. Quando o form aparece, o list box contém cinco
items. Quando o usuário dá um clique no botão, uma nova string é inserida no topo da lista de items:

void __fastcall TForm1::FormCreate(TObject *Sender)


{
for (int i = 1; i <= 5; i++)
ListBox1->Items->Add("Item " + AnsiString(i));
}
//--------------------------------------------------------------------------

void __fastcall TForm1::Button1Click(TObject *Sender)

{
ListBox1->Items->Insert(0, "Novo item inserido");
}

//---------------------------------------------------------------------------

TStringList::Sorted

Especifica se a string deve ser ordenada automaticamente na lista.

__property bool Sorted = {read=FSorted, write=SetSorted, nodefault};

Assinale Sorted para true para determinar que as strings na listas sejam automaticamente ordenadas
ascendentemente. Assinale Sorted para false para permitir que as strings permaneçam onde estão
inseridas. Quando Sorted está false, as strings na lista podem ser colocadas na ordem ascendente em
qualquer momento pela chamada ao método Sort.

Se Sorted estiver true, não use Insert para adicionar strings na lista. Em vez de Insert, use Add, que
possui habilidades de inserir a string na posição apropriada. Se Sorted estiver false, use Insert para
adicionar strings em determinada posição específica da lista, ou Add para acrescentar a string no fim da
lista.

Nota: Quando Sorted está true, a string list usa AnsiCompareStr para ordenar a lista. Esta
ordenação leva em consideração o sistema do local onde a aplicação está executando.

Este exemplo usa um list box no form. Quando a aplicação executa, um objeto string list é criado e três
strings são adicionadas ao componente. As strings são ordenas e adicionadas no list box:

void __fastcall TForm1::FormCreate(TObject *Sender)

{
TStringList *pList = new TStringList();
pList->Add("Plants");
pList->Add("Animals");
pList->Add("Minerals");
pList->Sorted = true;
ListBox1->Items->AddStrings(pList);
delete pList;
}

****************************************

Vamos abrir espaço em nosso estudo para pequena introdução aos arquivos INI.

TIniFile é um invólucro de baixo nível para aplicações de 16 bits Windows 3.x. Arquivos *.ini
capacitam uma aplicação a armazenar e a recuperar, facilmente, informações de configuração. A
informação é armazenada no formato texto, sendo que tais informações são inseridas em grupos
chamados "sections" (Seções). Por exemplo, o arquivo WIN.INI contém uma seção chamada
"[Desktop]". Dentro de cada Seção, cada valor de dados é guardado numa chave que poderá ser
acessada (escrita ou lida) isola ou conjuntamente pela aplicação. Embora esse tipo de arquivo esteja
ultrapassado pelo advento do Registro a partir do Windows 95, muitos programadores continuam
preferindo usá-los, pelo fato de serem mais facilmente acessados e manipulados.

Arquivos *.ini implementam métodos que nos capacitam a ler e/ou escrever data, horas, valores
booleanos, inteiros, strings, pontos flutuantes etc. Outros métodos nos permitem criar, apagar ou
verificar se seções ou determinados valores existem.

Normalmente, as funções que mais nos interessam são aquelas destinadas à escrita ou à leitura de dados
nos arquivos. Via de regra essas funções possuem os seguintes aspectos:

Função("Seção", "Chave", "Dado");

A função usada para escrever uma string em disco chama-se WriteString(). No exemplo anterior:

WriteString("Seção", "Chave", "Dado");

Escreveria algo parecido com isso no arquivo INI:

[Seção]
Chave=Dado

Para ler esse valor, usamos a função ReadString():

ReadString("Seção", "Chave", "X");

onde "X" pode ser qualquer coisa que queiramos colocar, uma vez que o valor retornado será "Dado",
sem as aspas.

Passemos a um exemplo prático. Coloque um Button no form:

void __fastcall TForm1::Button1Click(TObject *Sender)


{
// incluímos: #include <inifiles.hpp>

// criamos o arquivo
TIniFile* ini = new TIniFile("c:\\Teste.ini");
// gravamos os dados no arquivo
ini->WriteString("Testando", "Strings", "abcde\nfghi");

// variável para ler o valor


AnsiString Str;
// lemos o valor
Str=ini->ReadString("Testando", "Strings", "<erro>");
// exibimos o dado lido
ShowMessage(Str);
// deletamos o objeto dinâmico
delete ini;
}

O código acima gravará as seguintes linhas em disco (Teste.ini):

[Testando]
Strings=abcde

(Importante: abra o arquivo Teste.ini e confira a veracidade da informação acima, pois em breve
retornaremos a esse assunto!)

Observe que alguns caracteres simplesmente sumiram (os que estavam situados após o caracter de nova
linha '\n'). Observe, também, que entre a Seção e a Chave existe um sinal de igual (=) sem qualquer
espaço.

****************************************

Bem, voltemos ao escopo de nosso tutorial. Estudaremos, agora, as seguintes propriedades e método de
TStringList derivados de TString:

propriedades: Names, Values, Text, CommaText;

método: IndexOfName.

TStrings::Names

Indica o nome particular de uma string com a forma Name=Value.

__property System::AnsiString Names[int Index] = {read=GetName};

Quando a lista de strings para o objeto TStrings inclui strings da forma Name=Value, Names pode ser
usado para acessar o nome particular de uma string. Names é o nome da string na posição indicada por
Index, que devolve a posição da string, onde 0 é a posição da primeira string, 1 da segunda e assim por
diante. Se a string na posição especificada não possuir a forma Name=Value, Names retorna uma
string vazia.

Strings da forma Name=Value são freqüentemente encontradas em arquivos INI. Por exemplo, eis aqui
umas poucas strings de um típico arquivo INI:

DisplayGrid=1

SnapToGrid=1
GridSizeX=8
GridSizeY=8
As strings que compõem a propriedade Params de um componente database (TDatabase) possuem
este formato também.

O nome que identifica a string está à esquerda do sinal de igual (=), e o Value atual do Name
identificador está do lado direito. Não deve haver espaços antes ou depois do sinal de igual.

TStrings::Values

Representa o valor de determinada string associada com um Name dado, em strings com o formato
Name=Value.

__property System::AnsiString Values[System::AnsiString Name]={read


=GetValue, write=SetValue};

O exemplo a seguir usa dois list boxes e um button no form. Quando o usuário dá um clique no botão,
o aplicativo faz uma verificação nos conteúdos dos list boxes. Se for encontrado, no list box de origem
(ListBox2) alguma string no formato Name=Value, ele copiará tal string no list box de destino
(ListBox1). Caso o programa encontre, no list box de destino, alguma string no formato Name=Value,
cujo Name também foi encontrado no list box de origem (ListBox2), atualizará o Value de tal string no
ListBox1.

//---------------------------------------------------------------------------

void RelacionaStrings(TStrings *Dest, TStrings *Source)

{
for (int i = 0; i < Source->Count; i++)
{
if (Source->Strings[i].Pos("="))
{
Dest->Values[(Source->Names[i])] =
Source->Values[(Source->Names[i])];
}
}
}

//---------------------------------------------------------------------------

void __fastcall TForm1::Button1Click(TObject *Sender)

{
RelacionaStrings(ListBox1->Items,ListBox2->Items);
}

NOTA: para entender Pos()

int __fastcall Pos(const AnsiString& subStr) const;


Pos() retorna a posição do primeiro caracter de uma substring
especificada na string. Se a substring não for encontrada na string,
Pos() retorna “zero”. Exemplo:

void __fastcall TForm1::Label1Click(TObject *Sender)


{
AnsiString test = "O_grande_teste_está_feito";
Label1->Caption = test.Pos("está");
}

TStrings::Text

Arruma as strings num objeto TStrings com se fosse uma única string com as strings individuais
delimitadas por carriage returns e line feeds.

Nota: carriage return e linefeed são caracteres da tabela ASCII.

__property System::AnsiString Text = {read=GetTextStr,


write=SetTextStr};

Para usar Text, as strings na lista devem estar separadas por carriage return, ou line feed. Se o conteúdo
propriamente dito de alguma string da lista contiver um caracteres de carriage return e de line feed, o
valor devolvido por Text poderá indicar mais strings do que aquela indicada pela propriedade Count.

Quando assinalado Text, o valor pode ser analisado como separado por substrings toda vez que um
sinal de carriage return ou linefeed for encontrado. (os dois não precisam formar um par).

Se uma string na lista contiver algum caracter de carriage return ou linefeed, o uso da propriedade
CommaText poderá retornar uma uma quantidade menor ou incerta de strings formatadas.

void __fastcall TForm1::Button4Click(TObject *Sender)


{

// Criamos a lista dinâmica


TStringList* ListaINI = new TStringList;

// adicionamos dados na lista


ListaINI->Add("Um=primeiro");
ListaINI->Add("dois=segundo");
ListaINI->Add("três=terceiro");
ListaINI->Add("quatro=quarto");
ListaINI->Add("cinco=quinto");
ListaINI->Add("seis=sexto");

// exibimos a lista através de Text


ShowMessage(ListaINI->Text);

// deletamos o arquivo dinâmico


delete ListaINI;

}
TStrings::CommaText

Coloca as strings de um objeto TStrings no sistema de formato de dados (SDF).

__property System::AnsiString CommaText =


{read=GetCommaText, write=SetCommaText};

Use CommaText para colocar todas as strings de um objeto TStrings separados por vírgulas,
delimitando as strings.

Quando CommaText retorna, qualquer string, na lista, que incluir espaços, vírgulas ou aspas ficará
contida entre um par de aspas, e quaisquer aspas numa string serão repetidas. Por exemplo, para uma
lista que contenha as seguintes strings:

Stri,ng 1

Stri"ng 2
String 3
String4

CommaText deve retornar:

"Stri,ng 1","Stri""ng 2","String 3",String4

Quando usado CommaText, o valor será retornado como texto no formato SDF. Pelo formato SDF,
strings são separadas por vírgulas ou espaços, e opcionalmente enclausuradas dentro de um par de
aspas. Aspas que fazem parte de uma string são repetidas para que sejam distinguidas das aspas que
cercarão a string. Espaços e vírgulas existentes não são contidos individualmente por um par de aspas
delimitadoras. Duas vírgulas, uma ao lado da outra, podem significar uma string vazia, mas espaços
que aparecem um ao lado do outro são ignorados. Por exemplo, suponha-se CommaText está assinaldo
por:

"Stri,ng 1", "Stri""ng 2" , String 3,String4

A lista, então, deve conter:

Stri,ng 1

Stri"ng 2
String
3
String4

Façamos uma salada de TStringLis a lá TIniFile.

void __fastcall TForm1::Button1Click(TObject *Sender)


{
// incluímos: #include <inifiles.hpp>
// criamos o arquivo
TIniFile* INI = new TIniFile("c:\\Teste.ini");

// Criamos a lista dinâmica


TStringList* ListaINI = new TStringList;

// adicionamos dados na lista


// observe o espaço na segunda string
ListaINI->Add("abcd");
ListaINI->Add("efgh ijkl");
ListaINI->Add("mnop");

// gravamos os dados no arquivo


INI->WriteString("Testando", "StringList",
QuotedStr(ListaINI->CommaText));

// Para lê-lo
ListaINI->Clear();

// carregamos os dados na lista


ListaINI->CommaText = INI->ReadString(
"Testando", "StringList", "<erro>");
// exibimos a lista
ShowMessage(ListaINI->Text);

// deletamos os arquivos dinâmicos


delete ListaINI;
delete INI;

O exemplo acima gravará no arquivo Teste.ini:

[Testando]
StringList='abcd,"efgh ijkl",mnop'

Faça algumas experiências no código acima retirando QuotedStr() e/ou CommaText.

Nota: Para entender QuotedStr():

void __fastcall TForm1::Button1Click(TObject *Sender)


{
String Str="Teste";
ShowMessage(QuotedStr(Str));
}

TStrings::IndexOfName

Retorna a posição da primeira string com o formato Name=Value na especificada parte name.

int __fastcall IndexOfName(const System::AnsiString Name);

Use IndexOfName para localizar a primeira ocorrência de uma string com a forma Name=Value onde
a parte Name é igual à do parâmetro Name. IndexOfName retorna o índice da string na lista (que se
inicia com zero). Se nenhuma string na lista possuir o Name indicado, IndexOfName retorna -1.

Strings do formato Name=Value são geralmente encontrados em arquivos INI.

Nota: Se houver mais de uma string com o mesmo nome a ser encontrado, IndexOfName
retornará apenas a posição da primeira.

O exemplo a seguir leva um Button no form.

void __fastcall TForm1::Button5Click(TObject *Sender)


{
// Criamos a lista dinâmica
TStringList* ListaINI = new TStringList;

// adicionamos dados na lista


// observe o espaço na segunda string
ListaINI->Add("Um=primeiro");
ListaINI->Add("dois=segundo");
ListaINI->Add("três=terceiro");
ListaINI->Add("quatro=quarto");
ListaINI->Add("cinco=quinto");
ListaINI->Add("Strings=abcde\nfghi");
ListaINI->Add("sete=sétimo");

// um pseudo ini
ListaINI->SaveToFile("c:\\Teste.ini");

// limpamos a lista
ListaINI->Clear();
// carregamos a lista
ListaINI->LoadFromFile("c:\\Teste.ini");
// verificamos alguns dados
ShowMessage((AnsiString)
"O Index da string cujo Name é cinco é: "+
ListaINI->IndexOfName("cinco"));

ShowMessage(ListaINI->Values["Strings"]);

ShowMessage((AnsiString)
"O Index da string cujo Name é sete é: "+
ListaINI->IndexOfName("sete"));

// deletamos o arquivo dinâmico


delete ListaINI;

O exemplo acima gravará as seguintes linhas no arquivo Teste.ini:

Um=primeiro
dois=segundo
três=terceiro
quatro=quarto
cinco=quinto
Strings=abcde
fghi
sete=sétimo

Não exatamente do modo acima. Abra-o arquivo Teste.ini com o bloco de notas (NotePad) para
visualizá-lo. Você perceberá que, no lugar do '\n', existe um caracter estranho (uma espécie de
quadradinho). Esse é o real modo que o arquivo se encontra gravado, pois a string não pula para a linha
de baixo pelo fato de encontrar algum caracter de nova linha ('\n'), como acontece com alguns editores
de texto. Abra esse mesmo arquivo com o WordPad para perceber a sutil diferença!

Bem, já estamos diante de alguma melhora. Lembra-se de que nosso exemplo INI não gravou nenhum
caracter depois do caracter de nova linha. Lembremos como o arquivo ficou gravado no disco:

[Testando]
Strings=abcde

E nós havíamos mandado o programa gravar uma string exatamente igual a essa última. Lembra-se?

ini->WriteString("Testando", "Strings", "abcde\nfghi");

O que isso significa? Significa que o tipo dos caracteres a serem guardados em disco será um fator
crucial na escolha do tipo de recurso a ser utilizado. TStringList é mais trabalhoso para esta finalidade,
mas se trata de um recurso mais poderoso. Imagine que, por exemplo, os dados a serem guardados em
disco estarão criptografados, cuja abrangência alcance os valores estendidos da tabela ASCII. Usar
TIniFiles, nessas situações, poderá produzir um erro fatal que prejudicará todo o funcionamento do
programa!!!!

Isso não significa que não serão encontrados problemas. Observe que ambos os códigos de que estamos
nos referindo, embora tratem de modo diverso as strings em disco, devolvem a mesma substring. Mas ,
de qualquer forma, esses problemas podem ser mais facilmente tratados com TStringList do que com
TIniFiles, uma vez que TStrings não mutilou a string... apenas não a carregou do modo esperado. Os
exemplos ilustraram bem essa situação. Vejamos outro exemplo, com outros caracteres:

void __fastcall TForm1::Button1Click(TObject *Sender)


{
/**************** USANDO TIniFile *************/

// incluímos: #include <inifiles.hpp>

// criamos o arquivo
TIniFile* ini = new TIniFile("c:\\Teste.ini");
// gravamos os dados no arquivo
ini->WriteString("Testando",
"Strings", "r‡ “„Œ“EŠŒHu“˜Mo›¦ -");

// variável para ler o valor


AnsiString Str;
// lemos o valor
Str=ini->ReadString("Testando", "Strings", "<erro>");
// exibimos o dado lido
ShowMessage(Str);
// deletamos o objeto dinâmico
delete ini;

/**************** USANDO TStringList *************/

// Criamos a lista dinâmica


TStringList* ListaINI = new TStringList;

// adicionamos dados na lista


ListaINI->Add("r‡ “„Œ“EŠŒHu“˜ Mo›¦-");

// um pseudo ini
ListaINI->SaveToFile("c:\\Teste.ini");

// limpamos a lista
ListaINI->Clear();
// carregamos a lista
ListaINI->LoadFromFile("c:\\Teste.ini");
// verificamos alguns dados
ShowMessage(ListaINI->Text);

// deletamos o arquivo dinâmico


delete ListaINI;

No exemplo atual TIniFile mutila, grava e devolve o resultado de seu funcionamento (ou seja, apenas
os dois primeiros caracteres da string), enquanto que TStringList devolve exatamente o resultado que
esperamos. Concluindo tudo isso, temos que os caracteres usados interferem diretamente no resultado,
tanto para um, quanto para outro objeto, mas a influência sofrida por TIniFile é mais devastadora.

****************************************

Nota: a partir de agora assumimos que você já entendeu que os objetos TStringList precisam ser
criados e, conforme o caso, deletados. Deixaremos a criação e destruição desse objeto por sua conta,
nos limitando a mencioná-lo em nossos códigos. Via de regra, estaremos usando a nomenclatura
ListaStr para designar um TStringList.

TStringList::Duplicates

Especifica se uma lista (ordenada) aceitará duplicação de strings.

enum TDuplicates { dupIgnore, dupAccept, dupError };


__property TDuplicates Duplicates = {read=FDuplicates,
write=FDuplicates, nodefault};

Use Duplicates para especificar o que acontecerá quando de uma tentativa de adição de string
duplicada numa lista ordenada. Duplicates pode assumir um dos seguintes valores.

Valor e características
dupAccept Permite a duplicação de strings na lista.

dupError Dispara uma exceção EListError quando houver uma tentativa de


adicionar uma string duplicada na lista ordenada.

dupIgnore Impede a tentativa de duplicar strings na lista.

Duplicates surtirá efeito somente para strings que ainda serão adicionadas na lista. Em relação às
strings pré-existentes no momento do acionamento de Duplicates, dupIgnore ou dupError não farão
coisa alguma.

Nota: Duplicates não funciona em listas não ordenadas.

Coloque um button e um group box no form. E coloque três radio buttons nesse group box alterando
seus Captions:

GroupBox1->Caption = Duplicates
RadioButton1->Caption = dupAccept
RadioButton2->Caption = dupError
RadioButton3->Caption = dupIgnore

Implemente o código a seguir e faça alguns testes tentando duplicar strings na lista.

//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;

// declaramos objeto TStringList


TStringList *ListaStr;

//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------

void __fastcall TForm1::FormCreate(TObject *Sender)


{
// criamos o objeto dinamicamente
ListaStr = new TStringList;
// a lista será ordenada
ListaStr->Sorted=true;

}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
// String para a lista
AnsiString Str;

// adicionamos dados string


Str=InputBox("Adiconando Strings na lista", "Digite uma String", "");

// adicionamos a string na lista


ListaStr->Add(Str);

// exibimos a lista através de Text


ShowMessage(ListaStr->Text);

}
//---------------------------------------------------------------------------
void __fastcall TForm1::RadioButton1Click(TObject *Sender)
{
// a lista aceitará strings duplicadas
ListaStr->Duplicates=dupAccept;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::RadioButton2Click(TObject *Sender)
{
// a string não aceitará strings duplicadas
// e dispará uma exceção avisando
ListaStr->Duplicates=dupError;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::RadioButton3Click(TObject *Sender)
{
// a lista simplesmente não aceitará
// a string duplicada sem emitir aviso
ListaStr->Duplicates=dupIgnore;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormDestroy(TObject *Sender)
{
// deletamos a lista variável
delete ListaStr;
}
//---------------------------------------------------------------------------

TStringList::Delete

Remove as strings especificadas pelo parâmetro Index.

virtual void __fastcall Delete(int Index);

Use Delete para remover uma única string da lista. Se houver algum objeto associado com tal string, a
referência ao objeto será removida também. Index reporta a posição da string, onde 0 é a primeira, 1 a
segunda e assim por diante.
Acrescente mais um botão no form do exemplo anterior com o seguinte código no evento OnClick:

void __fastcall TForm1::Button2Click(TObject *Sender)


{
String Apaga;
Apaga=InputBox("Deletando Strings da lista",
"Digite a String a ser deletada", "");
int Indice;
ListaStr->Find( Apaga, Indice);

ShowMessage(Indice);

if(Indice != ListaStr->Count)
ListaStr->Delete(Indice);
else
ShowMessage("Não existe tal dado na lista!");

O código acima precisa ser melhorado, pois Find busca pela substring, podendo produzir resultados
indesejáveis.

****************************************

Assim concluímos o objetivo de nosso estudo, onde foram abordados, a nosso ver, os principais
métodos e propriedades de TStringList. A partir de agora nos limitaremos a expor a tradução dos
métodos e propriedades restantes, diretamente do help do BCB. Em determinados momentos
poderemos alterar algum dado, ou colocar algum exemplo de nossa autoria, sem, contudo,
necessariamente mencionar tal fato.

TStringList::Capacity

Indica o número de strings para o qual a string tem alocado memória.

__property int Capacity = {read=FCapacity,


write=SetCapacity,
nodefault};

Use Capacity para descobrir quanta memória está disponível para receber strings, ou para realocar a
memória para colocar mais ou algumas poucas strings. Capacity é o número de entradas na string list
que precisa ser alocada, em oposição a Count, que é o número de strings na lista. Dessa forma,
Capacity Sempre será maior ou igual a Count.

Em nossos testes, Capacity progrediu da seguinte forma: enquanto Count


estava entre 1 e 4, Capacity tinha o valor igual a 4; enquanto Count estava entre
5 e 8, Capacity tinha o valor igual a 8; enquanto Count estava entre 9 e 12,
Capacity tinha o valor igual a 12; enquanto Count estava entre 12 e 28,
Capacity tinha o valor igual a 28; enquanto Count estava entre 29 e 44
Capacity tinha o valor igual a 44.

Adicionar novas strings na lista pode motivar o crescimento da propriedade Capacity, mas o simples
fato de a propriedade Count incrementar-se não alterará a propriedade Capacity. Não assinale um valor
menor para Capacity do que para Count, ou a lista pode ser cortada e Count vir a indicar que a lista
contém mais strings que ocorreria na realidade. Pior... se Capacity for marcado para um valor menor do
que Count, a memória usada para armazenar as strings que estão soltas na lista não é liberada.

ListaStr->Add(InputBox("Adiconando Strings na lista",


"Digite uma String", ""));

// exibimos a lista através de Text


ShowMessage(ListaStr->Text);
// exibimos o número de strings
ShowMessage(ListaStr->Count);
// exibimos capacity
ShowMessage(ListaStr->Capacity);

TStringList::Objects

Lista um grupo de objetos associados cada um com cada string na propriedade Strings.

__property System::TObject* Objects[int Index]=


{read=GetObject,
write=PutObject};

Objects associa um objeto com uma string existente assinalando a propriedade Objects com o mesmo
index (índice) da propriedade Strings da string na lista. Procure o objeto associado com a string pela
leitura da propriedade Objects por intermédio do Index da string. Index retorna a posição da string
associada com o objeto, onde 0 é a primeira string, 1 a segunda e assim por diante. Use o método
IndexOf para encontrar o Index da string.

Por exemplo, use Objects para associar objetos bitmap com a string na string list. A propriedade
Objects capacitará a aplicação a, rapidamente. localizar bitmaps para exibi-los junto à string num
listbox, ou para extraí-los quando a string associada é selecionada num controle.

TStrings::StringsAdapter

Implementa uma interface IStrings que capacita ao objeto TStrings comunicar-se usando OLE.

__property _di_IStringsAdapter StringsAdapter =


{read=FAdapter,
write=SetStringsAdapter};

Não chame a propriedade StringsAdapter. StringsAdapter é usado internamente por controles ActiveX
para efetivar comunicações através de interface OLE.
TStringList::Exchange

Troca a posição de duas strings na lista.

virtual void __fastcall Exchange(int Index1, int Index2);

Chame Exchange para readequar as strings na lista. As strings são especificadas nos parâmetros
Index1 e Index2. Index iniciam com zero, sendo que a primeira string possui o index 0, a segunda 1 e
assim por diante.

Se alguma das strings possuir um objeto associado, Exchange altera o index do objeto também.

Aviso: Não chame Exchange numa lista ordenada, exceto para trocar duas strings idênticas com
diferentes objetos associados. Exchange não verifica se a lista é ordenada, e pode bagunçar a ordem da
lista.

Vejamos umas linhas de código:

// adicionamos strings na lista


ListaStr->Add("FFF");
ListaStr->Add("EEE");
ListaStr->Add("DDD");
ListaStr->Add("CCC");
ListaStr->Add("BBB");
ListaStr->Add("AAA");

// exibimos a lista através de Text


ShowMessage(ListaStr->Text);

// trocamosas strings de lugar


for(int sobe=0, desce=(ListaStr->Count-1);
sobe<3, desce>2;
sobe++, desce--)
{
ListaStr->Exchange(sobe, desce);
}

// exibimos as novas posições


ShowMessage(ListaStr->Text);

Agora o exemplo do help:

void __fastcall TForm1::Button1Click(TObject *Sender)

{
ListBox1->Items->Exchange(1, 2);
}

TStrings::AddObject
Adiciona uma string na lista, associando um objeto com a string.

virtual int __fastcall AddObject(


const System::AnsiString S, System::
TObject* AObject);

Chame AddObject para adicionar uma string e associar um objeto com a string na lista. AddObject
retorna o index da nova string e do objeto.

Este código adiciona a string 'Orange' e um bitmap de uma laranja ao owner-draw list box:

void __fastcall TForm1::Button1Click(TObject *Sender)

{
TIcon* Icon;
Icon = new TIcon;
Icon->LoadFromFile("LARANJA.ICO");
ListBox1->Items->AddObject("Laranja", Icon);
}

TStrings::AddStrings

Adiciona um grupo de strings na lista.

virtual void __fastcall AddStrings(TStrings* Strings);

Chame AddStrings para adicionar strings de outro objeto TStrings na lista atual. Se tanto o objeto
TStrings de origem como o de destino suportam objetos associados com suas strings, eventuais
referências existentes aos objetos associados serão adicionadas também.

O exemplo a seguir adiciona o conteúdo de ListBox1 na TStringList ListaStr e depois adiciona o


conteúdo dessa lista em Memo1:

void __fastcall TForm1::Button5Click(TObject *Sender)


{
ListaStr->AddStrings(ListBox1->Items);
Memo1->Lines->AddStrings(ListaStr);
}

TStrings::Append

Adiciona a string S na lista.

void __fastcall Append(const System::AnsiString S);

Append é semelhante ao método Add, exceto pelo fato de que não retorna nenhum valor. Use Append
quando não houver necessidade de conhecer o index da string depois de ela ter sido adicionada, ou com
descendentes de TStrings para os quais o index retornado não contém valor significativo.
Por exemplo, O descendente de TStrings usado por um objeto memo usa um index para determinar
onde está inserida a string, mas a string inserida não será necessariamente a totalidade da string
inserida na lista. Parte do texto inserido talvez se torne parte de uma string previamente existente na
lista, e outra parte pode ser inserida na string subseqüente. O index retornado por Add não é
significativo nesses casos.

Use Append em preferência a Add como um parâmetro para uma função requerendo um TGetStrProc.

O exemplo a seguir usa um Button e um Memo no Form. Quando o usuário dá um clique no botão, é
criado um objeto TMemoryStream que receberá a string do objeto TStringList na memória. Depois
esse conteúdo será imprimido no memo. Observe bem o resultado. Depois refaça o teste sem usar
TMemoryStream para visualizar sutis diferenças nos resultados.

void __fastcall TForm1::Button1Click(TObject *Sender)


{
// criamos um TMemoryStream dinâmico
TMemoryStream* pms = new TMemoryStream();
// esvaziamos a lista
ListaStr->Clear();
// adicionamos strings na lista
ListaStr->Append("ABCDE\nFGHIJ\rKLMNO");
// escrevemos o conteúdo da lista
// no stream
ListaStr->SaveToStream(pms);
// retorna para o início do stream
pms->Position = 0;
// carrega o conteúdo do stream para
// o controle memo
Memo1->Lines->LoadFromStream(pms);
// libera a memória dinâmica
delete pms;
}

O belo exemplo abaixo, tirado do help do BCB, usa um Open dialog box, um memo, e um button no
form. Quando o usuário clica o button, o Open dialog box aparece. Quando o usuário seleciona
arquivos no dialog box e pressiona o botão OK, a primeira linha do arquivo é adicionada no memo:

void __fastcall TForm1::Button1Click(TObject *Sender)


{
FILE *stream;
char FirstLine[512];

OpenDialog1->Options.Clear();
OpenDialog1->Options << ofAllowMultiSelect << ofFileMustExist;
OpenDialog1->Filter = "Text files (*.txt)|*.txt|All files (*.*)|*.*";
OpenDialog1->FilterIndex = 2; // start the dialog showing all files
if (OpenDialog1->Execute())
{
for (int I = 0; I < OpenDialog1->Files->Count; I ++)
{
stream = fopen(OpenDialog1->Files->Strings[I].c_str(), "r");

if (stream)
{
// read the first line from the file
fgets(FirstLine, sizeof(FirstLine), stream);
Memo1->Lines->Append(FirstLine);
fclose(stream);
}
}
}
}

TStrings::Assign

Coloca strings na lista, e possivelmente objetos associados, desde um objeto de origem (Source).

virtual void __fastcall Assign(TPersistent* Source);

Use Assign para copiar o valor do objeto TStrings desde outro objeto. Qualquer objeto associado que
seja suportado, também será copiado desde o Source. Se Source não for do tipo TStrings, o Assign
herdado assinalará o valor da lista desde um objeto que suporte TStrings em seu método AssignTo.

Este exemplo consiste de um form com um memo e um list box. Quando o button recebe um clique, o
conteúdo do memo é preenchido com o conteúdo do listbox.

void __fastcall TForm1::Button1Click(TObject *Sender)

{
// coloca o conteúdo do list box no memo
Memo1->Lines->Assign(ListBox1->Items);
}

Vejamos outro exemplo:

TStrings* StringList = new TStringList();


try
{
StringList->Add("This example uses A string List. ");
StringList->Add("It is the easiest way to add strings");
StringList->Add("to a combobox's list of strings. ");
StringList->Add("Always remember TStrings is abstract,");
StringList->Add("So create a TStringList instead.");
ComboBox1->Width = 210;
ComboBox1->Items->Assign(StringList);
ComboBox1->ItemIndex = 0;
}
__finally
{
delete StringList;
}

O exemplo a seguir usa um tab control para exibir o conteúdo de vários arquivos. Para executar este
exemplo, coloque um tab control no form e adicione um controle memo ajustado dentro de sua client
area. Adicione OpenDialog e um button no form. Coloque o código a seguir no evento OnClick do
button:

void __fastcall TForm1::Button1Click(TObject *Sender)

{
OpenDialog1->Options<<ofAllowMultiSelect<<ofFileMustExist<<ofHideReadOnly;
if(OpenDialog1->Execute())
{
TabControl1->Tabs->Assign(OpenDialog1->Files);
Memo1->Lines->LoadFromFile(
TabControl1->Tabs->Strings[TabControl1->TabIndex]);
}
}

Coloque o código a seguir no evento OnChange do controle tab:

void __fastcall TForm1::TabControl1Change(TObject *Sender)

{
Memo1->Lines->LoadFromFile(
TabControl1->Tabs->Strings[TabControl1->TabIndex]);
}

TStrings::BeginUpdate

Capacita o objeto TStrings a verificar quando a lista de strings sofreu alguma alteração.

void __fastcall BeginUpdate(void);

BeginUpdate é chamado automaticamente por qualquer propriedade ou método que faz alguma
alteração na lista de strings. Uma vez que as mudanças estejam completadas, a propriedade ou método
chama EndUpdate. Chame BeginUpdate antes de modificar diretamente as strings na lista e
EndUpdate depois. Quando implementar propriedades ou métodos que alteram a lista em descendentes
de TStrings, chame BeginUpdate antes de a mudança ser feita e EndUpdate quando a mudança estiver
completada.

TStrings simplesmente fica de olho quando a lista de string é alterada. Alguns descendentes de
TStrings usam esta informação para executar certas ações , como por exemplo saber se um controle
será novamente pintado quando a atualização estiver completada.

BeginUpdate e EndUpdate devem ser usados em em conjunto com try...catch para assegurar que
EndUpdate será chamado se ocorrer alguma exceção. Um bloco de código que usa BeginUpdate e
EndUpdate geralmente possui a seguinte aparência:

void __fastcall TForm1::Button1Click(TObject *Sender)

{
ListBox1->Items->BeginUpdate();
try
{
ListBox1->Items->Clear();
ListBox1->Items->Add("Some foo");
ListBox1->Items->Add("Some data");
ListBox1->Items->EndUpdate();
}
catch(...)
{
//Executado no caso de uma exceção
ListBox1->Items->EndUpdate();
throw;
}
}

TStrings::EndUpdate

Capacita o objeto TStrings a ficar atento a fim de saber se a lista de strings terminou de ser atualizada.

void __fastcall EndUpdate(void);

EndUpdate é chamado automaticamente por qualquer propriedade ou método que alterar a lista de
strings.

O exemplo a seguir preenche a lista de um combo box com o grupo de fonts disponíveis no sistema
quando a lista do combo box é aberta. Cada vez que for aberta a lista do combo, o grupo de fonts é
atualizado. Desse modo o combo box sempre exibirá a lista atualizada, mesmo que alguma aplicação
adicione ou remova fontes.

void __fastcall Form1::ComboBox1DropDown(TObject *Sender)

{
ComboBox1->Items->BeginUpdate(); // impede nova pintura até acabar
ComboBox1->Items->Clear(); // esvazia os valores velhos da lista
ComboBox1->Sorted = true; // assegura lista ordenada
ComboBox1->Items = Screen->Fonts; // adiciona a lista de fonts
ComboBox1->Items->EndUpdate(); //reabilita a pintura
}

TStrings::Equals

Compara a lista de strings de uma lista com a lista de outro objeto TStrings e retorna true se as duas
listas forem iguais.

bool __fastcall Equals(TStrings* Strings);

Chame Equals para comparar as listas de dois objetos TStrings. Equals compara somente as strings,
não verificando qualquer referência a objetos associados. Equals retorna true se as listas de ambos os
objetos TStrings tiverem o mesmo número de strings e as strings em cada lista for idêntica à sua
equivalente da outra lista. Equals retorna false se a lista for diferente em length (tamanho), se elas
contiverem diferentes strings, ou se a ordem das strings nas duas listas forem diferentes.

ListaStr->Add("AAA");
ListaStr->Add("BBB");

ListBox1->Items->Add("BBB");
ListBox1->Items->Add("AAA");

if(ListaStr->Equals(ListBox1->Items)==true)
ShowMessage("IGUAIS");
else
ShowMessage("DIFERENTES");

TStrings::GetText

Aloca um text buffer e o preenche com o valor da propriedade Text.

virtual char * __fastcall GetText(void);

Chame GetText para obter um buffer de caracteres alocados dinamicamente contendo todas as strings
da lista. Strings individuais são separadas por carriage return e line feed. O caller é responsável por
liberar o valor retornado.

ListaStr->GetText();

TStrings::IndexOfObject

Retorna o index da primeira string na lista associada com um objeto determinado.

int __fastcall IndexOfObject(System::TObject* AObject);

Chame IndexOfObject para localizar a primeira string na lista associada com o objeto AObject.
Especifique o objeto que você quer localizar como um valor do parâmetro AObject. IndexOfObject
retorna o número da string e objeto na lista. Se o objeto não estiver associado com nenhuma string da
lista, IndexOfObject retorna -1.

O código a seguir verifica ser MyObject é o primeiro object em MyStringList.

void __fastcall TForm1::Button1Click(TObject *Sender)


{
if(ListBox1->Items->IndexOfObject(MyObject)==0)
ShowMessage("foo");
}

TStrings::InsertObject

Insere uma string na posição especificada dentro da lista, e a associa com um objeto.

void __fastcall InsertObject(int Index,


const System::AnsiString S,
System::TObject* AObject);

Use InsertObject para inserir a string S dentro da lista na posição identificada por Index, e associá-la
com o objeto AObject. Se Index for 0, a string é inserida no início da lista. Se o Index for 1, a string é
colocada na segunda posição, e assim por diante.
O código a seguir insere os nomes dos componentes de Form1 a partir da primeira linha de Memo1.

void __fastcall TForm1::Button1Click(TObject *Sender)


{
for (int i=0; i<Form1->ComponentCount-1; i++)
Memo1->Lines->InsertObject(0, Form1->Components[i]->Name, this);
}

Para o próximo exemplo, adicione um status bar, um list box, e quaisquer outros componentes no
Form1. Assinale a propriedade SimplePanel do status bar para True, usando o object inspector. O
código preenche o list box com os nomes de todos os componentes do form quando o form é criado.
Quando o usuário der um clique com o botão direito do mouse no nome de um objeto na lista, as
coordenadas do componente são exibidas no status bar.

void __fastcall TForm1::FormCreate(TObject *Sender)

{
for (int i = 0; i < ComponentCount; i++)
ListBox1->Items->InsertObject(0,
Components[i]->Name,
(TObject *)Components[i]);

}
//-------------------------------------------------------------------

void __fastcall TForm1::ListBox1MouseUp(TObject *Sender,


TMouseButton Button, TShiftState Shift, int X, int Y)

{
if (Button == mbRight)
{
TClass ClassRef;
int Index = ListBox1->ItemAtPos(Point(X,Y), true);
// somente componentes que são controles possuem uma posição
// assegure que o componente é um controle
for (ClassRef = ListBox1->Items->Objects[Index]->ClassType();
ClassRef != NULL;
ClassRef = ClassRef->ClassParent())
if (String(ClassRef->ClassName()) == "TControl")
{
TControl *TheObject = (TControl *)ListBox1->Items->Objects[Index];

StatusBar1->SimpleText =
TheObject->Name + " is at (" +
IntToStr(TheObject->Left) + ", " +
IntToStr(TheObject->Top) + ")";
break;
}
if (ClassRef == NULL) // se não for um controle
MessageBeep(0);
}
}
TStrings::LoadFromStream

Preenche a lista com linhas de texto lidas desde um stream.

virtual void __fastcall LoadFromStream(TStream* Stream);

Chame LoadFromStream para preencher a lista de strings do objeto TStrings desde um stream
especificado por Stream. O texto lido desde o stream é colocado dentro da string separado por
caracteres carriage return ou linefeed. Desse modo, LoadFromStream lê o valor da propriedade Text.

Se o Stream é um arquivo stream, LoadFromStream faz a mesma coisa que LoadFromFile, exceto
pelo fato de que a aplicação precisa criar e destruir o arquivo stream.

Este exemplo requer controles TListBox, TRichEdit e TButton no form. O list box deve conter um ou
mais items.

Quando o form se torna visível, um clique no button transferirá o conteúdo do list box para o controle
rich edit.

void __fastcall TForm1::Button1Click(TObject *Sender)

{
TMemoryStream* pms = new TMemoryStream();
// escreve o conteúdo do list box no stream
ListBox1->Items->SaveToStream(pms);
// reseta para o início do stream
pms->Position = 0;
// carrega o stream para dentro do rich edit
RichEdit1->Lines->LoadFromStream(pms);

delete pms;
}

TStrings::Move

Muda a posição de uma string na lista.

virtual void __fastcall Move(int CurIndex, int NewIndex);

Use Move para mover, na lista, uma string da posição CurIndex para NewIndex. As posições são
especificadas com base no index que começa com 0. Por exemplo, a linha de código a seguir move a
primeira posição para a última posição.

MyStringsObject->Move(0, MyStringsObject->Count - 1);

Se a string possuir algum objeto associado, tal objeto ficará associado com a string em sua nova
posição.

Este exemplo usa um list box e um button no form. O list box receberá algumas strings na criação do
form. Quando o usuário der um clique no button, o quinto item no list box é movido para o topo do list
box:
void __fastcall TForm1::FormCreate(TObject *Sender)
{
for (int i = 0; i < 10; i++)
ListBox1->Items->Append("New String" + AnsiString(i));
}
//---------------------------------------------------------------------------

void __fastcall TForm1::Button1Click(TObject *Sender)


{
ListBox1->Items->Move(4,0);
}

TStrings::SaveToStream

Escreve o valor da propriedade Text no objeto stream.

virtual void __fastcall SaveToStream(TStream* Stream);

Chame SaveToStream para salvar as strings na lista para o stream especificado pelo parâmetro Stream.
SaveToStream escreve as strings delimitadas por carriage return, line feed pairs. Se o stream for um
arquivo stream, SaveToStream comporta-se como SaveToFile, exceto pelo fato de precisar criar e
destruir o arquivo stream.

Este exemplo usa um memo e um button no form. Quando o button recebe um clique, o memo é
preenchido com o conteúdo do arquivo e salvo num novo arquivo.

void __fastcall TForm1::Button1Click(TObject *Sender)

{
Memo1->Lines->LoadFromStream(
&TFileStream("Unit1.cpp",
fmOpenRead));
Memo1->Lines->SaveToStream(
&TFileStream("Unit2.cpp",
fmOpenWrite));
}

TStrings::SetText

Assinala a propriedade Text.

virtual void __fastcall SetText(char * Text);

Chame SetText para substituir a lista com a string especificada pelo parâmetro Text. SetText adiciona
strings de uma só vez na lista, usando caracteres carriage returns ou linefeed no texto como
delimitadores indicando que será adicionada uma nova string.

ListaStr->Add("AAA");
ListaStr->Add("BBB");
// substituímos todo o conteúdo da lista
ListaStr->SetText("CCC\nDDD\rEEE");
// mostramos o conteúdo atual
ShowMessage(ListaStr->Text);

Eventos em TStringList

TStringList::OnChange

Ocorre imediatamente após a lista de strings sofrer alteração.

__property TNotifyEvent OnChange =


{read =FOnChange, write=FOnChange};

Escreva um evento handler OnChange para responder a mudanças na lista de strings. Por exemplo, se
a string list estiver associada com um controle, o evento OnChange pode detectar a necessidade de
realizar nova pintara no controle por si mesmo toda vez que a lista sofrer alguma alteração.

Todas as vezes que strings são adicionadas, deletadas, modificadas, ou movidas na lista, ocorrem os
seguintes eventos:

1 Primeiro, um evento OnChanging ocorre antes da mudança;


2 As strings são adicionadas, deletadas, movidas ou modificadas;
3 Finalmente, ocorre o evento OnChange.

TStringList::OnChanging

Ocorre imediatamente antes de as strings sofrerem alteração.

__property TNotifyEvent OnChanging =


{read=FOnChanging, write=FOnChanging};

Escreva um evento OnChanging para preparar mudanças na lista de strings. Por exemplo, se a string
list estiver associada com um controle, o evento OnChanging deve informar ao controle para
desabilitar a repintura até que ocorra o evento OnChange quando a lista terminar sua alteração.

****************************************

Interagindo nossos estudos com compontes visuais.

A partir de agora estaremos provocando você com algumas questões puramente lógicas. Nós
colocaremos o início do assunto, cabendo a você o raciocínio e o aprofundamento do estudo das
questões levantadas.
Pensemos. Em diversas ocasiões nos nossos exemplos sobre TStringList, foram usados componentes
visuais tais como os list box, combo box, rich edit ou memo. Que relacionamento existe entre tais
objetos?

Vejamos:

AnsiString Str = "Aprofundando um pouco o estudo das listas";


ListaStr->Add(Str);
Memo1->Lines->Add(Str);
ComboBox1->Items->Add(Str);

Sleep(3000);

ListaStr->Clear();
Memo1->Lines->Clear();
ComboBox1->Items->Clear();

Sleep(3000);

ComboBox1->Items->Add("Ora, ora, ora... quem diria!!!");


ListaStr->Assign(ComboBox1->Items);
Memo1->Lines->Assign(ListaStr);

E assim por diante!

Percebeu que uma boa compreensão do objeto TStringList ajuda, muito, a compreender várias
capacidades de certos componentes, em face da estreita relação existente entre eles quando usada
determinada propriedade ou método? Em componentes como memo e rich edit, temos a propriedade
Lines que suportam praticamente todos os métodos e propriedades de TStringList e nos componentes
como os list box e combo box temos a propriedade Items. Pesquise um mais pouco esse assunto!

Assim nós concluímos nossos estudos sobre as propriedades, métodos e eventos que interessam
diretamente ao objeto TStringList. Existem outros métodos (derivados de TPersistent e de TObject)
que interessam a esse objeto de uma forma menos direta para o programador.

Estaremos, noutro artigo, expondo esses métodos, porém não como parte integrante do estudo acima, e
sim a título de conhecimento geral, uma vez que tais métodos interessam a uma gama muito grande de
objetos tais como TRegistry, TIniFiles, TStream etc (na verdade, trata-se mais de uma tradução do
help do que de uma edição, embora...).

Lembra-se do dicionário jurídico? Acha que se encontra em condições de melhorar (e muito!) o código
proposto no início deste artigo usando apenas dois componentes e os recursos de TstringList numa
única lista? Que tal tentar com um Edit e um RichEdit? Tente desenvolver um código que vá
autocompletando (no edit e no rich edit) as strings à medida que estas vão sendo digitadas no edit.
Desenvolva uma técnica para colocar, numa só string, o termo e seu significado.

Métodos
Derivado de TPersistent

TPersistent::GetNamePath

Retorna o nome do objeto, do modo que ele aparece no Object Inspector.

DYNAMIC System::AnsiString __fastcall GetNamePath();

GetNamePath é para uso interno somente. Ele determina o texto que o Object Inspector exibirá como
nome do objeto que estiver sendo editado. GetNamePath é introduzido em TPersistent assim como em
seus descendentes como uma coleção que pode aparecer no Object Inspector. Não chame
GetNamePath diretamente.

Para componentes, GetNamePath retorna o nome do componente. Para objetos TCollectionItem ele
retorna o nome do componente hospedeiro, o nome da propriedade, e o index dentro da coleção dentro
de colchetes [].

****************************************

TList
Programação avançada

•Descrição:
A classe TList representa um vetor dinâmico de ponteiros. Esta classe contém a maioria dos métodos necessários
para a manipulação de cadeias de ponteiros, como adicionar, excluir, localizar, ordenar. TList ‘sobrecarrega’ o
operador de matriz [] para fornecer acessos a seus elementos; portanto, no sentido mais purista, apesar do nome
TList sugerir uma lista, TList é, na realidade, um vetor dinâmico. Entretanto, para não conflitar com o nome
TList, utilizarei o termo ‘lista’ para designar o arranjo de elementos de TList. Como os elementos da lista são
acessados através da propriedade Items[ ], utilizarei o termo ‘item’ para designar um elemento da lista.
Por esperar ponteiros void, TList pode ser usada para armazenar referências para quaisquer tipos de dados
(estruturas, objetos, strings...). Isso agiliza bastante a manipulação de grandes blocos de informação, haja vista
que apenas as referências aos blocos são movimentas.
Como a ordenação é uma das operações mais comuns, TList implementa seu próprio método de ordenação,
utilizando-se do algoritmo QuickSort. Infelizmente este é o único algoritmo implementado para a classe, mas
outros algoritmos podem ser facilmente implementados, utilizando-se TList como classe base para outras classes
mais elaboradas.
A classe TList esta presente desde a versão 1.0 do Borland C++ Builder, tanto na VCL como na CLX.
•Protótipo
O protótipo de TList fica em <classes.hpp>

•Propriedades
Abaixo, segue uma breve descrição de cada propriedade definida em TList:

ØCapacity
__property int Capacity = {read=FCapacity, write=SetCapacity, nodefault};

Utilize Capacity para predeterminar o número de itens que se deseja armazenar na lista. Aumentar o
valor de Capacity gera uma prévia alocação de memória, suficiente para armazenar um número de itens
igual a Capacity. Caso não houver memória suficiente (not enough memory), uma exceção
EOutOfMemory é gerada. A propriedade Capacity não se faz (em situações triviais) necessária, pois
chamadas ao método Add, por exemplo, incrementa Capacity automaticamente. Porém, ajustar o valor
de Capacity antes de um loop, otimiza a operação, pois o método Add não precisará recorrer a alocação
de memória. Também não se corre o risco de ficar sem memória no meio da operação. TList gerencia
automaticamente Capacity (sempre para mais, nunca para menos).
Atenção! Capacity não representa o número de itens realmente armazenados na lista. Para verificar o real
número de itens, utilize a propriedade Count. Capacity é sempre maior ou igual a Count.

ØCount
__property int Count = {read=FCount, write=SetCount, nodefault};

Representa o número de itens armazenados na lista.


Aumentar o valor de Count adiciona o necessário número de itens no final da lista, todos estes,
ponteiros NULL. Diminuir, remove o necessário número de itens do final da lista. Count representa o
número de itens da lista, porém, não necessariamente o número de objetos referenciados, haja vista que
os ponteiros podem estar apontando para NULL. Para remover todos os itens cujo ponteiro aponta para
NULL utilize o método Pack.

ØItems
__property void * Items[int Index] = {read=Get, write=Put};

Esta propriedade representa, na forma de um array, todos os itens da lista.


Utilize Items para obter um determinado ponteiro da lista através de seu índice. O parâmetro Index determina o
índice do item a obter, onde 0 é o primeiro item da lista e Count-1 o último. Utilize em conjunto Items e Count
para interagir com todos os itens da lista. Caso houver ponteiros NULL na lista, eles também são acessados. Para
remover todos os itens cujo ponteiro é NULL utilize o método Pack. Chamar Items[n] com um índice ‘n’ além
dos limites do array gera uma exceção do tipo ElistError (List index out of bounds).
ØList
typedef void **PPointerList;
__property PPointerList List = {read=FList};

Utilize List para obter acesso direto (e somente leitura) aos ponteiros armazenados em TList.

•Métodos
Abaixo, segue uma breve descrição sobre os principais métodos definidos em TList:

ØTList - construtora
inline __fastcall TList(void) : System::TObject() { }

Não utilize a construtora diretamente. Para instanciar objetos TList utilize o operador new.

Ø~TList - destrutora
__fastcall virtual ~TList(void);

Não utilize a destrutora diretamente. Para liberar a memória alocada por objetos TList utilize o operador delete.
É importante ressaltar que a destrutora de TList não destroi os objetos referenciados pelos itens da lista.
Cabe ao programador efetuar a devida ‘coleta de lixo’ antes de destruir o objeto TList.

ØAdd
int __fastcall Add(void * Item);

Utilize Add para inserir um novo item no final da lista.


Add retorna o índice do novo item, onde o primeiro item é o número 0, o segundo 1, e assim por diante.
Add incrementa Count e, se necessário, aloca memória, incrementando Capacity.
Add insere um novo item no final da lista, mesmo se houver ponteiros apontando para NULL na lista.
Para inserir um item em uma determinada posição, utilize o método Insert.

ØDelete
void __fastcall Delete(int Index);

Utilize Delete para remover um item da lista através de seu índice. O parâmetro Index determina o
índice do item a remover, onde 0 é o primeiro item da lista e Count-1 o último. Chamar Delete reduz
em uma unidade todos os índices superiores ao removido, e reduz também a propriedade Count. Delete,
porém, não reduz a propriedade Capacity. Para liberar a memória alocada por um item deletado ajuste a
propriedade Capacity para o valor desejado. Delete não destrói o objeto para o qual o ponteiro deletado
aponta. Apenas a referência é eliminada.
ØError
virtual void __fastcall Error(const AnsiString Msg, int Data) {
Error(__classid(TList), Msg, Data)};

Utilize Error para lançar uma exceção quando um erro ocorrer enquanto se trabalha com um objeto TList. A
exceção gerada será do tipo EListError. Error recebe como parâmetro uma string e um inteiro, que podem ser
utilizados para indicar a causa do erro.
Na verdade, Error apenas simplifica a seguinte operação:
throw EListError(MsgErro, ValorErro);

ØExchange
void __fastcall Exchange(int Index1, int Index2);

Utilize Exchange para trocar os índices dos itens especificados pelos parâmetros Index1 e Index2. O primeiro
item é o número 0, o segundo 1, e assim por diante.
Chamar Exchange com um índice além dos limites do array gera uma exceção do tipo ElistError.

ØExpand
TList* __fastcall Expand(void);

Utilize Expand para criar mais espaço para adicionar novos itens na lista. Expand não faz nada se Capacity for
maior que Count (ou seja, a capacidade da lista ainda não foi totalmente utilizada). Se Count=Capacty, então
Expand altera o valor de Capacity seguindo as seguintes regras:
-Se o valor de Capacity é maior do que 8, seu valor e incrementado em 16 unidades;
-Se o valor de Capacity é maior do que 4 e menor que 9, seu valor é incrementado em 8 unidades;
-Se o valor de Capacity for menor que 4, seu valor é incrementado em 4 unidades.
Expand retorna um ponteiro para a própria lista.

ØExtract
void * __fastcall Extract(void * Item);

Utilize Extract para remover um item da lista. O parâmetro Item é o ponteiro a ser removido. Chamar Extract
reduz em uma unidade todos os índices superiores ao removido, e reduz também a propriedade Count. Extract,
porém, não reduz a propriedade Capacity. Para liberar a memória alocada por um item deletado ajuste a
propriedade Capacity para o valor desejado. Extract não destroi o objeto para o qual o ponteiro deletado aponta.
Apenas a referência é eliminada.
Extract retorna o item extraído da lista.
Se mais de uma cópia do ponteiro estive presente na lista, apenas a primeira cópia será removida.

ØFirst
void * __fastcall First(void);

O método First retorna o primeiro item da lista. First equivale exatamente a uma chamada Items[0].
Portanto, chamar Fisrt em uma lista vazia gera uma exceção do tipo ElistError (List index out of
bounds).

ØIndexOf
int __fastcall IndexOf(void * Item);

Utilize IndexOf para obter o índice de um ponteiro na lista. O primeiro item é o número 0, o segundo 1,
e assim por diante. Se o parâmetro passado não for encontrado na lista, IndexOf retornará –1. Se o
mesmo ponteiro estiver mais de uma vez na lista, IndexOf retorna o índice de menor valor no qual o
mesmo se encontra.

ØInsert
void __fastcall Insert(int Index, void * Item);

Utilize Insert para inserir um item em uma determinada posição na lista. O parâmetro Index representa a posição
a inserir, onde 0 é o primeiro item, o segundo 1, e assim por diante. O parâmetro Item é o ponteiro a ser inserido.
Ao inserir, todo item cujo índice for superior ou igual ao do item inserido, terá seu valor incrementado em uma
unidade. A propriedade Count também terá seu valor incrementado, e conseqüentemente, se necessário, a
propriedade Capacity também será aumentada. Insert insere o item como descrito, mesmo se o item cujo índice
estiver referenciando um ponterio NULL.
Chamar Insert com um índice além dos limites do array gera uma exceção do tipo ElistError.
Para comodidade, utilize o método Add, se a intenção for inserir o item no final da lista.

ØLast
void * __fastcall Last(void);

O método Last retorna o último item da lista. Last equivale exatamente a uma chamada Items[Count-
1]. Portanto, chamar Last em uma lista vazia gera uma exceção do tipo ElistError (List index out of
bounds).

ØMove
void __fastcall Move(int CurIndex, int NewIndex);

Utilize Move para movimentar o item que ocupa a posição CurIndex para a posição NewIndex. Todo o
rearranjo de índices necessário será efetuado automaticamente. CurIndex e NewIndex são índices
baseados em zero; o primeiro item é o número 0, o segundo 1, e assim por diante. Chamar Move com
um índice além dos limites do array gera um exceção do tipo ElistError.

ØPack
void __fastcall Pack(void);

Utilize Pack para remover da lista todos os itens cujo ponteiro aponta para NULL, atualizando a
propriedade Count. Pack, porém, não altera a propriedade Capacity. Para realmente liberar toda a
memória alocada pelos itens removidos por Pack, ajuste, na seqüência, Capacity para o novo valor de
Count.

ØRemove
int __fastcall Remove(void * Item);

Utilize Remove para remover um item da lista. O parâmetro Item é o ponteiro que se deseja remover. Chamar
Remove reduz em uma unidade todos os índices superiores ao removido, e reduz também a propriedade Count.
Remove, porém, não reduz a propriedade Capacity. Para liberar a memória alocada por um item removido, ajuste
a propriedade Capacity para o valor desejado. Remove não libera o objeto para o qual o ponteiro removido
aponta. Apenas a referência é eliminada.
Remove retorna o índice que o item removido ocupava na lista.
Se mais de uma cópia do ponteiro estiver presente na lista, apenas a primeira cópia será removida.

ØSort
typedef int __fastcall (*TListSortCompare)(void * Item1, void * Item2);
void __fastcall Sort(TListSortCompare Compare);

Utilize Sort para ordenar os itens da lista. O parâmetro Compare é um ponteiro para uma função do tipo
TListSortCompare que Sort utilizará para comparar os itens. Está função deve receber como parâmetros dois
ponteiros void, e retornar um int. O retorno deve seguir as seguintes regras:
-Um valor negativo (< 0) deve ser retornado quando Item1 for considerado menor do que Item2;
-Um valor zero (0) deve ser retornado quando Item1 for considerado igual a Item2;
-Um valor positivo (> 0) deve ser retornado quando Item1 for considerado maior que Item2.
Sort utiliza o algoritmo QuickSort para efetuar a ordenação.

•Considerações Finais:

Como visto acima, a classe TList tem uma utilização muito simples, tendo em vista que, para um bom
aproveitamento da linguagem C++, o domínio dos ponteiros e fundamental. A classe TList representa
uma saída rápida e segura quando se faz necessário manipular listas de objetos, principalmente se estes
objetos foram implementados utilizando polimorfismo, já que nada impede que um objeto TList
armazene diferentes tipos de objetos.
Estou anexando a este texto alguns exemplos de utilização de TList (teoria, sem prática, na minha opinião, não
serve para nada J ). Boa sorte a todos!!!

Let’s Rock!!!
Ivan T.G. Moraes - ivantgmoraes@yahoo.com.br

O artigo acima, bem como os códigos fontes


que o acompanham, foi-nos presenteado por:
IVAN TARCÍZIO GOMES MORAES
PROGRAMADOR C/C++, DELPHI
http://geocities.yahoo.com.br/ivantgmoraes/
ivantgmoraes@yahoo.com.br

Métodos derivados de TObject


******************************

Visual Component Library - primeira parte

Embora o presente artigo seja, em princípio, apresentado como uma extensão do estudo sobre
TStringList, com a presente seção estaremos, na verdade, iniciando um estudo que deverá se estender
por várias edições de nossa Revista. A idéia é a de irmos caminhando pelos trilhos do C++Builder,
desde o seu mais remoto objeto, até os componentes mais elaborados, nativos ou desenvolvidos por
terceiros. Começaremos pelo conhecidíssimo, mas obscuro TObject. É de bom alvitre esclarecer que
boa partes desses primeiros artigos devem parecer um tanto maçantes, pois estará muito baseado em
meras traduções. Todavia, temos por certo que, os muitos conceitos introduzidos ajudarão a
compreender melhor os mecanismos de funcionamento do BCB. Além do que, sempre que possível,
estaremos disponibilizando algum código para ilustrar o assunto abordado!

Todos os componentes do C++Builder são parte de uma hierarquia de classes chamada Visual
Component Library (VCL). O esquema a seguir mostra o relacionamento das classes selecionadas
que constituem a VCL.

A classe TComponent é o ancestral comum de todos os componentes na VCL. TComponent provê as


mínimas propriedades e eventos necessários para que um componente seja usado no C++Builder. Em
outros ramos da livraria encontramos outras capacidades mais especializadas.

TObject
Todos os objetos da VCL descendem de TObject, uma classe abstrata cujos métodos definem
comportamentos fundamentais como construção, destruição e tratamento de mensagens. A maior parte
das poderosas capacidades dos objetos VCL é estabelecida pelos métodos que TObject introduz.
TObject encapsula os comportamentos fundamentais comuns a todos os objetos na VCL, apresentando
métodos que possibilitam:

A capacidade de responder quando objetos são criados ou destruídos;


Tipo, classe e informação de instância num objeto, e informação em tempo de execução (RTTI)
acerca dessas propriedades públicas;
Suporte para tratamento de mensagens.

Use TObject como uma classe básica imediata quando da declaração de objetos simples que não são
componentes. Quando da declaração de um tipo de objeto novo, se nenhum tipo de ancestral for
especificado, o BCB usará TObject automaticamente como o antepassado.

Muito das capacidades poderosas dos objetos BCB são estabelecidas pelos métodos introduzidos por
TObject. Muitos destes métodos são usados interiormente pelo ambiente Borland C++Builder, não
sendo destinados à chamada direta dos usuários.

TObject é o ancestral imediato de muitas classes. Classes que estão contidas dentro desse ramo
possuem uma característica comum importante: elas são transitórias. O que isto quer dizer? Significa
que essas classes não têm um método para salvar o estado que elas se encontram antes de destruição,
ou seja, elas não são persistentes.

Um dos principais grupos está no ramo da classe Exception. Essa classe fornece um grande grupo de
construção de classes de exceções para tratar automaticamente erros como divisão por zero, erros em
arquivos de entrada e saída I/O, e muitas outras exceções.

Outro tipo de grupo no ramo TObject são as classes que encapsulam dados estruturais, tais como:

TBits, uma classe que armazena um “array” de valores Booleanos.

TList , vinculado a classes de listas.

TStack , uma classe que mantém um last-in first-out array de ponteiros

TQueue, uma classe que mantém um first-in first-out array de ponteiros.

Você também pode encontrar encapsulamento de objetos externos como TPrinter, o qual encapsula as
janelas de interface para a impressora, e TRegistry, um invólucro de baixo nível para o System Registry
e funções que operam o registro.

TStream é um bom exemplo de outro tipo de classe deste ramo. TStream é a classe base para objetos
stream que podem ler ou escrever vários tipos de armazenamento, como arquivos de disco, memória
dinâmica, e assim por diante.
Como você pode ver, esse ramo inclui diversos e diferentes tipos de classes que são muito úteis quando
você está desenvolvendo aplicações.

Conheceremos agora todos os métodos implementados em TObject, uma vez que eles interessam,
direta ou indiretamente, a todos os descendentes desta classe.

****************************************
TObject::TObject

Constrói um objeto e o inicializa antes de seu primeiro uso.

__fastcall TObject();

Não crie instâncias de TObject. Em vez disso, construa descendentes de TObject usando a palavra-
chave new passando algum argumento para o construtor do descendente. Objetos descendentes
definem um construtor que está adaptado para criar uma espécie particular de objeto e inicializar seus
dados quando necessário.

Nota: Se uma exceção escapar desde um construtor, o objeto destruidor é chamado para esvaziar a instância falida.

TObject::~TObject

Libera uma instância de objeto.

virtual __fastcall ~TObject(){}

Não chame ~TObject diretamente. Em vez disso, use a palavra-chave delete para destruir objetos VCL.
~TObject não faz nada especial. Os objetos descendentes geralmente definem um destruidor que é
personalizado para destruir essa espécie de particular objeto.

Nota: Se uma exceção escapar desde o construtor, o destruidor é chamado para destruir a
instância do objeto parcialmente construída e que falhou ao ser inicializada completamente. Então
destruidores devem verificar quais os recursos alocados, tal como handles, onde estão alocados antes de
tentar liberá-los.

Aviso: Nunca destrua um componente desde um de seus eventos handlers ou desde um evento
handler de algum componente que lhe pertença. Por exemplo, não destrua um button em seu evento
handler OnClick bem como não destrua um form através do evento OnClick de algum botão que lhe
pertença.

Para destruir um form, chame seu método Release, que destrói o form e libera a memória alocada para
todos os seus eventos handlers e de seus componentes.

TCustomForm::Release

Release destrói o form e libera toda memória associada.

void __fastcall Release(void);

Release não destrói o form antes de todos os eventos handlers do form e dos
eventos handlers dos componentes do form terem terminado a execução. Todos
os eventos handlers do form deveriam usar Release.
O exemplo a seguir exibe uma caixa de mensagens sobre o form estar indo
embora (sendo destruído), chama Release e fecha a aplicação.

void __fastcall TForm1::ButtonClick(TObject *Sender)

{
Application->MessageBox(
"Este form está se indo para sempre",
"Notificação Release",
MB_OK);
Release();
Application->Terminate();
}

TObject::AfterConstruction

Responde após o o último construtor ser executado.

virtual void __fastcall AfterConstruction();

AfterConstruction é chamado automaticamente após o construtor do último objeto ter sido executado.
Por exemplo, TCustomForm ativa AfterConstruction para gerar um evento OnCreate. Não o chame
explicitamente em suas aplicações.

TObject::BeforeDestruction

Responde antes de o primeiro destruidor executar.

virtual void __fastcall BeforeDestruction();

BeforeDestruction é chamado automaticamente imediatamente antes de o primeiro destruidor do


objeto executar. Não o chame explicitamente em suas aplicações. Esse método é ativado na criação de
uma classe que recebe algumas ações antes de o objeto ser destruído. Por exemplo, TCustomForm
ativa BeforeDestruction para gerar um evento OnDestroy.

TObject::ClassInfo

Retorna um ponteiro para a tabela run-time type information (RTTI) do tipo do objeto.

typedef TMetaClass* TClass;


static void * __fastcall ClassInfo(TClass cls);
void * __fastcall ClassInfo(){return ClassInfo(ClassType()); }

Use ClassInfo para acessar a tabela RTTI que contém informações sobre o tipo do objeto, seu tipo
ancestral, e todas as suas propriedades published. Se chamado sem argumentos, ClassInfo devolve a
tabela RTTI para uma especifica instância de objeto. Chame o método static TObject::ClassInfo,
passando a informação metaclass, para obter a tabela RTTI para uma classe de objeto quando você não
tem uma instância.
RTTI é usado internamente no ambiente C++Builder. ClassInfo raramente é chamado diretamente
numa aplicação. TObject inclui outros métodos voltados a fornecer meios mais fáceis de obter
informações RTTI.

if(Button1->ClassInfo())
Label1->Caption = "true";

TObject::ClassName

Retorna uma string indicando o tipo da instância objeto (oposto ao tipo da variável passada como um
argumento).

typedef TMetaClass* TClass;


static ShortString __fastcall ClassName(TClass cls);
ShortString __fastcall ClassName(){ return ClassName(ClassType());}

Use ClassName para encontrar o nome (por exemplo, “TButton“) de um tipo de objeto. Isto é
proveitoso para diferenciar instâncias de objetos que estão designadas para uma variável que possuem
mesmo tipo de uma classe ancestral.

Chame ClassName sem argumentos para determinar o nome de uma instância objeto. Neste caso,
ClassName retorna uma string indicando o tipo atual do objeto. Chame ClassName passando num
valor TClass para obter a class name de sua metaclass information.

Este exemplo usa um button, um label, um check box, e um edit box no form. Quando o usuário clicar
um dos controles, o nome da classe do controle aparece no edit box.

void __fastcall TForm1::Button1Click(TObject *Sender)


{
Edit1->Text = String(Button1->ClassName());
}

void __fastcall TForm1::CheckBox1Click(TObject *Sender)


{
Edit1->Text = String(CheckBox1->ClassName());
}

void __fastcall TForm1::Label1Click(TObject *Sender)


{
Edit1->Text = String(Label1->ClassName());
}

TObject::ClassNameIs

Determina se um objeto é do tipo especificado.


typedef TMetaClass* TClass;
static bool __fastcall ClassNameIs(TClass cls, const AnsiString string);
bool __fastcall ClassNameIs(const AnsiString string)
{ return ClassNameIs(ClassType(), string); }

Use ClassNameIs quando escrever código condicional baseado num tipo de objeto para perguntar
através de módulos ou DLLs.

Chame o método static ClassNameIs para verificar se uma variável TClass representa uma classe em
particular (por exemplo, “TButton”). Chame ClassNameIs com uma única AnsiString no parâmetro
para determinar se uma instância em particular do objeto é do tipo especificado. Para determinar se um
objeto é de um tipo especificado ou de um de seus descendentes, use método InheritsFrom em vez de
ClassNameIs.

ClassNameIs retorna true se a string passada no parâmetro string coincidir com o nome da classe. De
outro modo, retorna false.

Vejamos um exemplo:

// loop para contar componentes do form


for (int i=0; i<ControlCount; i++)
{
// Se o componente for um Edit
if ( Controls[i]->ClassNameIs("TEdit") )
{
// exibimos o conteúdo de sua propriedade Text
ShowMessage(dynamic_cast<TEdit*>(Controls[i])->Text);
}
}

TObject::ClassParent

Retorna o tipo do ancestral imediato de uma classe.

typedef TMetaClass* TClass;


static TClass __fastcall ClassParent(TClass cls);
TClass __fastcall ClassParent() { return ClassParent(ClassType());}

Use o método ClassParent para buscar a origem de um objeto ou variável TClass. ClassParent é usado
internamente para implementar o método InheritsFrom. A versão static pode ser usada para
determinar o tipo de qualquer classe referencia. ClassParent pode ser usado para determinar o ancestral
de uma particular instância de objeto.

ClassParent retorna NULL para o tipo TObject porque TObject não é derivado de uma classe
ancestral.

Nota: Aplicações raramente devem chamar ClassParent diretamente. Em vez disso, use o
método InheritsFrom ou um dynamic cast.
Este código usa um button e um list box no form. Quando o usuário clicar o button, o nome da classe
button's e os nomes de suas classes parent são adicionadas no list box.

void __fastcall TForm1::Button1Click(TObject *Sender)

{
TClass ClassRef;

ListBox1->Clear();
ClassRef = Sender->ClassType();

while(ClassRef != NULL)
{
ListBox1->Items->Add(ClassRef->ClassName());
ClassRef = ClassRef->ClassParent();
}
}

Depois que o botão receber um clique, o list box conterá as seguintes strings:

TButton
TButtonControl
TWinControl
TControl
TComponent
TPersistent
TObject

TObject::ClassType

Retorna um ponteiro para o run-time type information de um object instance (RTTI).

typedef TMetaClass* TClass;


TClass __fastcall ClassType();

ClassType determina o tipo de um objeto dinamicamente. Use ClassType para obter informações
metaclass para uma instância de objeto. Para obter informações metaclass para uma class name, use a
rotina __classid.

Este exemplo usa um button e um label no form. Quando o usuário clicar o button, o tipo do
componente button (TButton) aparece no caption do label.

void __fastcall TForm1::Button1Click(TObject *Sender)


{
TClass ButtonClassType;
ButtonClassType = Button1->ClassType();
Label1->Caption = ButtonClassType->ClassName();
}
TObject::CleanupInstance

Executa a finalização de long strings, Variants, e interface variável dentro de uma classe.

void __fastcall CleanupInstance();

Não chame CleanupInstance diretamente. CleanupInstance é chamado automaticamente quando uma


instância de objeto é destruída através da palavra-chave delete.

CleanupInstance libera todas as long strings e Variants. Ele coloca long strings para empty (vazia) e
Variants para Unassigned.

TObject::DefaultHandler

Fornece a interface para um método que processa message records.

virtual void __fastcall DefaultHandler(void* Message);

DefaultHandler é chamado por Dispatch quando ele não encontra um método para uma mensagem em
particular. DefaultHandler fornece tratamento para todas as mensagens para as quais um objeto não
possui tratamentos específicos. Classes descendentes que processam mensagens ativam
DefaultHandler de acordo com o tipo de mensagens de seus handle. Por exemplo, TWinControl ativa
DefaultHandler para chamar DefWindowProc.

TObject::Dispatch

Chama métodos de tratamento de mensagens para o objeto, baseado no conteúdo do parâmetro


Message.

virtual void __fastcall Dispatch(void *Message);

Chame Dispatch para passar mensagens automaticamente para o apropriado tratamento de mensagens
(message handler).

Dispatch determina se uma mensagem está na lista de tratamento de mensagens declarada para um
objeto. Se o objeto não possui tratamento, Dispatch então examina a lista de tratamento de mensagens
da classe ancestral, e continua checando ancestrais até encontrar um tratamento específico ou,
esgotando os ancestrais, chama DefaultHandler.

O parâmetro Message é void *. A única suposição que Dispatch faz sobre o dado numa mensagem é
que os dois primeiros bytes contêm uma mensagem ID, isto é, um inteiro que determina qual
tratamento Dispatch chama para tratar a mensagem. Embora qualquer espécie de dados possa ser
passada para Dispatch, muitos descendentes TObject esperam uma mensagem record como um
TMessage ou um específico tipo message-record.
TForm::Dispatch(&msg);

TObject::FieldAddress

Retorna o endereço de um campo objeto published.

void * __fastcall FieldAddress(const ShortString &Name);

FieldAddress é usado internamente pelo componente streaming system para acessar um campo
específico published de um objeto. FieldAddress retorna um ponteiro para o campo se ele existir. Se o
objeto não possui um campo published com tal nome, FieldAddress retorna NULL.

Programas devem acessar e manipular campos através de propriedades ao invés de através de


FieldAddress.

TObject::Free

Destrói um objeto e libera a memória associada, se necessário.

__fastcall Free();

Não chame o método Free para um objeto. Use a palavra-chave delete em seu lugar, que por sua vez
invocará Free para destruir tal objeto. Free automaticamente chama o destruidor se a instância do
objeto não for NULL.

#include "Form2.h"
Application->CreateForm(__classid(TForm2), &Form2); // cria
Form2->ShowModal(); // exibe
delete Form2; // libera a memória

TObject::FreeInstance

Libera a memória alocada por uma prévia chamada ao método NewInstance.

virtual void __fastcall FreeInstance();

Todos os destruidores chamam FreeInstance automaticamente para liberar a memória que foi alocada
pela ativação de NewInstance.

Não chame FreeInstance diretamente. FreeInstance deve ser ativada se NewInstance foi ativada para
para alterar o modo que a instância de dados do objeto foi alocada.

Como NewInstance, FreeInstance usará o valor retornado de InstanceSize para desalocar o objeto da
memória.

TObject::GetInterface

Retorna a interface identificada por um identificador fornecido.

bool __fastcall GetInterface(const TGUID &IID, void *Obj);

GetInterface é usado internamente para retornar qualquer interface implementada por um objeto.

GetInterface retorna, através do parâmetro Obj, um ponteiro para a interface identificada pelo
parâmetro IID. Se a interface é suportada pelo objeto, GetInterface retornará true. Se a instância não
for suportada pelo objeto, GetInterface retornará false, e o parâmetro Obj será NULL.

TObject::GetInterfaceEntry

Retorna a entrada para uma específica interface implementada na classe.

struct PACKAGE TInterfaceEntry


{
TGUID IID;
void* VTable;
int IOffset;
};
typedef TInterfaceEntry *PInterfaceEntry;
static PInterfaceEntry __fastcall GetInterfaceEntry(const TGUID IID);

GetInterfaceEntry retorna a classe de entrada para a interface especificada pelo parâmetro IID.
GetInterfaceEntry é usado internamente. Raramente será chamada diretamente.

TObject::GetInterfaceTable

Retorna um ponteiro para uma estrutura contendo todas as interfaces implementadas por uma
determinada classe.

struct PACKAGE TInterfaceEntry


{
TGUID IID;
void* VTable;
int IOffset;
};

struct PACKAGE TInterfaceTable


{
int EntryCount;
TInterfaceEntry Entries[];
};
typedef TInterfaceTable *PInterfaceTable;
static PInterfaceTable * __fastcall GetInterfaceTable(void);

GetInterfaceTable retorna a TInterfaceEntries para a classe. Esta lista contém somente interfaces
implementadas por esta classe, e não por seus ancestrais. Para encontrar uma lista ancestral, chame
GetParentClass e então chame o seu método GetInterfaceTable. Para encontrar a entrada para uma
interface específica, use o método GetInterfaceEntry no lugar de GetInterfaceTable.

TObject::InheritsFrom

Determina o relacionamento de dois tipos de objetos.

typedef TMetaClass* TClass;


static bool __fastcall InheritsFrom(TClass cls, TClass aClass);
bool __fastcall InheritsFrom(TClass aClass)
{
return
InheritsFrom(ClassType(), aClass);
}

Use InheritsFrom para determinar se um tipo de classe em particular ou objeto é uma instância de uma
classe ou de uma de suas descendentes. InheritsFrom retorna true se o tipo de objeto especificado no
parâmetro aClass for um ancestral do tipo do objeto ou o tipo do objeto por si mesmo. De outro modo,
retorna false.

InheritsFrom é útil para determinar se uma classe descendente, método ou propriedade pode ser usada,
dada uma variável de uma classe base. Por exemplo, use InheritsFrom para determinar se o parâmetro
Sender num evento handler é o de um tipo de classe em particular ou uma de suas descendentes.

Use o método ClassNameIs quando a classe precisar adaptar exatamente, sem retornar true se a classe
for uma classe ancestral.

TClass ButtonClassType;
ButtonClassType = Button1->ClassType();

TClass RadioButtonClassType;
RadioButtonClassType = RadioButton1->ClassType();

if(InheritsFrom(RadioButtonClassType, ButtonClassType)==true)
ShowMessage("true");
else
ShowMessage("false");

TObject::InitInstance

Inicializa uma instância de objeto alocada recentemente.

typedef TMetaClass* TClass;


static TObject * __fastcall InitInstance(TClass cls, void *instance);

TObject * __fastcall InitInstance(void *instance)


{
TObject::InitInstance(this, instance);
}

Não há razões para chamar InitInstance diretamente. InitInstance é chamada automaticamente por
NewInstance quando um objeto é criado. Quando ativar NewInstance seja claro em chamar
InitInstance como o último enunciado.

InitInstance não é virtual, assim não se pode ativá-la. Em vez disso, inicialize qualquer dado para um
objeto no construtor.

TObject::InstanceSize

Retorna o size, em bytes, de cada instância do objeto.

typedef TMetaClass* TClass;


static long __fastcall InstanceSize(TClass cls);

long __fastcall InstanceSize()


{
return InstanceSize(ClassType());
}

Use InstanceSize para determinar quantos bytes de memória são requeridos para os dados de uma
instância de uma classe. C++Builder usa InstanceSize internamente para métodos que alocam e
desalocam memória. InstanceSize não é um método virtual, e sendo assim, não pode ser ativado
manualmente. InitInstance deve ser chamada somente no contexto de implementação para uma versão
custom de NewInstance.

TObject::MethodAddress

Retorna o endereço de um método published.

typedef TMetaClass* TClass;


static void * __fastcall MethodAddress(TClass cls,
const ShortString &Name);

void * __fastcall MethodAddress(const ShortString &Name)


{
return MethodAddress(ClassType(), Name);
}

MethodAddress é usado internamente pelo streaming system VCL. Quando lê um evento de um


stream, MethodAddress converte um método Name, especificado por Name, para um ponteiro
contendo o método address. Não deve ser necessário chamar MethodAddress diretamente.

Se Name não específica um método published para o objeto, MethodAddress retorna NULL.
TObject::MethodName

Retorna uma string contendo o nome do método localizado em Address.

typedef TMetaClass* TClass;


static ShortString __fastcall MethodName(TClass cls, void *Address);
ShortString __fastcall MethodName(void *Address)
{
return MethodName(ClassType(), Address);
}

MethodName é usado internamente por streaming system VCL. Quando escreve um evento
propriedade para um stream, MethodName converte um ponteiro contendo o método address para uma
string contendo o método name. Ele é a contra-parte para MethodAddress. Não é necessário chamar
diretamente.

Se Address não apontar para um método published do objeto, MethodName retorna uma string vazia.

TObject::NewInstance

Aloca memória para cada instância de um objeto e retorna um ponteiro para a nova instância.

typedef TMetaClass* TClass;


virtual TObject* __fastcall NewInstance(TClass cls);

Todos os construtores chamam NewInstance automaticamente. NewInstance chama InstanceSize para


determinar quanta memória precisa ser alocada no heap para conter uma instância em particular. Não
chame NewInstance diretamente.

Ative NewInstance somente para requerimento de alocação especial de memória. Por exemplo, quando
há uma alocação de um grande número de objetos idênticos onde todos precisam estar na memória ao
mesmo tempo, faz-se uma alocação de um grande bloco de memória para o grupo, e depois ativa
NewInstance para usar parte desse grande bloco para cada instância.

Se ativar NewInstance é provável que necessite ativar FreeInstance para desalocar essa memória.

Quando ativar NewInstance, chame InitInstance como última instrução.

Nota: Por default NewInstance chama InitInstance.

TObject::SafeCallException

Tratamento de exceções OLE.


virtual HResult __fastcall SafeCallException(
TObject *ExceptObject, void *ExceptAddr);

SafeCallException é usado para tratamento de exceção COM. Como implementado em TObject,


SafeCallException retorna E_UNEXPECTED. Este é um caso default que é apropriado para classes
que não suportam qualquer interface COM. Classes que implementam interfaces COM deveriam
ativar esse método para tratar outros erros que possam ocorrer.

****************************************

Assim nós concluímos nosso tour por TObject e seus métodos. Valeu a pena? De um modo geral, os
objetos e componentes herdam a totalidade dos métodos implementados por TObject. Portanto
acreditamos ser fundamental uma boa noção desses princípios basilares da VCL. Como dissemos no
princípio, estamos indo do geral em direção ao específico.

Na próxima edição estaremos introduzindo nossa jornada rumo a TPersistent. TPersistent,além de


herdar métodos de TOBject, implementa alguns próprios. Aqui ainda não encontraremos eventos ou
propriedades.

Depois estaremos passeando por TComponent. Nesta classe, além de um batalhão de novos métodos,
encontraremos as primeiras propriedades a implementadas pelo BCB, dentre as quais, algumas muito
conhecidas como Name, Owner e Tag.

TControl deverá nos consumir razoável tempo de estudo, uma vez que estaremos diante de muitos
métodos e propriedades novos, sem mencionar que nesta classe aparecem os primeiros eventos.

A seguir estaremos diante de TWinControl... e assim por diante até chegarmos naqueles componentes
criados e instalados por você.

Sem dúvida pode ser muita pretensão de nossa parte! Mas, naquilo que depender de nós, ao menos
tentaremos!

****************************************
**********************************
****************************
**********************

Truques e dicas BCB

Formulários sempre visíveis


Para que um formulário seja mantido sobre os outros forms, podemos, no Object Inspector, assinalar-
lhe a propriedade FormStyle para fsStayOnTop. Já fsNormal, determina a normalidade (sobreponível).

FormStyle pode assumir um dos seguintes valores:

fsMDIChild O form é uma janela MDI child.

FsMDIForm O form é uma janela MDI parent. Em aplicações MDI, assinalamos esse
valor para a janela principal.

FsNormal Um form em seu estado natural.

FsStayOnTop Determina que o form permaneça no topo do desktop sobre todos os


outros forms, salvo aqueles que também tenham a propriedade FormStyle assinalada para
fsStayOnTop. Nesse caso, tais formulários poderão alternar-se no topo.

Importante: Não é prudente alterar a propriedade FormStyle em tempo de execução, o que poderia
significar um problema. Imagine uma tela de proteção que, ocupando toda a área do desktop, esteja
assinalada para permanecer sobre outros formulários (fsStayOnTop). Nesse caso, para ativar o efeito,
poderíamos chamar a função SetWindowPos da API do Windows passando-lhe, como segundo
parâmetro, os valores HWND_TOPMOST ou HWND_NOTOPMOST:

Vamos desenvolver um exemplo básico, onde veremos como ativar ou desativar tal efeito em tempo de
execução:

Inicie uma nova aplicação. No construtor do form:

__fastcall TForm1::TForm1(TComponent* Owner)


: TForm(Owner)
{
// tiramos barra de títulos e bordas
BorderStyle = bsNone;
// janela maximizada
WindowState = wsMaximized;
// melhoramos problemas de repintura da janela
SetWindowLong(Form1->Handle, GWL_EXSTYLE,
GetWindowLong(Form1->Handle, GWL_EXSTYLE) | WS_EX_TRANSPARENT);
// janela transparente
Form1->Brush->Style = bsClear;
}

No evento OnCreate do Form:

void __fastcall TForm1::FormCreate(TObject *Sender)


{
// colocamos o form sobre os demais
// pela chamada direta à API.
// Poderia estar num botão etc
SetWindowPos(Handle, HWND_TOPMOST, 0, 0, 0, 0,
SWP_NOMOVE + SWP_NOSIZE);
}

No evento OnDplClick do Form:


void __fastcall TForm1::FormDblClick(TObject *Sender)
{
// escolhemos uma senha
AnsiString Senha = "DicasBCB";
// valor para comparação
AnsiString Tentativa =
InputBox("Desativar proteção", "Entre com a senha", "");
// Se o valor digitado pelo usuário for igual à senha
if(Tentativa==Senha)
// deixamos a janela ser sobreposta
SetWindowPos(Handle, HWND_NOTOPMOST, 0, 0, 0, 0,
SWP_NOMOVE + SWP_NOSIZE);
else // senão
// avisamos ...
ShowMessage("Senha não confere");
}

O exemplo acima pode ser melhorado, através da desativação de teclas etc. Mas desenvolver uma tela
de proteção não é o escopo do presente artigo, e sim mostrar que sempre existem formas alternativas
para se contornar certas restrições do C++Builder!

****************************
**********************

Efeitos de Font

Efeito de profundidade. O exemplo a seguir leva um label no form. Altere as seguintes propriedades
do label:

Alignment = taCenter
AutoSize = false
Font -> Size = 12

Acerte o tamanho do label para que apareça todo o seu Caption. Os eventos OnMouseDown e
OnMouseUp do label devem ficar semelhantes a:

void __fastcall TForm1::Label1MouseDown(TObject *Sender,


TMouseButton Button, TShiftState Shift, int X, int Y)
{
Label1->Font->Size=4;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Label1MouseUp(TObject *Sender, TMouseButton Button,
TShiftState Shift, int X, int Y)
{
Label1->Font->Size=12;
}

O Size do label poderia conter valores diferentes dos usados acima. Poderíamos, também, deixar a font
em negrito, sublinhado e/ou itálico, bastando alterar a propriedade TfontStyles().

Tfont::Style

Determina se a font é normal, italic, underlined, bold, e assim por diante.

enum TFontStyle { fsBold, fsItalic, fsUnderline, fsStrikeOut };


typedef Set<TFontStyle, fsBold, fsStrikeOut> TFontStyles;
__property TFontStyles Style = {read=GetStyle, write=SetStyle, nodefault};

Use Style para adicionar características especiais à font. Style pode assumir um dos seguintes valores:

fsBold - a Font fica em negrito


fsItalic - a Font fica em itálico
fsUnderline - a Font fica sublinhada
fsStrikeout - a Font é exibida com um risco horizontal

Poderíamos alterar o Color ou o Name da Font também. São inúmeras as possibilidades:

void __fastcall TForm1::Label1MouseDown(TObject *Sender,


TMouseButton Button, TShiftState Shift, int X, int Y)
{
Label1->Font->Size=4;
// Label1->Font->Name="Times New Roman";
Label1->Font->Color=clRed;
Label1->Font->Style=TFontStyles()<<fsBold<<fsItalic;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Label1MouseUp(TObject *Sender, TMouseButton Button,
TShiftState Shift, int X, int Y)
{
Label1->Font->Size=12;
// Label1->Font->Name="Lucida Console";
Label1->Font->Color=clBlue;
Label1->Font->Style=TFontStyles()>>fsBold>>fsItalic;
}

****************************
**********************

Atributos dos arquivos


Quem nunca deu um clique com o botão direito do mouse sobre um arquivo e escolheu a opção
Propriedades no menu pop-up que se abre?
No momento que fazemos isso, estamos acessando os atributos de tal arquivo. Podemos alterá-los
manualmente. Ou podemos desenvolver um código que o faça para nossa aplicação.

Atributos dos arquivos são valores inteiro. Por exemplo:

Somente Leitura: 1
Oculto: 2
Sistema: 4
Arquivo: 32
Nenhum atributo assinalado: 128

Nota: Para assinalar mais de um atributo para o arquivo, basta somar os valores.

Vejamos um trecho tirado do tópico TSearchRec type do help do C++Builder:

Constante Valor Descrição

faReadOnly $00000001 Read-only files


faHidden $00000002 Hidden files
faSysFile $00000004 System files
faVolumeID $00000008 Volume ID files
faDirectory $00000010 Directory files
faArchive $00000020 Archive files
faAnyFile $0000003F Any file

**********************
FileSetAttr
extern PACKAGE int __fastcall FileSetAttr(const AnsiString FileName, int Attr);

FileSetAttr assinala, como atributo de arquivo, o valor do parâmetro Attr para o arquivo especificado
por FileName.

**********************
FileGetAttr
extern PACKAGE int __fastcall FileGetAttr(const AnsiString FileName);

FileGetAttr, por sua vez, retorna os atributos de FileName.

**********************

Sem mais delongas, vamos aos exemplos práticos. Eis um pequeno programa para verificar os atributos
dos arquivos:

//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
// #include <Filectrl.hpp> // para DirectoryExists
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
String arquivo;
int atributo;
arquivo=ExtractFilePath(Application->ExeName)+ "\Data.txt";
atributo=FileGetAttr(arquivo);
ShowMessage(atributo);
}
//---------------------------------------------------------------------------

O programa abaixo lê e depois altera os atributos do arquivo:

//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
// #include <Filectrl.hpp> // para DirectoryExists
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
String arquivo;
int atributo;
arquivo=ExtractFilePath(Application->ExeName)+ "\Data.txt";
atributo=FileGetAttr(arquivo);
ShowMessage(atributo);
//Somente Leitura: 1
//Arquivo: 32
//Oculto: 2
// 1+32+2=35
FileSetAttr(arquivo, 35);
}
//---------------------------------------------------------------------------