Я согласен с решением Marjan Venema, так как оно использует уже встроенную поддержку для хранения объектов в TStringList.
Я также имел дело с этим, и я сначала получил свой собственный компонент со списком, используя настроенную версиюиз вышеупомянутого решения с "csOwnerDrawFixed".Мне действительно нужно было хранить идентификатор (обычно из базы данных) вместе с текстом.Идентификатор будет скрыт от пользователя.Я думаю, что это распространенный сценарий.ItemIndex используется только для извлечения данных из списка, на самом деле он не является значимой переменной, как в приведенном выше примере.
Поэтому я решил объединить идентификатор с отображаемым текстом, разделенным знаком "# ", например, и переопределить DrawItem (), чтобы он рисовал текст только с разделенным идентификатором.Я расширил это, чтобы сохранить больше, чем идентификатор, в форме «Имя # ID; var1; var2», например."Майкл Симонс # 11; правда; М".DrawItem () удалит все после #.
Теперь это хорошо для начала, когда в комбо есть несколько предметов.Но при работе с большим списком прокрутка комбо интенсивно использует процессор, так как при каждом рисовании элемента текст должен быть убран.
Итак, во второй версии, которую я сделал, использовался метод AddObject.Это торгует процессором для немного большего потребления памяти, но это справедливая сделка, потому что все было намного быстрее.
Текст, который видит пользователь, обычно хранится в combo.Items, а все остальные данные хранятся вTStringList связан с каждым элементом.Нет необходимости переопределять DrawItem, поэтому вы можете получить, например, из.TmxFlatComboBox и сохранить его плоский вид как есть.
Вот некоторые из наиболее важных функций производного компонента:
procedure TSfComboBox.AddItem(Item: string; Lista: array of string);
var ListaTmp: TStringList;
i: integer;
begin
ListaTmp:= TStringList.Create;
if High(Lista)>=0 then
begin
for i:=0 to High(Lista) do
ListaTmp.Add(Lista[i]);
end;
Items.AddObject(Item, ListaTmp);
//ListaTmp.Free; //no freeing here! we override .Clear() also and the freeing is done there
end;
function TSfComboBox.SelectedId: string;
begin
Result:= GetId(ItemIndex, 0);
end;
function TSfComboBox.SelectedId(Column: integer): string;
begin
Result:= GetId(ItemIndex, Column);
end;
function TSfComboBox.GetId(Index: integer; Column: integer = 0): string;
var ObiectTmp: TObject;
begin
Result:= '';
if (Index>=0) and (Items.Count>Index) then
begin
ObiectTmp:= Items.Objects[Index];
if (ObiectTmp <> nil) and (ObiectTmp is TStringList) then
if TStringList(ObiectTmp).Count>Column then
Result:= TStringList(ObiectTmp)[Column];
end;
end;
function TSfComboBox.SelectedText: string;
begin
if ItemIndex>=0
then Result:= Items[ItemIndex]
else Result:= '';
end;
procedure TSfComboBox.Clear;
var i: integer;
begin
for i:=0 to Items.Count-1 do
begin
if (Items.Objects[i] <> nil) and (Items.Objects[i] is TStringList) then
TStringList(Items.Objects[i]).Free;
end;
inherited Clear;
end;
procedure TSfComboBox.DeleteItem(Index: Integer);
begin
if (Index < 0) or (Index >= Items.Count) then Exit;
if (Items.Objects[Index] <> nil) and (Items.Objects[Index] is TStringList) then
TStringList(Items.Objects[Index]).Free;
Items.Delete(Index);
end;
В обеих версиях все данные (даже идентификаторы) представлены в виде строкпотому что он хранит вещи более общего характера, поэтому при их использовании вам нужно выполнить много преобразований StrToInt и наоборот.
Пример использования:
combo1.AddItem('Michael Simons', ['1', '36']); // not using Items.Add, but .AddItem !
combo1.AddItem('James Last', ['2', '41']);
intSelectedID:= StrToIntDef(combo1.SelectedId, -1); // .ItemIndex would return -1 also, if nothing is selected
intMichaelsId:= combo1.GetId(0);
intMichaelsAge:= combo1.GetId(0, 1); // improperly said GetId here, but you get the point
combo1.Clear; // not using Items.Clear, but .Clear directly !
Кроме того,
GetIndexByValue(ValueToSearch: string, Column: integer = 0): integer
метод полезен для получения индекса любого идентификатора, но этот ответ уже слишком длинный, чтобы разместить его здесь.
Используя тот же принцип, вы также можете получить собственный ListBoxили CheckListBox.