limelect 52 Posted yesterday at 05:14 PM 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
Remy Lebeau 1595 Posted 22 hours ago (edited) 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 22 hours ago by Remy Lebeau Share this post Link to post
limelect 52 Posted 2 hours ago @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
Remy Lebeau 1595 Posted 47 minutes ago 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