2

I think I found a potential bug in TListView.

Steps to reproduce: Create a new VCL Forms application, add a TListView, set it`s ViewStyle to vsReports. Add two buttons

button1:

procedure TForm1.Button1Click(Sender: TObject);
var
  lCol: TListColumn;
begin
  lcol :=   ListView1.Columns.Add;
  lcol.Caption := 'name';
  lcol :=   ListView1.Columns.Add;
  lcol.Caption := 'name2';
  lcol :=   ListView1.Columns.Add;
  lcol.Caption := 'name3';
end;

button2:

procedure TForm1.Button2Click(Sender: TObject);
begin
  ListView1.Columns.Delete(1);
end;

Result: The column is deleted, but the caption of the last column gets lost. This also happens, when adding more columns and deleting a column that is between others (or deleting the first column). The caption of the last column is always empty.

I'm using XE3. Is there anything I missed?

Thanks

edit: QC link

potential duplicate

torno
  • 466
  • 12
  • 25

2 Answers2

4

There's more to this then what's reported in the question (more at the end).

This is related with a previous question of yours. That question involved the listview control losing the mapping between columns and items/subitems when you moved a column after adding a column. I proposed a possible fix for comctrls.pas which involved preserving FOrderTags of columns when they are moved. The VCL had 'FOrderTag's built from the ground-up whenever a column is moved - disregarding any current positioning of the columns.

What happens then is, you file a bug report, submit the possible fix as a workaround, and it gets checked-in exactly as is. The problem now you discover is, when we preserve FOrderTag of each column, and then remove a column from the middle, we create a hole - they are not sequential any more (say we have columns 0, 1 and 2 with respective order tags, remove column 1 and now we have 2 columns with order tags 0 and 2). Apparently the native control does not like this.

Again modifying the VCL, we can remove any possible hole when we are removing a column. The below seems to take care of the missing caption and the AV when you resize/move the column with the missing caption mentioned in a comment to the question.

destructor TListColumn.Destroy;
var
  Columns: TListColumns;
  i: Integer; //+
begin
  Columns := TListColumns(Collection);
  if TListColumns(Collection).Owner.HandleAllocated then
    ListView_DeleteColumn(TListColumns(Collection).Owner.Handle, Index);
//{+
  for i := 0 to Columns.Count - 1 do
    if Columns[i].FOrderTag > FOrderTag then
      Dec(Columns[i].FOrderTag);
//}
  inherited Destroy;
  Columns.UpdateCols;
end;


Now if we come back to what's not reported in the question, if you had been inserted some subitems, you'd have noticed that they are preserving their positions, IOW the mapping between columns and subitems are lost. There's the probability that your view is different then mine on this, but I think the subitems of the deleted column should get lost. Unfortunately I couldn't figure out a way to achieve this.


edit: I cannot think of anything to easily integrate/fix in the VCL. There's nothing stopping you from deleting the first inserted column. This one corresponds to the items, if we delete the items, all subitems will also be taken out. The current implementation in VCL is that in fact no item data is deleted when you remove a column. You can verify this by adding a column after you remove one, subitems will magically appear under the new column.

Anyway, what I can suggest you to is to delete the subitems of a removed column manually. Below is an example of a utility procedure to delete a column and its corresponding subitems:

procedure ListViewDeleteColumn(ListView: TListView; Col: Integer);
var
  i: Integer;
  ColumnOrder: array of Integer;
begin
  SetLength(ColumnOrder, ListView.Columns.Count);
  ListView_GetColumnOrderArray(
        ListView.Handle, ListView.Columns.Count, PInteger(ColumnOrder));
  Assert(ColumnOrder[Col] <> 0, 'column with items cannot be removed');

  for i := 0 to ListView.Items.Count - 1 do
    if Assigned(ListView.Items[i].SubItems) and
        (ListView.Items[i].SubItems.Count >= Col) then
    ListView.Items[i].SubItems.Delete(ColumnOrder[Col] - 1);

  ListView.Columns.Delete(Col);
end;

If you decide to delete the first column, decide what you'll do with items/subitems and rebuild them.

Community
  • 1
  • 1
Sertac Akyuz
  • 54,131
  • 4
  • 102
  • 169
  • thanks again sertac :) so maybe I won't suggest this workaround to QC this time and let them do their work.. perhaps they`ll fix the subitems too. you are right, that subitems connected to the column should get deleted too – torno Sep 21 '12 at 07:16
  • @torno - What the vendor does with a bug report is theirs to decide. But as it is now this is only half-work, so I don't think it's worth putting in the report.  You're welcome! – Sertac Akyuz Sep 21 '12 at 08:48
2

If you delete the last column using code below it works ok:

uses
 CommCtrl;

procedure TForm1.Button3Click(Sender: TObject);
begin
  ListView_DeleteColumn(ListView1.Handle, 2);
end;
Shambhala
  • 1,159
  • 3
  • 13
  • 31
  • for some reasons, the Columns.Count is not updated after using this macro above. Any suggestion how to force an update? – torno Sep 20 '12 at 14:34
  • @torno, columns are stored in the [`TListColumns`](http://docwiki.embarcadero.com/Libraries/en/Vcl.ComCtrls.TListColumns) collection (from which the column should be deleted). Since you are deleting columns using directly the [`ListView_DeleteColumn`](http://msdn.microsoft.com/en-us/library/windows/desktop/bb761236(v=vs.85).aspx) Windows API macro, the collection wasn't updated. Using this workaround you broke the collection synchronization. In other words, it's just a bad workaround. – TLama Sep 20 '12 at 14:57
  • The goal was not to delete the last column. The goal was to delete *anything but* the last column, and for the last column to appear and operate normally. – Rob Kennedy Sep 20 '12 at 14:58
  • @TLama: Yeah, apparently this is the case. – torno Sep 20 '12 at 15:02
  • I am trying to figure out how to update the columns but I don't think my answer is that great... I may remove it – Shambhala Sep 20 '12 at 15:02
  • Then you can try to delete the column item from the [`Columns`](http://docwiki.embarcadero.com/Libraries/en/Vcl.ComCtrls.TCustomListView.Columns) collection after you use that macro, but it's still quite *dirty* workaround. – TLama Sep 20 '12 at 15:05
  • deleting it from the collection afterwards causes the bug to appear again. – torno Sep 20 '12 at 15:10