Проверьте дубликат TListItem перед добавлением в Tlistview
У меня есть список records
что я хочу обобщить в TListView
Структура записи следующая
MyRecord = record
SourceTable: string;
SourceField: string;
TargetTable: string;
TargetField: string;
end;
В записи может быть несколько экземпляров SourceTable / TargetTable, с единичными экземплярами поля Source/Target.
Я хотел бы создать TListView
в стиле vsReport, который суммирует каждую пару SourceTable \ TargetTable.
В идеале я хотел бы сделать следующее:
procedure SetTables;
var
mp: MyPointer;
LI: TListItem;
begin
LI := LI.Create(nil);
LI.Caption := ap^.SourceTable;
LI.SubItems.Add(ap^.TargetTable);
LI.Checked := not ap^.Updated;
if lvMigration.Items.IndexOf(LI) = -1 then
lvMigration.Items.AddItem(LI);
end;
т.е. создайте автономный TListItem, проверьте, что он еще не существует, затем добавьте его в мой TListView. Однако это нарушается при назначении LI.Caption
- по сути, нечего назначать. Я подозреваю, что, по крайней мере, часть проблемы заключается в (nil)
Нормальное создание TListItem будет использовать LI := lvMigration.Items.Add;
но это не помогает моему варианту использования. Я не могу найти документацию, где сделано выше.
2 ответа
Вместо:
LI := LI.Create(nil);
Ты хотел написать
LI := TListItem.Create(nil);
Это самая старая ошибка Delphi в книгах, и я уверен, что вы сделали это раньше (у всех нас), и я уверен, что вы узнаете об этом, когда увидите.
Остальная часть вашего кода не будет работать, хотя. Вы ничего не можете сделать с TListItem
экземпляр без предоставления Owner
, Например, посмотрите на реализацию TListItem.SetCaption
:
procedure TListItem.SetCaption(const Value: string);
begin
if Value <> Caption then
begin
FCaption := Value;
if not Owner.Owner.OwnerData then
....
end;
end;
С вашим кодом, Owner
является nil
и поэтому этот код просто приведет к нарушению доступа. На самом деле вы не должны быть экземпляром TListItem
Когда-либо. Это делается с помощью контейнерных классов.
Даже если без этой проблемы, вы найдете это IndexOf
не делает то, что вы хотите. Вы хотите, чтобы он выполнил поиск по значению предмета. Но он выполняет поиск по ссылке.
То, что вам нужно будет сделать, - это перебрать каждый элемент в списке и проверить его Заголовок и подэлементы (или все, что идентифицирует элемент) относительно предполагаемого нового значения.
Если вы настроены на более радикальные изменения, которые в долгосрочной перспективе облегчат жизнь и улучшат производительность, если список станет большим, то переключение на использование списка в виртуальном режиме поможет. Вы должны вести список своих предметов в TList<TMyItem>
или похожие. И тогда вы будете заполняться по требованию. Если вы это сделаете, то будет намного проще обнаружить дубликаты, так как вы будете работать с простым контейнером, а не с элементом управления GUI. По сути, вы усложняете жизнь, используя элемент управления с графическим интерфейсом в качестве класса контейнера.
Я бы предложил вам отсортировать основной список записей во второй список, в котором отфильтрованы дубликаты, а затем использовать второй список для заполнения ListView. Я также предложил бы использовать ListView в виртуальном режиме (установите для его свойства OwnerData значение true, а затем используйте его события OnData...). Это кардинально улучшит производительность вашего ListView, и вы сможете получить доступ к своим записям в памяти и манипулировать ими, и пользовательский интерфейс не будет замедлен.