2009-03-02 13 views
8

Considerate questo codice (i nomi dei tipi genericised ai fini di esempio):Quali sono le cause di un WPF ListCollectionView che utilizza l'ordinamento personalizzato per riordinare gli elementi?

// Bound to ListBox.ItemsSource 
_items = new ObservableCollection<Item>(); 

// ...Items are added here ... 

// Specify custom IComparer for this collection view 
_itemsView = CollectionViewSource.GetDefaultView(_items) 
((ListCollectionView)_itemsView).CustomSort = new ItemComparer(); 

Quando ho creato CustomSort, la collezione viene ordinato come mi aspetto.

Tuttavia, ho bisogno che i dati vengano riordinati al momento dell'esecuzione in risposta alla modifica delle proprietà su Item. La classe Item deriva da INotifyPropertyChanged e so che la proprietà si attiva correttamente mentre il mio modello di dati aggiorna i valori sullo schermo, solo la logica di ordinamento non viene chiamata.

Ho anche provato ad aumentare INotifyPropertyChanged.PropertyChanged passando una stringa vuota, per vedere se una notifica generica causerebbe l'ordinamento da avviare. Niente banane

EDIT In risposta al suggerimento di Kent ho pensato di sottolineare che l'ordinamento gli elementi utilizzando questo ha lo stesso risultato, vale a dire che i tipi di raccolta una volta, ma non ri-sorta come le modifiche dei dati:

_itemsView.SortDescriptions.Add(
    new SortDescription("PropertyName", ListSortDirection.Ascending)); 

risposta

7

Ho trovato this article by Dr. WPF che inizia con una risposta alla mia domanda, quindi passa a discutere dell'impatto sulle prestazioni della chiamata Refresh. Alcuni estratti chiave:

Sfortunatamente, il metodo Refresh() restituisce una completa rigenerazione della vista. Quando si verifica un Refresh() all'interno della vista, solleva una notifica CollectionChanged e fornisce l'azione come "Reset". ItemContainerGenerator per il ListBox riceve questa notifica e risponde scartando tutte le immagini esistenti per gli articoli. Quindi rigenera completamente nuovi oggetti e oggetti visivi.

Quindi descrive una soluzione alternativa che migliora le prestazioni. Piuttosto che chiamare Aggiorna, rimuovere, modificare, quindi aggiungere nuovamente l'elemento.

Avrei pensato che la visualizzazione elenco potesse tracciare un elemento che cambia e sapere di riposizionarlo da solo all'interno della vista.

Un nuovo approccio è stato introdotto nel .NET 3.5 SP1 che coinvolgono l'interfaccia IEditableObject che fornisce la modifica transazionale tramite associazioni di dati per il modello con metodi BeginEdit(), CancelEdit(), e EndEdit(). Leggi the article per ulteriori informazioni.

EDIT Come user346528 points out, IEditableObject non era in realtà di nuovo in 3.5SP1. In realtà sembra che sia stato nel framework since 1.0.

+2

Il link è morto. Nuovo: http: // drwpf .com/blog/2008/10/20/itemscontrol-e-is-for-editable-collection/ – Gman

+0

@Gman, grazie, ho aggiornato il collegamento nella risposta –

1

Dato che si sta utilizzando l'ordinamento personalizzato, non è possibile per lo ListCollectionView sapere quali criteri devono attivare un aggiornamento. Pertanto, dovrai chiamare tu stesso lo Refresh() nella vista raccolta.

+0

@Kent, grazie Ci proverò.Non sono chiaro sulla logica qui come ho anche provato l'ordinamento utilizzando "SortDescriptions.Add (new SortDescription". Anche quando il ListCollectionView sa quali criteri per ordinare, non lo fa. –

+0

@Kent - I'm non riesco ancora a vedere perché il framework non è stato in grado di vedere la proprietà specificata in 'SortDescription' per le modifiche La mia classe notifica quando questa proprietà cambia. –

1

Bump di un vecchio post, ma solo creazione di una nuova classe di raccolta che eredita da ListViewCollection e Overrides OnPropertyChanged (per un IBindingList, gli eventi ListChanged conterranno la modifica della proprietà nel parametro ListChangedEventArgs).E assicurati che gli elementi all'interno della raccolta implementino e utilizzino INotifyPropertyChange ogni volta che una proprietà cambia (sollevata da te), o la collezione non si legherà alle modifiche delle proprietà.

