O combobox (TComboBox) é um componente que apresenta uma lista de vários valores onde tipicamente escolhemos um. Imagine uma lista de frutas:
- Maracujá
- Açaí
- Avelã
- Melão
- Maçã
- Mamão
E você precisa apresentar uma forma de escolha ao usuário que seja econômica num formulário, isto é, que não ocupe muito espaço, então usamos isso:
Um ComboBox pode ser alimentado com valores literais, isto é, o que os olhos vêem será exatamente o que será manipulado depois. Mas também há a opção de escolher com os olhos um valor, mas ter outros atributos a serem usados. Parece estranho, mas fica mais fácil entender na prática.
Valores literais numa ComboBox pode ser alimentada assim:
cbLista.Clear;
cbLista.Items.Add('Maracujá');
cbLista.Items.Add('Acaí');
cbLista.Items.Add('Avelã');
cbLista.Items.Add('Melão');
cbLista.Items.Add('Maçã');
cbLista.Items.Add('Mamão');
No entanto, o exemplo acima não é uma boa prática de programação em quase nenhuma linguagem. Se você trabalha em equipe, nunca deixariam você implementar o código acima, pois ao mudar um valor de um dos elementos da lista teria de alterar todas as ocorrências desse mesmo valor dentro do programa também, e se esquecesse de alterar um deles poderia criar sérios problemas, portanto, sempre que alimentar um ComboBox com valores literais, faça uso de constantes.
Geralmente as constantes deverão estar na seção implementation, pois isso garantiria que as constantes estariam visíveis apenas a unidade do projeto, declarar constantes fora da seção implementation significaria que pretende usar estas mesmas constantes em outras unidades – algo bem raro que se usado erroneamente confunde a IDE e pode causar erros de lógica difíceis de encontrar. Então ficaria assim nosso ajuste:
var
Form1: TForm1;
implementation
{$R *.lfm}
{ TForm1 }
const
Maracuja='Maracujá';
Acai='Açaí';
Avela='Avelã';
Melao='Melão';
Maca='Maçã';
Mamao='Mamão';
procedure TForm1.FormCreate(Sender: TObject);
begin
cbLista.Clear;
cbLista.Items.Add(Maracuja);
cbLista.Items.Add(Acai);
cbLista.Items.Add(Avela);
cbLista.Items.Add(Melao);
cbLista.Items.Add(Maca);
cbLista.Items.Add(Mamao);
cbLista.ItemIndex:=-1;
E para recuperar o valor escolhido simplesmente fazemos isso:
var
FrutaCodigoEscolhida:String;
begin
if cbLista.ItemIndex>=0 then
begin
FrutaCodigoEscolhida:=cbLista.Items.Objects[cbLista.ItemIndex];
Memo1.Lines.Add('fruta escolhida: '+IntToStr(FrutaCodigoEscolhida));
end;
Não tem nada mais simples do que o código acima e é o método mais usado, mas e se fosse algo mais sofisticado? Imagine que ao selecionar Maracujá você queira que o sistema prossiga dizendo que foi selecionado o código 5, isso mesmo, um numero ao invés do texto que foi exibido e selecionado. Neste caso você usaria um método chamado AddObject que serve para atrelar um item da lista a um objeto qualquer previamente criado, mas criar um objeto implica em criarmos construtores, destrutores e propriedades e é muita areia para o nosso caminhãozinho então vamos usar uma forma simplificada de objeto que usará um numero inteiro como objeto(esquisito isso né, explico mais adiante), ficaria assim para popularmos nosso ComboBox:
procedure TForm1.FormCreate(Sender: TObject);
begin
cbLista.Clear;
cbLista.Items.AddObject(Maracuja, TObject(5));
cbLista.Items.AddObject(Acai, TObject(10));
cbLista.Items.AddObject(Avela, TObject(15));
cbLista.Items.AddObject(Melao, TObject(20));
cbLista.Items.AddObject(Maca, TObject(25));
cbLista.Items.AddObject(Mamao, TObject(30));
cbLista.ItemIndex:=-1;
No exemplo acima conseguimos fazer um numero se passar como um objeto – chamamos isso de casting – fazer casting de números para objetos é algo bem criativo e não daria certo dessa maneira simplificada com outros tipos primitivos, mas funciona com números porque o resultado é ponteiro de memória. Para recuperar o valor selecionado bastaria então:
var
FrutaCodigoEscolhida:Integer;
begin
if cbLista.ItemIndex>=0 then
begin
FrutaCodigoEscolhida:=PtrUInt(cbLista.Items.Objects[cbLista.ItemIndex]);
Memo1.Lines.Add('codigo escolhido: '+IntToStr(FrutaCodigoEscolhida));
end;
Mas e se não fosse para retornar um número e sim uma outra String, digamos que ao selecionar Maracujá eu gostaria de retornar um código alfanumérico como ‘cod_maracuja‘? Um exemplo prático seria selecionar pela descrição, porém retornar um código de barras, mas para ser didático vou resumir numa palavra. Isso pode ser feito de duas formas, usando par de chave e valor ou por meio de objetos. Vou mostrar o método que é mais simples o par chave e valor, você já deve ter observado que propriedades como TItems e TStrings pode ser usado com índices(ItemIndex:=x) e também par/valor, este ultimo geralmente usado em algo como MinhaStringList.Values[‘var’] para retornar o valor atrelado a ‘var’, pois bem, vamos dar o nome de AddPair porque a tática é atribuir um valor a uma variável da lista por isso o nome: AddPair do inglês ‘adicionar par’. Ainda usaremos a mesma lista de nosso exemplo, vamos popular nossa base da seguinte forma:
procedure TForm1.FormCreate(Sender: TObject);
begin
cbLista.Clear;
cbLista.Items.AddPair(Maracuja, 'cod_maracuja');
cbLista.Items.AddPair(Acai, 'cod_acai');
cbLista.Items.AddPair(Avela, 'cod_avela');
cbLista.Items.AddPair(Melao, 'cod_melao');
cbLista.Items.AddPair(Maca, 'cod_maca');
cbLista.Items.AddPair(Mamao, 'cod_mamao');
cbLista.ItemIndex:=-1;
Mas isso não resolveria o problema ainda, por que não? Porque nosso ComboBox seria visto assim:
Então precisamos mudar a forma como nosso ComboBox será exibido, tecnicamente ele ainda é do jeito acima internamente, mas externamente, isto é, a forma como será apresentado deve ser modificado. Já que iremos desenhar nosso próprio ComboBox vamos precisar modificar a propriedade cbLista.Style para csOwnerDrawFixed ou csOwnerDrawVariable:
E então incluir um código de programação ao evento OnDrawItem responsável por desenhar a aparência do ComboBox, vou deixá-lo da maneira mais simples possível, mais tarde poderá complementar criando cores alternadas se achar necessário:
procedure TForm1.cbListaDrawItem(Control: TWinControl; Index: Integer;
ARect: TRect; State: TOwnerDrawState);
begin
with (Control as TComboBox) do
begin
if odSelected in State then // requer unit LCLType
begin
Brush.Color := clWhite;
Font.Color := clBlack;
end;
Canvas.FillRect(ARect);
Canvas.TextOut(ARect.Left, ARect.Top, Items.Names[Index]);
end;
end;
Agora nossos elementos serão vistos assim:
E para resgatar o valor selecionado faremos assim:
var
Fruta_Escolhida_Par_Nome:String;
Fruta_Escolhida_Par_Valor:String;
begin
if cbLista.ItemIndex>=0 then
begin
Fruta_Escolhida_Par_Nome := cbLista.Items.Names[cbLista.ItemIndex];
Fruta_Escolhida_Par_Valor:= cbLista.items.Values[Fruta_Escolhida_Par_Nome];
Memo1.Lines.Add('Fruta escolhida: ');
Memo1.Lines.Add(' Nome: '+Fruta_Escolhida_Par_Nome);
Memo1.Lines.Add(' Valor: '+Fruta_Escolhida_Par_Valor);
end;
E valores ainda mais complexos? Imagine que ao selecionar Maracujá você queira que o sistema retorne uma série de propriedades como nome, código e o peso da fruta (nosso exemplo) então neste caso não podemos usar um casting ou o método par/valor como nos exemplos anteriores, temos de criar um objeto completo para armazenar essas informações. Vamos declarar nosso objeto com tudo que um objeto precisa: construtor, destrutor e as propriedades:
{ TFruta }
type TFruta=class(TObject)
private
FNome: string;
FPesoGrama: Integer;
FCodigo: Integer;
public
constructor Create(ANome:String; APesoGrama:Integer; ACodigo:Integer);
destructor Destroy; override;
published
property Nome: string read FNome write FNome;
property PesoGrama: Integer read FPesoGrama write FPesoGrama;
property Codigo:Integer read FCodigo write FCodigo;
end;
O método create com parâmetros é apenas para nos poupar na hora de criação, então vamos ao código:
constructor TFruta.Create(ANome: String; APesoGrama: Integer; ACodigo: Integer);
begin
Nome := ANome;
PesoGrama:=APesoGrama;
Codigo:=ACodigo;
end;
destructor TFruta.Destroy;
begin
inherited Destroy;
end;
Tendo criado o nosso objeto então agora vamos popular o ComboBox da seguinte forma:
procedure TForm1.FormCreate(Sender: TObject);
begin
cbLista.Clear;
cbLista.Items.AddObject(Maracuja, TFruta.Create(Maracuja,550,5));
cbLista.Items.AddObject(Acai, TFruta.Create(Acai,300,10));
cbLista.Items.AddObject(Avela, TFruta.Create(Avela,15,15));
cbLista.Items.AddObject(Melao, TFruta.Create(Melao,500,20));
cbLista.Items.AddObject(Maca, TFruta.Create(Maca,55,25));
cbLista.Items.AddObject(Mamao, TFruta.Create(Mamao,700,30));
cbLista.ItemIndex:=-1;
E para exibir o valor que foi escolhido então:
var
Fruta_Escolhida:TFruta;
begin
if cbLista.ItemIndex>=0 then
begin
Fruta_Escolhida := TFruta(cbLista.Items.Objects[cbLista.ItemIndex]);
Memo1.Lines.Add('Fruta escolhida: '+Fruta_Escolhida.Nome);
Memo1.Lines.Add(' Codigo: '+IntToStr(Fruta_Escolhida.Codigo));
Memo1.Lines.Add(' Peso aproximado gramas: '+IntToStr(Fruta_Escolhida.PesoGrama));
end;
Usar um objeto ao invés de um texto é mais comum do que você imagina, especialmente com banco de dados, onde com métodos como getters e setters (property … get … set ….) você pode pegar os resultados desejados diretamente do banco de dados ao invés de popular a lista previamente como nós fizemos. Nosso exemplo era para ser didático.
Espero ter esclarecido as formas mais comuns – talvez as únicas – de utilizar um ComboBox e retornar dados de formas diferentes. Se precisarem do código fonte dos exemplos usados poderão baixá-los no link a seguir:
1. Demonstração do uso do componente TCombobox no Delphi e FreePascal – uso comum
2. Demonstração de uso do combobox – retornando código do elemento selecionado
3. Demonstração de uso do combobox – retornando uma string diferente do elemento selecionado
4. Demonstração de uso do combobox – retornando o objeto do elemento selecionado
No vídeo a seguir, demonstro como usar essas quatro formas diferentes e uma outra adicional que é uma derivativa de objeto, porém retornando informações de uma base de dados:
Caso queira estudar os exemplos, os mesmos podem ser obtidos aqui: