Jump to content
limelect

Listview data problem

Recommended Posts

I hope I explained my problem  with the listview

I have 2 pictures and 2 text

Adding text ,the text items is OK, but

Adding 2 pictures is the problem

 

  PListItemData = ^TListItemData;

  TListItemData = record
    theString: string;
    ThePicture: TBitmap;
  end;
b: Tbitmap; <<<< is created at oncreat.

var
  ClipItem: TListItem;

ListItemData: PListItemData;

 

 while not FDQuery2.Eof do
  begin
------ process Query

ClipItem := lvClip.Items.Insert(0);

  New(ListItemData);

    try
      ListItemData.theString := s.Text;  <<< OK
     if ContainsText(s.Text, 'Picture') then
       begin
         b.Assign(nil);
        BlobField := FDQuery2.FieldByName('Image') as TBlobField;
            Stream := FDQuery2.CreateBlobStream(BlobField, bmRead);<<< read the picture

        b.LoadFromStream(Stream);
        ListItemData.ThePicture := TBitmap(b); <<<< get also the picture
        FreeAndNil(Stream);
      end;
      ClipItem.Data := ListItemData;

    except
      Dispose(ListItemData);
    end;
    FDQuery2.Next;
  end;

No problem, no leak BUT !!!! Now to the problem of looping twice

 

PListItemData(lvClip.Items[0].data).ThePicture <<<< first picture
PListItemData(lvClip.Items[0].data).TheString <<< first text OK

Second time in the loop

PListItemData(lvClip.Items[1].data).ThePicture <<< got second picture
PListItemData(lvClip.Items[1].data).TheString <<< second text OK

But now PListItemData(lvClip.Items[0].data).ThePicture got the second picture

 

SO, more explanation. The first item got the second picture!!! But the text is OK it has 2 texts no problem

 

On the breakpoint, I see the watch list as

PListItemData(lvClip.Items[0].data).ThePicture
PListItemData(lvClip.Items[0].data).TheString

PListItemData(lvClip.Items[1].data).ThePicture
PListItemData(lvClip.Items[1].data).TheString



         

Share this post


Link to post

All of your list items are pointing at a single TBitmap object in memory, so all of them will show the last image that was loaded from the DB.  If you want to show a separate image for each list item, they each need their own TBitmap object, eg:

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

...

procedure TMyForm.RunQueryAndFillListView;
var
  ClipItem: TListItem;
  ListItemData: PListItemData;
begin
  ...
  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);
    end;
    FDQuery2.Next;
  end;
  ...
end;

...

// TListView.OnDeletion event handler
procedure TMyForm.lvClipDeletion(Sender: TObject; Item: TListItem);
var
  ListItemData: PListItemData;
begin
  ListItemData := PListItemData(Item.Data);
  if ListItemData <> nill then
  begin
    ListItemData.ThePicture.Free;
    Dispose(ListItemData);
  end;
end;

 

Edited by Remy Lebeau

Share this post


Link to post

@Remy Lebeau Obviously, it works, but using this, free gives an error

D10,2,3

procedure TMyForm.lvClipDeletion(Sender: TObject; Item: TListItem);
var
  ListItemData: PListItemData;
begin
  ListItemData := PListItemData(Item.Data);
  if ListItemData <> nill then
  begin
    ListItemData.ThePicture.Free; <<<<<<<<<<<<<<<< error
    Dispose(ListItemData);
  end;
end;

On vcl.control

 

procedure TWinControl.MainWndProc(var Message: TMessage);
begin
  try
    try
      WindowProc(Message);
    finally
      FreeDeviceContexts;
      FreeMemoryContexts;
    end;
  except
    Application.HandleException(Self); <<<<<<<<<<<< error
  end;
end;
 

I wanted to add this to the program close

 

procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
var
  i: Integer;
  ListItemData: PListItemData;

begin
 for i := 0 to lvClip.Items.Count - 1 do
 //   if lvClip.Items.Data <> nil then
    begin
      ListItemData := PListItemData(lvClip.Items.Data);
      if ListItemData <> nil then
      begin
 //     PListItemData(lvClip.Items.Data).ThePicture.Free;
  ListItemData.ThePicture.Free; <<<<<<<<<< Error and
//        Dispose(ListItemData); <<<<<<<<<<<  I cannot use this as it gives many more errors
      end;

    end;

end;

 

Any Idea ?

Share this post


Link to post
1 hour ago, limelect said:

@Remy Lebeau Obviously, it works, but using this, free gives an error

D10,2,3

Calling Free() on a nil pointer is perfectly safe. So that means you must be calling Free() on a non-nil pointer that is NOT pointing at a valid TBitmap object. Did you make the adjustments I showed you to give each TListItemData its own TBitmap object?

 

Also, DO NOT use both OnClose and OnDeletion events to free the same memory. Use one or the other. The OnDeletion event is the preferred place to free custom data stored in a TListView, as it is called every time a TListItem is removed from the TListView, even during shutdown.

 

I've used this technique many times in the past, so I know it works.

1 hour ago, limelect said:


 

 

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×
OSZAR »