Search code examples
listviewdelphidelphi-7listviewitem

TListItem.Checked switches itself to True after Assign()


I came across some unexpected behaviour of TListItem. Check the following test code:

  var
  Item: TListItem;

  //...

  ListView1.Checkboxes := True;
  with ListView1.Items.Add do
  begin
    Caption := 'old item';
    Checked := False;
  end;

  Item := TListItem.Create(ListView1.Items);
  Item.Caption := 'new item';
  Item.Checked := False;

  ListView1.Items[0].Assign(Item);

  Assert(ListView1.Items[0].Caption = 'new item');
  Assert(ListView1.Items[0].Checked = False); //WTF

Assertion failure occurs, as Checked property somehow switches itself to True. I didn't find any remarks on this behaviour in the help file.

Is this a bug or a feature?

I'm using Delphi 7 with all updates.


Solution

  • Delphi comes with VCL source code. Look in ComCtrls.pas for the implementation of TListItem and TListView.

    Had you enabled "Use debug DCUs" in your project options and then stepped through the TListItem source code with the debugger, you would have seen exactly what is happening. Item.Checked := False fails, and reading Item.Checked always returns True, because Item.Index is -1 (since you bypassed TListItems.Add()).

    The TListItem.GetChecked() getter method does not handle the possibility that the ListView_GetCheckState() macro (which is a wrapper for the LVM_GETITEMSTATE message) is failing due to being passed an invalid item index.

    Manually creating a TListItem object is not enough to add the item to the underlying Win32 ListView control. If you create a TListItem via means other than TListItems.Add() or TLisItem.Insert(), you must call TListItems.AddItem() to insert the TListitem into the underlying ListView control.