2011-11-24 13 views
6

Sto tentando di aggiungere una colonna tra le colonne esistenti in un TListView. Per questo aggiungo la nuova colonna alla fine e la sposto impostando il suo indice sul valore designato. Funziona, fino all'aggiunta di un'altra nuova colonna.TListView: VCL perde l'ordine delle colonne se si aggiunge una colonna

Cosa ho fatto: Aggiungere la colonna nell'ultima posizione (Colonne.Aggiunte) e aggiungere anche il sottotitolo nell'ultima posizione (Sottitems.Add). Successivamente sposto la colonna impostando l'indice nella posizione corretta. Questo funziona bene finché è solo una colonna che viene aggiunta. Quando si aggiunge una seconda nuova colonna, i sottotemi si rovinano. Il nuovo sottoelemento della prima colonna viene spostato nell'ultima posizione, ad es. così:

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

Sarei molto felice se qualcuno potesse aiutare!

Ad esempio, c'è forse un comando o un messaggio che posso inviare a ListView in modo che aggiorni o salvi la colonna Colonna -> Mappatura sottotemia che potrei usare dopo aver aggiunto la prima nuova colonna e le sue sotto-voci in modo da poter gestire il seconda nuova colonna allo stesso modo della prima.

Oppure si tratta solo di un bug della colonna TListViews -> gestione di sottotitoli o TListColumns ...?

codice di esempio per un'applicazione forme VCL (assegnare l'evento 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. 

Grazie!


Edit: La correzione suggerita da Sertac Akyuz funziona bene, anche se non posso usarlo perché cambiare il codice sorgente Delphi non è una soluzione per il mio progetto. Bug è segnalato.

Modifica: Rimossa la seconda domanda non voluta inclusa nel primo post e aperta una nuova domanda (vedi domanda collegata e domanda-revisione).

Aggiornamento: Il reported bug è ora chiuso fisso come di Delphi XE2 Update 4.

+0

Suppongo che ci sia un aggiornamento/aggiornamento mancante da qualche parte. Non sono sicuro di cosa sia. Detto questo, questo suona come un altro caso in cui le visualizzazioni della lista delle modalità virtuali brillerebbero. –

+0

Ma sono disponibili solo per .Net, no? Ho lo stesso problema con il progetto C# .Net equivalente e forse posso usarlo lì. – torno

+0

No. La visualizzazione elenco di Windows supporta la modalità virtuale e Delphi lo avvolge molto bene. Se stai manipolando le colonne in fase di esecuzione è sicuramente la strada da percorrere.Tutti gli altri qui ti indicherebbero alla visualizzazione ad albero virtuale, ma a me piace il controllo nativo. –

risposta

7

Chiamare il metodo UpdateItems dopo aver disposto le colonne. Es .:

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



Aggiornamento:

Nel mio test, mi sembra ancora avere bisogno della chiamata di cui sopra in qualche occasione. Ma il vero problema è che "c'è un errore nel controllo della vista elenco Delphi".

Duplicare il problema con un progetto semplice:

  • Posizionare un controllo TListView in un modulo VCL, impostare la sua ViewStyle a 'vsReport' e impostare FullDrag al 'vero'.
  • mettere il codice sottostante per il OnCreate gestore della forma:
    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'); 
    
  • Posizionare un TButton sulla forma, e mettere il codice sottostante per il suo OnClick gestore:
    ListView1.Columns.Add.Caption := 'col 4';
  • Eseguire il progetto e trascinare l'intestazione di colonna di 'col 3' a in-tra 'col 1' e 'col 2'. L'immagine qui sotto è quello che vedrete in questo momento (tutto va bene):

    list view after column drag

  • Fare clic sul pulsante per aggiungere una nuova colonna, ora vista elenco diventa:

    list view after adding column

    Si noti che ' la cella 2 'ha recuperato la sua posizione originale.

Bug:

Le colonne di un TListView (TListColumn) mantiene le sue informazioni nel suo ordinamento FOrderTag campo. Ogni volta che si modifica l'ordine di una colonna (impostando la proprietà Index o trascinando l'intestazione), questo FOrderTag viene aggiornato di conseguenza.

Ora, quando si aggiunge una colonna alla raccolta TListColumns, la raccolta aggiunge prima il nuovo TListColumn e quindi chiama il metodo UpdateCols. Il sotto è il codice del metodo di TListColumnsUpdateCols in 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; 


il codice precedente rimuove tutte le colonne dal API sottostante controllo list-view e poi li inserisce nuovamente. Si noti come il codice assegna FOrderTag il contatore di indice di ciascuna colonna inserita:

 Items[I].FOrderTag := I; 

Questo è l'ordine delle colonne da sinistra a destra in quel punto nel tempo. Se il metodo viene chiamato ogni volta che le colonne vengono ordinate in modo diverso rispetto al momento della creazione, tale ordine viene perso. E poiché gli oggetti non cambiano di conseguenza le loro posizioni, tutto si confonde.

Fix:

La modifica sotto sul metodo sembrava funzionare per il poco quanto ho provato, è necessario effettuare più test (evidentemente questa correzione non copre tutti i casi possibili, vedere 'Torno di commenti qui sotto per i dettagli):

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; 

Se non si utilizza pacchetti che si può mettere una copia modificata di 'comctrls.pas' alla cartella del progetto. Altrimenti si potrebbe perseguire l'applicazione del codice di runtime, o presentare una segnalazione di bug e attendere una correzione.

+0

sfortunatamente, questo non risolve il problema :(ancora il comportamento descritto dopo aver aggiunto la seconda nuova colonna può essere riprodotto con il codice sopra dopo aver aggiunto la tua linea. – torno

+0

hai provato il primo esempio? Con l'inserimento della colonna e dei sottotemi alla corretta posizione o lo snippet di codice? sfortunatamente, ho bisogno di farlo ora e posso provare di nuovo lunedì ... grazie per il tuo suggerimento – torno

+0

@torno - No, ho appena testato lo snippet inserendo solo una colonna. Avrei dovuto leggere la domanda throughly ... –

Problemi correlati