Marsil 5 Posted Saturday at 02:13 AM (edited) Hi, I have a ListView with CheckBoxes enabled (CheckBoxes = True) and an OnItemChecked event handler, if I try to add an item to the ListView: var Item := ListView1.Items.Add; // this immediately triggers OnItemChecked Item.Data := SomeLinkedObject; Item.Checked := SomeLinkedObject.Enabled; the OnItemChecked event handler is called immediatley not giving me a chance to assign the Item.Data property. The handler is procedure TForm1.ListView1ItemChecked(Sender: TObject; Item: TListItem); begin if not Assigned (Item.Data) then Memo1.Lines.Add ('ListView1ItemChecked called with nil Data '); // this debug always executed with nil data TSomeLinkedObject (Item.Data).Enabled := Item.Checked // AV here when ListView1.Items.Add executed end; Any idea why this is happening? or is it a bug? Edited Saturday at 02:17 AM by Marsil Share this post Link to post
Remy Lebeau 1617 Posted Saturday at 05:18 AM (edited) The checkboxes are implemented as state images. You are getting the event when the item's initial state is assigned. Just check for the nil condition and don't access the object when it's not ready yet: procedure TForm1.ListView1ItemChecked(Sender: TObject; Item: TListItem); begin if Assigned(Item.Data) then TSomeLinkedObject(Item.Data).Enabled := Item.Checked; end; Alternatively, disable the event when adding an item, and then re-enable the event when ready: ListView1.OnItemChecked := nil; var Item := ListView1.Items.Add; ... ListView1.OnItemChecked := ListView1ItemChecked; Edited Saturday at 05:21 AM by Remy Lebeau 1 Share this post Link to post
Marsil 5 Posted Saturday at 08:04 AM Thanks Remy, I implemented the first workaround. But I still don't understand why the ListView is firing the checked event for an item that still being created and still not properly initialized yet?😕 this behavior was really unexpected I always use VirtualTreeView, but this time I decided to use ListView because it will only contain just one or two items, a quick lightweight job. Share this post Link to post
Kas Ob. 147 Posted Saturday at 08:49 AM 38 minutes ago, Marsil said: that still being created and still not properly initialized yet? But, it is already created, initialized (with default) and added with ListView1.Items.Add Your next Item.Checked := SomeLinkedObject.Enabled; Is triggering the event as designed, nothing wrong here, If you don't want that behavior, then try to create the item as local var, not by calling Items.Add then set (Initialize) properties and only then add it to the items, though i am not sure if this will trigger the event or not, it shouldn't, but who knows you need to test it, this will change the narrative of the expected behaviour and might, i say might, be discussed as bug or short in design, if just adding an item triggers an event. 1 Share this post Link to post
Marsil 5 Posted Saturday at 09:09 AM (edited) 23 minutes ago, Kas Ob. said: But, it is already created, initialized (with default) and added with ListView1.Items.Add I mean my own initialization! 23 minutes ago, Kas Ob. said: Your next Item.Checked := SomeLinkedObject.Enabled; Is triggering the event as designed, nothing wrong here, No, the OnItemChecked event is triggered by this statement var Item := ListView1.Items.Add; // this immediately triggers OnItemChecked As I explained in the comment, I tried and removed the Item.Checked := SomeLinkedObject.Enabled; and same problem!! 23 minutes ago, Kas Ob. said: If you don't want that behavior, then try to create the item as local var, not by calling Items.Add then set (Initialize) properties and only then add it to the items, though i am not sure if this will trigger the event or not, it shouldn't, but who knows you need to test it, this will change the narrative of the expected behaviour and might, i say might, be discussed as bug or short in design, if just adding an item triggers an event. Thanks!, I will try what you suggested here! Edited Saturday at 09:13 AM by Marsil Share this post Link to post
Marsil 5 Posted Saturday at 10:08 AM 50 minutes ago, Kas Ob. said: If you don't want that behavior, then try to create the item as local var, not by calling Items.Add then set (Initialize) properties and only then add it to the items, though i am not sure if this will trigger the event or not, it shouldn't, but who knows you need to test it, this will change the narrative of the expected behaviour and might, i say might, be discussed as bug or short in design, if just adding an item triggers an event. I tried this var Item := TListItem.Create (ListView1.Items); Item.Caption := SomeLinkedObject.Name; Item.Checked := SomeLinkedObject.Enabled; Item.Data := SomeLinkedObject; ListView1.Items.AddItem (Item); but the item is shown without caption!, and memory leak occurred! --------------------------- Unexpected Memory Leak --------------------------- An unexpected memory leak has occurred. The unexpected small block leaks are: 13 - 20 bytes: TList x 4, Unknown x 4 21 - 28 bytes: UnicodeString x 3 37 - 44 bytes: UnicodeString x 1 45 - 52 bytes: TListItem x 4 77 - 84 bytes: TSubItems x 4 I don't think solves the issue since the only way to implement your suggestion is to use ListView.Items.AddItem which is called by ListView.Items.Add. 1 Share this post Link to post
Kas Ob. 147 Posted Saturday at 10:30 AM You are right the the behavior is wrong and buggy, Using similar approach of create then add, doesn't show caption, but no memory leak on XE8. 1 Share this post Link to post
Kas Ob. 147 Posted Saturday at 10:41 AM Debugging and tracking the OnChecked event, the behavior has nothing to do with Delphi and its VCL, it is triggered by Windows message, so you have to use a workaround about that, something like what Remy suggested. 1 Share this post Link to post
Remy Lebeau 1617 Posted Saturday at 09:43 PM 13 hours ago, Marsil said: But I still don't understand why the ListView is firing the checked event for an item that still being created and still not properly initialized yet?😕 As I said earlier, you are getting the event when the new item's default state image is assigned. More specifically, when TListView.Items.Add() creates a new TListItem object and inserts it into the TListView.Items, it calls the Win32 ListView_InsertItem() function, which immediately sends the TListView a LVN_ITEMCHANGED notification before it returns. In that notification, the item's uChanged field is LVIF_STATE and the state image has changed index from 0 to 1. That condition is what fires the OnItemChecked event. The item is fully initialized from the Win32 perspective before TListView.Items.Add() returns. Anything you do extra to the item after Add() returns is optional, and is initialization from your perspective, not the Win32 perspective. 1 Share this post Link to post
Marsil 5 Posted Sunday at 04:53 AM Thanks again Remy!, I'll try to keep that in mind. Whatever the technical details behind this, But as a component user, I still consider it awkward to work with because It doesn't behave like other components, at least not in this case, which makes it a bit weird. Share this post Link to post
Lars Fosdal 1866 Posted Monday at 10:50 AM Ref. the above mentioned leak. Does it occur if you set ListView.OwnerData to true? There is BeginUpdate/EndUpdate on ListView.Items, but not sure that would suppress item events. Share this post Link to post
Remy Lebeau 1617 Posted Monday at 03:10 PM On 7/5/2025 at 3:08 AM, Marsil said: I tried this var Item := TListItem.Create (ListView1.Items); Item.Caption := SomeLinkedObject.Name; Item.Checked := SomeLinkedObject.Enabled; Item.Data := SomeLinkedObject; ListView1.Items.AddItem (Item); but the item is shown without caption!, and memory leak occurred! I cannot reproduce any memory leak with that code, but I can reproduce the caption issue. You need to add the item before changing its Caption: var Item := TListItem.Create (ListView1.Items); ListView1.Items.AddItem (Item); // <-- move here Item.Caption := SomeLinkedObject.Name; Item.Checked := SomeLinkedObject.Enabled; Item.Data := SomeLinkedObject; The reason is because setting the Caption assigns the LPSTR_TEXTCALLBACK flag on the item, which TListView needs to display the assigned String data, but AddItem() does not set that same flag. So, if you set the Caption and then Add, the flag is not set and the ListView has no text data to display. Looks like a bug, I have reported it: RSS-3772: TListItems.AddItem() does not set LPSTR_TEXTCALLBACK flag for existing TListItem 1 Share this post Link to post
Marsil 5 Posted Tuesday at 03:36 AM (edited) 12 hours ago, Remy Lebeau said: You need to add the item before changing its Caption: The idea was to first set item properties then add it to Items as Kas Ob suggested that it may solve the unwanted too early OnItemChecked event trigger: On 7/5/2025 at 11:49 AM, Kas Ob. said: If you don't want that behavior, then try to create the item as local var, not by calling Items.Add then set (Initialize) properties and only then add it to the items, though i am not sure if this will trigger the event or not, it shouldn't, but who knows if I coded it as you showed the Items.AddItem behaves just like Items.Add, Actually Items.Add calls Items.AddItem to do the work, But Items.AddItem gives us a chance to set properties beforehand, but it's buggy and useless. 12 hours ago, Remy Lebeau said: Looks like a bug, I have reported it Indeed looks like a bug because the AddItem method documentation says: Link Quote Call AddItem to add an Item at any place in the list. The properties of Item are duplicated if Item is not nil. But clearly the properties of Item are NOT duplicated. Thanks for reporting that. I'm not sure why item properties are duplicated when the item itself is supposed to be added to the Items, why the properties are duplicated and where? Edited Tuesday at 04:09 AM by Marsil Share this post Link to post
Marsil 5 Posted Tuesday at 03:57 AM (edited) 17 hours ago, Lars Fosdal said: Ref. the above mentioned leak. Does it occur if you set ListView.OwnerData to true? There is BeginUpdate/EndUpdate on ListView.Items, but not sure that would suppress item events. On 7/5/2025 at 1:30 PM, Kas Ob. said: but no memory leak on XE8. 12 hours ago, Remy Lebeau said: I cannot reproduce any memory leak with that code I tried to reproduce the leak but I couldn't , I'm sorry, the leak probably was accidental and not related to the code I said it caused a leak. I could not edit my post about the leak, no edit button. Edited Tuesday at 03:58 AM by Marsil Share this post Link to post
Remy Lebeau 1617 Posted Tuesday at 04:39 AM (edited) 1 hour ago, Marsil said: But clearly the properties of Item are NOT duplicated. Thanks for reporting that. I'm not sure why item properties are duplicated when the item itself is supposed to be added to the Items, why the properties are duplicated and where? The documentation is misleading. The properties are NOT duplicated at all. If you pass in an existing TListItem then it gets inserted as-is and becomes part of the TListView. Edited Tuesday at 04:39 AM by Remy Lebeau 1 Share this post Link to post
Marsil 5 Posted Tuesday at 05:27 AM Yes you are right, I verified that the item itself is added to Items, and no duplication as documentation says: Assert (Item = ListView1.Items.AddItem (Item), 'Item is different than the one in Items'); // always passes Now the documentation is buggy too Share this post Link to post
Remy Lebeau 1617 Posted Tuesday at 07:19 AM 1 hour ago, Marsil said: Now the documentation is buggy too RSS-3777: TListItems.AddItem() documentation is misleading 1 Share this post Link to post