Я убежден, что это должно быть типичной проблемой, но я, может казаться, не нахожу простое решение...
Я хочу использовать управление полем комбинированного списка с парами значение-имя как объекты. ComboBox берет TStrings в качестве своих объектов так, чтобы был прекрасен.
К сожалению, метод рисования для поля комбинированного списка тянет Объекты [я] так, Вы получаете Name=Value в поле.
Я хотел бы, чтобы значение было скрыто, таким образом, я могу работать со значением в коде, но пользователь видит имя.
Какие-либо идеи?
Установить Стиль
на csOwnerDrawFixed
и напишите
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;
Текст элементов поля со списком должен содержать отображаемый текст. Это правильный стиль. Затем используйте свойство ItemIndex для поиска значений внутренних ключей. Преодоление свойств элемента управления для хранения кода модели или значений внутренних ключей базы данных является серьезным нарушением принципов ООП.
Давайте просто посмотрим, как кто-то собирается поддерживать ваше приложение в будущем. Вы можете сами вернуться к этому коду и подумать: «О чем я думал?». Помните «принцип наименьшего удивления». Используйте вещи так, как они предназначены, и спасите себя и своих коллег от боли.
Если ваши значения целые: Разделите пары значений имени, сохраните имена в строках поля со списком и значения в соответствующих объектах.
for i := 0 to List.Count - 1 do
ComboBox.AddItem(List.Names[i], TObject(StrToInt(List.ValueFromIndex[i], 0)));
Таким образом, вы можете продолжать использовать свои элементы управления обычным образом и по-прежнему иметь значение, доступное через:
Value := Integer(ComboBox.Items.Objects[ComboBox.ItemIndex]);
Этот подход также можно использовать для списков других объектов. Например, TObjectList, содержащий экземпляры объекта TPerson:
var
i: Integer;
PersonList: TObjectList;
begin
for i := 0 to PersonList.Count - 1 do
ComboBox.AddItem(TPerson(PersonList[i]).Name, PersonList[i]);
и получение соответствующего TPerson выбранного элемента с помощью:
Person := TPerson(ComboBox.Items.Objects[ComboBox.ItemIndex]);
Лучший способ - и тот, который не зависит от значений, являющихся целыми числами, - это предварительно обработать List, заключая значения в простой класс и добавляя их экземпляры к объектам List.
Простой расширенный класс оболочки на основе RTTI:
type
TValueObject = class(TObject)
strict private
FValue: TValue;
public
constructor Create(const aValue: TValue);
property Value: TValue read FValue;
end;
{ TValueObject }
constructor TValueObject.Create(const aValue: TValue);
begin
FValue := aValue;
end;
Если вы используете версию Delphi до D2010, просто используйте строку
вместо TValue.
Предварительная обработка списка:
// Convert the contents so both the ComboBox and Memo can show just the names
// and the values are still associated with their items using actual object
// instances.
for idx := 0 to List.Count - 1 do
begin
List.Objects[idx] :=
TValueObject.Create(List.ValueFromIndex[idx]);
List.Strings[idx] := List.Names[idx];
end;
Загрузка списка в Combo теперь является простым назначением:
// Load the "configuration" contents of the string list into the combo box
ComboBox.Items := List; // Does an Assign!
Имейте в виду, что внутренне это выполняет назначение, поэтому вам лучше убедиться, что комбинация больше не может получить доступ к экземплярам объектов его списка до того, как освободить список.
Получение имени и значения из списка:
begin
Name_Text.Caption := List.Items[idx];
Value_Text.Caption := TValueObject(List.Objects[idx]).Value.AsString;
end;
или из ComboBox:
begin
Name_Text.Caption := ComboBox.Items[idx];
Value_Text.Caption := TValueObject(ComboBox1.Items.Objects[idx]).Value.AsString;
end;
Эту же информацию с более подробным объяснением можно найти в моем блоге: TL; версия DR пары значений имени в ComboBox и Kinfolk