TListView:如果添加列,VCL将丢失列的顺序

时间:2011-11-24 14:10:55

标签: delphi dynamic tlistview subitem

我正在尝试在TListView中的现有列之间添加一列。因此,我在最后添加新列,并通过将其索引设置为指定值来移动它。这有效,直到添加另一个新列。

我做了什么: 在最后位置添加列(Columns.Add)并在最后位置(Subitems.Add)添加子项。然后我通过将列的索引设置为正确的位置来移动列。 只要只添加一列,这样就可以正常工作。添加第二个新列时,子项目会被搞砸。第一列的新子项被移动到最后一个位置,例如,像这样:

0        |  1          |  new A       |  new B      | 3
Caption  |  old sub 1  |  old sub 3   |  new Sub B  | new sub A

如果有人可以提供帮助,我会很高兴!

例如,是否有可以发送给ListView的命令或消息,以便刷新或保存它的列 - >添加第一个新列及其子项后我可以使用的子项映射,以便我可以像第一个一样处理第二个新列。

或者这只是TListViews列的错误 - >子项处理或TListColumns ......?

vcl表单应用程序的示例代码(分配Form1.OnCreate事件):

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ComCtrls;

type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
  private
    listview: TListView;
    initButton: TButton;
    addColumn: TButton;
    editColumn: TEdit;
    subItemCount: Integer;
    procedure OnInitClick(Sender: TObject);
    procedure OnAddClick(Sender: TObject);
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
  listview := TListView.Create(self);
  with listview do
  begin
    Left := 8;
    Top := 8;
    Width := self.Width - 30;
    Height := self.Height - 100;
    Anchors := [akLeft, akTop, akRight, akBottom];
    TabOrder := 0;
    ViewStyle := vsReport;
    Parent := self;
  end;

initButton := TButton.Create(self);
with initButton do
  begin
    left := 8;
    top := listview.Top + listview.Height + 20;
    Width := 75;
    Height := 25;
    TabOrder := 1;
    Caption := 'init';
    OnClick := OnInitClick;
    Parent := self;
  end;

  editColumn := TEdit.Create(self);
  with editColumn do
  begin
    left := initButton.Left + initButton.Width + 30;
    top := listview.Top + listview.Height + 20;
    Width := 120;
    Height := 25;
    TabOrder := 2;
    Parent := self;
    Caption := '';
  end;

  addColumn := TButton.Create(self);
  with addColumn do
  begin
    left := editColumn.Left + editColumn.Width + 10;
    top := listview.Top + listview.Height + 20;
    Width := 75;
    Height := 25;
    TabOrder := 1;
    Enabled := true;
    Caption := 'add';
    OnClick := OnAddClick;
    Parent := self;
  end;

end;

procedure TForm1.OnInitClick(Sender: TObject);
var col: TListColumn;
i, j: integer;
item: TListItem;
begin
  listview.Items.Clear;
  listview.Columns.Clear;

  // add items
  for I := 0 to 2 do
  begin
    col := ListView.Columns.Add;
    col.Caption := 'column ' + IntToStr(i);
    col.Width := 80;
  end;

  // add columns
  for I := 0 to 3 do
  begin
    item := ListView.Items.Add;
    item.Caption := 'ItemCaption';

    // add subitems for each column
    for j := 0 to 1 do
    begin
      item.SubItems.Add('subitem ' + IntToStr(j+1));
    end;
  end;

  subItemCount := 5;
end;

procedure TForm1.OnAddClick(Sender: TObject);
var number: integer;
col: TListColumn;
i: Integer;
ascii: char;
begin
  listview.Columns.BeginUpdate;

  number := StrToInt(editColumn.Text);
  ascii :=  Chr(65 + number);

  // create the new column
  col := TListColumn(ListView.Columns.add());
  col.Width := 80;
  col.Caption := ascii;

  // add the new subitems
  for I := 0 to ListView.Items.Count-1 do
  begin
    ListView.Items[i].SubItems.Add('subitem ' + ascii);
  end;

  // move it to the designated position
  col.Index := number;

  listview.Columns.EndUpdate;

  Inc(subItemCount);
end;

end.

谢谢!


编辑:来自Sertac Akyuz的建议修复工作正常,但我无法使用它,因为更改Delphi源代码不是我的项目的解决方案。报道了Bug。

