2011-11-24 63 views
6

我想在TListView中的現有列之間添加一列。因此,我在最後添加新列,並通過將其索引設置爲指定值來移動它。這會起作用,直到添加另一個新列。TListView:VCL失去了列的順序,如果你添加一列

我所做的: 添加最後一個位置的列(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的命令或消息,以便刷新或保存它的Column - > Subitem映射,我可以在添加第一個新列和它的子項後使用,以便我可以處理第二個新列與第一個列相同。

或者這只是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建議的修復工作正常,但由於改變了德爾福源代碼是爲我的項目沒有解決方案,我無法使用它。報告錯誤。

編輯:刪除了第一個帖子中未包含的第二個問題,並打開了新問題(請參閱鏈接問題和問題修訂版)。

更新reported bug現已關閉,如Delphi XE2 Update 4已修復。

+0

我想有一個丟失刷新/更新的地方。不知道它是什麼。也就是說,這聽起來像虛擬模式列表視圖會發光的另一種情況。 –

+0

但它們只能用於.Net,不是嗎?我得到了與C#.Net項目相同的問題,也許可以在那裏使用它。 – torno

+0

編號Windows列表視圖支持虛擬模式,Delphi將其包裝得非常好。如果你在運行時操縱列,它肯定是要走的路。這裏的其他人都會指出你在虛擬樹視圖中,但我自己喜歡本地控制。 –

回答

7

排列好列後調用UpdateItems方法。例如: -

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



更新:

在我的測試中,我似乎仍然需要在一些場合上面的調用。但真正的問題是,「在Delphi列表視圖控件」中存在一個錯誤。

用一個簡單的項目複製的問題:

  • 放置一個TListView控制一個VCL形式,將其ViewStyle爲「vsReport」,並設置FullDrag爲「真」。
  • 把下面的代碼到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方法。下面是TListColumnsUpdateCols法在D2007 VCL的代碼:

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; 

這列從在那個時間點左到右的順序。如果在列的排列順序與創建時不同時調用該方法,那麼該排序會丟失。而且由於物品不會相應地改變它們的位置,它們都會變得混亂起來。

修復:

的方法,下面的修改似乎只要花工作,我的測試,您需要進行更多的測試(顯然此修復程序並沒有覆蓋所有可能的情況,請參見「托爾諾的以下詳細說明評論):

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」的修改拷貝到項目文件夾。否則,你可能會尋求運行時代碼修補,或者提交一個錯誤報告並等待修復。

+0

不幸的是,這並不能解決問題:(在添加第二個新列之後仍然可以使用上面的代碼複製添加行後描述的行爲。 – torno

+0

您是否嘗試過第一個示例?將列和子項插入正確的位置?或代碼片段?不幸的是,我現在得到並可以在星期一再試一次...感謝您的建議 – torno

+0

@torno - 不,我只測試了只插入一列的代碼片段。通過... –