Карта сайта Kansoftware
НОВОСТИУСЛУГИРЕШЕНИЯКОНТАКТЫ
KANSoftWare

Решение проблемы с отображением изображений в ListView Delphi при использовании TListItemData

Delphi , Компоненты и Классы , TListView

 

При работе с компонентом TListView в Delphi разработчики часто сталкиваются с проблемами при отображении изображений, особенно когда элементы списка содержат как текст, так и картинки. Рассмотрим решение этой проблемы на основе реального кейса с форума.

Проблема

Исходный код пытался отображать в TListView элементы с текстом и изображениями, загружаемыми из базы данных. Основные проблемы:

  1. Все элементы списка показывали одно и то же (последнее загруженное) изображение
  2. Возникали ошибки при освобождении памяти
  3. Неправильная работа при удалении элементов

Анализ проблемы

Основная ошибка заключалась в использовании одного объекта TBitmap для всех элементов списка. В исходном коде:

b: TBitmap; // создается при OnCreate
...
ListItemData.ThePicture := TBitmap(b); // все элементы ссылаются на один объект

Это приводило к тому, что все элементы списка показывали последнее загруженное изображение, так как они ссылались на один и тот же объект в памяти.

Решение

Правильный подход - создавать отдельный объект TBitmap для каждого элемента списка:

type
  PListItemData = ^TListItemData;
  TListItemData = record
    theString: string;
    ThePicture: TBitmap;
  end;

procedure TMyForm.RunQueryAndFillListView;
var
  ClipItem: TListItem;
  ListItemData: PListItemData;
  BlobField: TBlobField;
  Stream: TStream;
begin
  lvClip.Items.BeginUpdate;
  try
    while not FDQuery2.Eof do
    begin
      ClipItem := lvClip.Items.Insert(0);
      New(ListItemData);
      try
        ListItemData.theString := s.Text;
        ListItemData.ThePicture := nil; // инициализация

        if ContainsText(s.Text, 'Picture') then
        begin
          BlobField := FDQuery2.FieldByName('Image') as TBlobField;
          Stream := FDQuery2.CreateBlobStream(BlobField, bmRead);
          try
            ListItemData.ThePicture := TBitmap.Create;
            ListItemData.ThePicture.LoadFromStream(Stream);
          finally
            Stream.Free;
          end;
        end;

        ClipItem.Data := ListItemData;
      except
        ListItemData.ThePicture.Free;
        Dispose(ListItemData);
        raise; // повторно вызываем исключение
      end;
      FDQuery2.Next;
    end;
  finally
    lvClip.Items.EndUpdate;
  end;
end;

Обработка удаления элементов

Для корректного освобождения памяти необходимо обрабатывать событие OnDeletion:

procedure TMyForm.lvClipDeletion(Sender: TObject; Item: TListItem);
var
  ListItemData: PListItemData;
begin
  ListItemData := PListItemData(Item.Data);
  if ListItemData <> nil then
  begin
    ListItemData.ThePicture.Free; // безопасно, даже если nil
    Dispose(ListItemData);
  end;
end;

Альтернативное решение

Если возникают проблемы с обработчиком OnDeletion, можно использовать альтернативный подход с классом вместо записи:

type
  TListItemData = class
  public
    theString: string;
    ThePicture: TBitmap;
    constructor Create;
    destructor Destroy; override;
  end;

constructor TListItemData.Create;
begin
  inherited;
  ThePicture := nil;
end;

destructor TListItemData.Destroy;
begin
  ThePicture.Free;
  inherited;
end;

// Заполнение ListView
procedure TMyForm.FillListView;
var
  ClipItem: TListItem;
  ListItemData: TListItemData;
begin
  ListItemData := TListItemData.Create;
  try
    // заполнение данных
    ClipItem.Data := ListItemData;
  except
    ListItemData.Free;
    raise;
  end;
end;

// Обработчик удаления
procedure TMyForm.lvClipDeletion(Sender: TObject; Item: TListItem);
begin
  TListItemData(Item.Data).Free;
end;

Проблемы и их решение

  1. Ошибка при освобождении памяти: Убедитесь, что ThePicture инициализирован как nil перед созданием. Проверка на nil перед вызовом Free не обязательна, так как Free сам проверяет на nil.

  2. Проверка на пустое изображение: Вместо проверки ThePicture.Empty (которая может вызвать ошибку, если объект не создан), используйте:

if (ListItemData.ThePicture <> nil) and (not ListItemData.ThePicture.Empty) then
  // работаем с изображением
  1. Освобождение памяти при закрытии формы: Если вы используете OnDeletion, дополнительное освобождение в OnClose не требуется. Если же решите освобождать в OnClose, делайте это аккуратно:
procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
var
  i: Integer;
begin
  for i := 0 to lvClip.Items.Count - 1 do
    if lvClip.Items[i].Data <> nil then
      TListItemData(lvClip.Items[i].Data).Free;
end;

Заключение

Правильное управление памятью при работе с TListView и пользовательскими данными требует:

  1. Создания отдельных объектов для каждого элемента
  2. Корректного освобождения памяти
  3. Использования либо OnDeletion, либо OnClose для освобождения ресурсов (но не обоих сразу)

Предложенные решения помогут избежать проблем с отображением изображений и утечками памяти в ваших приложениях на Delphi.

Создано по материалам из источника по ссылке.

Решение проблемы некорректного отображения изображений в Delphi TListView путем создания отдельных объектов TBitmap для каждого элемента и правильного управления памятью.


Комментарии и вопросы

Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS




Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.


:: Главная :: TListView ::


реклама


©KANSoftWare (разработка программного обеспечения, создание программ, создание интерактивных сайтов), 2007
Top.Mail.Ru

Время компиляции файла: 2024-12-22 20:14:06
2025-08-06 01:43:45/0.0058791637420654/0