修改:删除了第一篇文章中无意中包含的第二个问题并打开了新问题(请参阅链接问题和问题修订版)。

更新reported bug现已关闭,修正为Delphi XE2 Update 4

1 个答案:

答案 0 :(得分:7)

排列完列后调用UpdateItems方法。 E.g:

..
col.Index := number;
listview.UpdateItems(0, MAXINT);
..


<小时/> 的更新

在我的测试中,我似乎仍然需要在某些场合使用上述电话。但真正的问题是“Delphi列表视图控件中存在错误”

使用简单项目复制问题:

  • 在VCL表单上放置TListView控件,将其ViewStyle设置为“vsReport”并将FullDrag设置为“true”。
  • 将以下代码放入表单的OnCreate处理程序:
    ListView1.Columns.Add.Caption := 'col 1';
    ListView1.Columns.Add.Caption := 'col 2';
    ListView1.Columns.Add.Caption := 'col 3';
    ListView1.AddItem('cell 1', nil);
    ListView1.Items[0].SubItems.Add('cell 2');
    ListView1.Items[0].SubItems.Add('cell 3');
    
  • 在表单上放置TButton,并将以下代码放入其OnClick处理程序:
    ListView1.Columns.Add.Caption := 'col 4';
  • 运行项目并将“col 3”的列标题拖到“col 1”和“col 2”之间。下图是你现在所看到的(一切都很好):

    list view after column drag

  • 点击按钮添加新列,现在列表视图变为:

    list view after adding column

    请注意,“单元格2”已恢复其原始位置。

<强>错误:

TListViewTListColumn)的列在其FOrderTag字段中保存其排序信息。每当您更改列的顺序时(通过设置Index属性或拖动标题),此FOrderTag都会相应更新。

现在,当您向TListColumns集合添加列时,集合首先添加新的TListColumn,然后调用UpdateCols方法。以下是D2007 VCL中UpdateCols TListColumns方法的代码:

procedure TListColumns.UpdateCols;
var
  I: Integer;
  LVColumn: TLVColumn;
begin
  if not Owner.HandleAllocated then Exit;
  BeginUpdate;
  try
    for I := Count - 1 downto 0 do
      ListView_DeleteColumn(Owner.Handle, I);

    for I := 0 to Count - 1 do
    begin
      with LVColumn do
      begin
        mask := LVCF_FMT or LVCF_WIDTH;
        fmt := LVCFMT_LEFT;
        cx := Items[I].FWidth;
      end;
      ListView_InsertColumn(Owner.Handle, I, LVColumn);
      Items[I].FOrderTag := I;
    end;
    Owner.UpdateColumns;
  finally
    EndUpdate;
  end;
end;


上面的代码从基础API列表视图控件中删除所有列,然后重新插入它们。注意代码如何为每个插入的列分配FOrderTag索引计数器:

      Items[I].FOrderTag := I;

这是该时间点从左到右的列顺序。如果在按顺序排序列时调用该方法,则在创建时不同,则该顺序将丢失。而且由于物品不会相应地改变它们的位置,所以这一切都变得混乱。

<强>修正:

以下对该方法的修改似乎与我测试的一样少,你需要进行更多的测试(显然这个修复不包括所有可能的情况,请参阅下面的'torno的评论以获取详细信息):

procedure TListColumns.UpdateCols;
var
  I: Integer;
  LVColumn: TLVColumn;
  ColumnOrder: array of Integer;
begin
  if not Owner.HandleAllocated then Exit;
  BeginUpdate;
  try
    SetLength(ColumnOrder, Count);
    for I := Count - 1 downto 0 do begin
      ColumnOrder[I] := Items[I].FOrderTag;
      ListView_DeleteColumn(Owner.Handle, I);
    end;

    for I := 0 to Count - 1 do
    begin
      with LVColumn do
      begin
        mask := LVCF_FMT or LVCF_WIDTH;
        fmt := LVCFMT_LEFT;
        cx := Items[I].FWidth;
      end;
      ListView_InsertColumn(Owner.Handle, I, LVColumn);
    end;
    ListView_SetColumnOrderArray(Owner.Handle, Count, PInteger(ColumnOrder));

    Owner.UpdateColumns;
  finally
    EndUpdate;
  end;
end;

如果您不使用软件包,可以将“comctrls.pas”的修改副本放到项目文件夹中。否则,您可能会进行运行时代码修补,或者提交错误报告并等待修复。