Quindi in questo metodo OnPropertyChanged, il mittente sarà l'elemento. Rimuovi l'oggetto se - e solo se - una proprietà che causerebbe la modifica di un resort, quindi aggiungila nuovamente (inseriscila in posizione ordinata se aggiungerla non lo fa già). Spostare un oggetto è preferibile se è disponibile invece di rimuoverlo/aggiungerlo. Allo stesso modo, questo dovrebbe essere fatto anche con il filtraggio (controllando il predicato del filtro).

IEditableObject non è necessario! Se si desidera modificare diverse proprietà, quindi terminare la modifica (come la modifica di 3 proprietà e quindi la selezione su una riga diversa in WinForm DataGridView), dopo averlo ordinato, questo sarebbe il metodo corretto per farlo funzionare. Ma molte volte vorrete probabilmente che la collezione ricorra dopo aver cambiato ogni proprietà senza dover chiamare manualmente BeginEdit/EndEdit. IEditableObject, btw, è presente nel framework .NET 2.0 e non è nuovo su .NET 3.5 (se leggi l'articolo del Dr).

Nota: i problemi possono verificarsi utilizzando BeginEdit() e EndEdit() con più modifiche allo stesso elemento, a meno che non si incrementi (per vero)/decremento (per falso) un intero invece di impostare un booleano! Ricordati di incrementare/decrementare un numero intero per sapere veramente quando il montaggio è finito.

La conservazione di un elenco ordinato in modo permanente richiede molto tempo ed è soggetta a errori (e può interrompere il contratto di inserimento se si forzano gli inserimenti ordinati) e deve essere utilizzata solo in determinati luoghi, ad esempio ComboBox. Su qualsiasi griglia, questa è una pessima idea, poiché la modifica di una riga lo farà ricadere da sotto la posizione corrente degli utenti.

Public Class ListView 
    Inherits ListCollectionView 

    Protected Overrides Sub OnPropertyChanged(sender As Object, e As PropertyChangedEventArgs) 

    ' Add resorting/filtering logic here. 
    End Sub 
End Class 

Il miglior esempio in una collezione che fa simile a quello che vi serve è Jesse Johnsons ObjectListView, anche se questo è NET 2.0 specifica (IBindingList invece di INotifyCollectionChanged/ObservableCollection/ListCollectionView) e utilizza una licenza molto restrittiva. Il suo blog potrebbe essere ancora molto prezioso nel modo in cui ha realizzato questo.

Edit:

dimenticato di aggiungere che Mittente sarà la voce è necessario ricorrere, e e.PropertyName è quello che sarà necessario utilizzare per determinare se rientra nei SortDescriptions. In caso contrario, una modifica a tale proprietà non comporterà la necessità di un resort. Se e.PropertyName è Nothing, quindi è proprio come un aggiornamento, in cui molte proprietà potrebbero essere cambiate e il ricorso dovrebbe essere fatto.

Per determinare se è necessario filtrarlo, è sufficiente eseguirlo attraverso FilterPredicate e rimuoverlo se necessario. Il filtraggio è molto meno costoso dell'ordinamento.

Speriamo disponibile,

TamusJRoyce

+0

Ottima risposta. la prossima volta mi scontrerò con questo: per una delle tue prime risposte SO, questo è fantastico :) Grazie. –

+0

Grazie. Al momento l'ho implementato per un progetto su cui sto lavorando. Vorrei tanto che non fosse closed source, dato che è stato divertente da implementare e voglio condividerlo così male :). Ho anche visto un paio di errori nell'articolo del Dr. WPF, quindi ho pensato di menzionare un'alternativa che non usa BeingEdit/EndEdit. – TamusJRoyce

6

come la risposta accettata mi indirizza verso, sono in grado di forzare singolo elemento riposizionare con il codice di

IEditableCollectionView collectionView = DataGrid.Items; 

collectionView.EditItem(changedItem); 
collectionView.CommitEdit(); 

Dove changedItem è il modello vista (l'articolo nella collezione ItemsSource).

In questo modo non è necessario disporre degli elementi per implementare interfacce come IEditableObject (che a mio parere è molto difficile e difficile da implementare in alcuni casi).

Problemi correlati