Recent

Author Topic: ListView ImageList Read, possibly a bug  (Read 659 times)

MaxM74

  • New Member
  • *
  • Posts: 23
ListView ImageList Read, possibly a bug
« on: January 30, 2025, 01:22:37 pm »
I am trying to read the imagelist of a ListView from a file, if I read it during the Create of the Form
(line 106 of testImgList_m.pas) then it works.
If I read it later using the "Read All" Button in the example, and selecting the "imgList.img" file, it is not displayed even if it has been correctly read.

Another consideration is: When I replace an image ("Replace Sel" button) it is not updated immediately, the only way is to change the Caption of the Item (line 69 of testImgList_m.pas).

Am I doing something wrong or should I report it as a Bug?
Thanks.

wp

  • Hero Member
  • *****
  • Posts: 12591
Re: ListView ImageList Read, possibly a bug
« Reply #1 on: January 30, 2025, 05:01:03 pm »
TListView is a very complex component. What we see in the LCL properties scratches only the surface, the bulk of the operations is done behind the scene by the widgetset. So whatever you do with the LCL properties must be passed on the widgetset where it is finally handled.

As for replacing an image in the imagelist, the problem is that the widget must be notified of the change. In the ImageIndex setter of the TListView this is done by calling the ItemSetImage method of the widget class. Since this method is not accessible from the outside, I think the most straightforward way to inform the widget about the image change is to call the ImageIndex setter. But this is not executed when there is no change at all, therefore we must set the ImageIndex to -1 before:

Code: Pascal  [Select][+][-]
  1. procedure TForm1.btReplaceSelClick(Sender: TObject);
  2. var
  3.    p: TPicture;
  4.    oCap: String;
  5.    imgIdx: Integer;
  6. begin
  7.   if (ListView1.Selected<>nil) and OpenPictureDialog1.Execute then
  8.   begin
  9.     imgIdx := ListView1.Selected.ImageIndex;
  10.  
  11.     p:= TPicture.Create;
  12.     p.LoadFromFile(OpenPictureDialog1.FileName);
  13.     ImageList1.Replace(imgIdx, p.Bitmap, nil);
  14.     p.Free;
  15.  
  16.     ListView1.Selected.ImageIndex := -1;
  17.     ListView1.Selected.ImageIndex := imgIdx;
  18.   end;
  19. end;

Some similar caching seems to be involved when the listview is initially created. There is a Flags property in TListItems which is an empty set after streaming the ListView from the lfm file, but after your call to ReadData this flags are at [lisWSItemsCreated]. I don't know whether this causes the different behaviour, and it does not matter after all since the Flags are not writeable from the outside. But looking at the ListView component editor in unit ListViewPropEdit (in packages/ideintf) there is an idea how to do it correctly:

Code: Pascal  [Select][+][-]
  1. procedure TListViewItemsEditorForm.SaveToList;
  2. var
  3.   I, J: Integer;
  4.   Node: TTreeNode;
  5.   Item: TListItem;
  6. begin
  7.   if Assigned(FListView) then
  8.   begin
  9.     FListView.BeginUpdate;
  10.     try
  11.       FListView.Items.Clear;
  12.  
  13.       //Recreate new items or modify
  14.       for I := 0 to TreeView1.Items.Count - 1 do
  15.       begin
  16.         Node := TreeView1.Items[I];
  17.         if Node.Level = 0 then
  18.         begin
  19.           Item := FListView.Items.Add;
  20.           Item.Caption := Node.Text;
  21.           Item.ImageIndex := Node.ImageIndex;
  22.           Item.StateIndex := Node.StateIndex;
  23.  
  24.           //SubItems
  25.           for J := 0 to Node.Count - 1 do
  26.           begin
  27.             Item.SubItems.Add(Node.Items[J].Text);
  28.             Item.SubItemImages[J] := Node.Items[J].ImageIndex;
  29.           end;
  30.         end;
  31.       end;
  32.     finally
  33.       FListView.EndUpdate;
  34.     end;
  35.        
  36.     FModified := True;
  37.   end;
  38. end;

So, simply call ListView1.Clear at the beginning of your LoadImgFile, and the items will be displayed with their images.

MaxM74

  • New Member
  • *
  • Posts: 23
Re: ListView ImageList Read, possibly a bug
« Reply #2 on: January 31, 2025, 08:41:37 am »
I want to test what happens if after readdata I inform the widgetset that the images have changed.
I noticed that trying to call
Code: Pascal  [Select][+][-]
  1.  
  2. TWSCustomListView.Item Update;
  3.  
  4. or
  5.  
  6. TWSCustomImageListResolutionClass(WidgetSetClass).Replace;
  7.  
outside the class always throws an exception, so I have to do it in TCustomListView.ReadData.

p.s.   there is a bug in the forum if i write the method Item Update correctly I have a gift for finding bugs  :o
« Last Edit: January 31, 2025, 08:54:26 am by MaxM74 »

MaxM74

  • New Member
  • *
  • Posts: 23
Re: ListView ImageList Read, possibly a bug
« Reply #3 on: January 31, 2025, 11:39:38 am »
Solution is to Destroy and Recreate the Reference after ReadData

Code: Pascal  [Select][+][-]
  1. procedure TDragImageListResolution.ReadData(AStream: TStream);
  2. begin
  3.   inherited ReadData(AStream);
  4.  
  5.   if HandleAllocated then
  6.   begin
  7.     DestroyReference;
  8.     ReferenceNeeded;
  9.   end;
  10. end;
  11.  

I made a bug report with a related patch

https://gitlab.com/freepascal.org/lazarus/lazarus/-/merge_requests/430

MaxM74

  • New Member
  • *
  • Posts: 23
Re: ListView ImageList Read, possibly a bug
« Reply #4 on: February 04, 2025, 01:08:35 pm »
Solved in Lazarus Trunk :D

 

TinyPortal © 2005-2018