views:

269

answers:

3

I'm convinced this must be a common problem, but I can't seem to find a simple solution...

I want to use a combobox control with name value pairs as the items. ComboBox takes TStrings as its items so that should be fine.

Unfortunately the drawing method on a combobox draws Items[i] so you get Name=Value in the box.

I'd like the value to be hidden, so I can work with the value in code, but the user sees the name.

Any Ideas?

+5  A: 

Set Style to csOwnerDrawFixed and write

procedure TForm1.ComboBox1DrawItem(Control: TWinControl; Index: Integer;
  Rect: TRect; State: TOwnerDrawState);
begin
  ComboBox1.Canvas.TextRect(Rect, Rect.Left, Rect.Top, ComboBox1.Items.Names[Index]);
end;
Andreas Rejbrand
Why didn't I think of that? ...
dummzeuch
In the end I just copied StdCtrls TCustomComboBox.DrawItem and replaced the items call, It's only 3 lines anyway.
JamesB
JamesB - the stated answer above is more future proof. What happens when your copy/paste code from StdCtrls is out of date with respect to the base VCL or the windows common controls?
Warren P
The pasted code reads...FillRect......TextOut...I realise that copying and pasting code is not always the best idea, but matching code to the VCL does at least maintain consistancy of appearance to other VCL Controls. I'm not too concerned with FillRect or TextOut being imminently deprecated.
JamesB
+3  A: 

If your values are integers: Split the name value pairs, store the names in the strings of the combobox and the values in the corresponding objects.

  for i := 0 to List.Count - 1 do
    ComboBox.AddItem(List.Names[i], TObject(StrToInt(List.ValueFromIndex[i], 0)));

This way you can keep using your controls in a generic way and still have the value avalaible through:

Value := Integer(ComboBox.Items.Objects[ComboBox.ItemIndex]);

This approach can also be used for lists of other objects. For example a TObjectList containing TPerson object instances:

var
  i: Integer;
  PersonList: TObjectList;
begin
  for i := 0 to PersonList.Count - 1 do
    ComboBox.AddItem(TPerson(PersonList[i]).Name, PersonList[i]);

and retrieve the corresponding TPerson of the selected item through:

Person := TPerson(ComboBox.Items.Objects[ComboBox.ItemIndex]);
Marjan Venema
A: 

The combobox items text should have contained the display text. That is the proper style. Then, use the ItemIndex property to look up the internal key values. Defeating the control's properties to contain your model code or database internal key values, is a huge violation of OOP principles.

Let's just consider how someone is going to maintain your application in the future. You might come back to this code yourself and think, "what was I thinking?". Remember the "principle of least amazement". Use things the way they were meant to be used, and save yourself and your co-workers from pain.

Warren P