Marsil 5 Posted yesterday 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 yesterday at 02:17 AM by Marsil Share this post Link to post
Remy Lebeau 1613 Posted yesterday 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 yesterday at 05:21 AM by Remy Lebeau 1 Share this post Link to post
Marsil 5 Posted yesterday 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. 145 Posted yesterday 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 yesterday 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 yesterday at 09:13 AM by Marsil Share this post Link to post
Marsil 5 Posted yesterday 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. 145 Posted yesterday 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. 145 Posted yesterday 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 1613 Posted 16 hours ago 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 9 hours ago 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