Was verursacht einen WPF-Listcollectionview, die benutzerdefinierten Verwendungen Sortierung seine Produkte neu zu sortieren?

StackOverflow https://stackoverflow.com/questions/601864

  •  03-07-2019
  •  | 
  •  

Frage

Betrachten Sie diesen Code (Typnamen für die Zwecke Beispiel genericised):

// 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();

Wenn ich CustomSort gesetzt ist, wird die Sammlung sortiert, wie ich erwartet.

Allerdings benötige ich um die Daten neu zu sortieren selbst zur Laufzeit in Reaktion auf die Änderung der Eigenschaften auf Item. Die Item Klasse leitet sich von INotifyPropertyChanged und ich weiß, dass die Eigenschaft Feuer richtig als meine Datenvorlage die Werte auf dem Bildschirm aktualisiert, nur die Sortierlogik nicht aufgerufen werden.

Ich habe auch versucht INotifyPropertyChanged.PropertyChanged Anhebung einen leeren String übergeben, um zu sehen, ob eine generische Benachrichtigung die Sortier zu initiierenden verursachen würde. Keine Bananen.

Bearbeiten In Reaktion auf Kent Vorschlag dachte ich, ich darauf hinweisen, würde, dass die Elemente Sortierung mit diesem das gleiche Ergebnis, nämlich, dass die Sammlung Sorten einmal aber nicht umsortieren, wie sich die Daten ändern:

_itemsView.SortDescriptions.Add(
    new SortDescription("PropertyName", ListSortDirection.Ascending));
War es hilfreich?

Lösung

Ich fand diesen Artikel von Dr. WPF ., die auf meine Frage mit einer Antwort beginnt, bewegt sich dann auf, um die Auswirkungen auf die Leistung des Aufrufs Refresh zu diskutieren. Einige der wichtigsten Auszüge:

  

Leider ist die Refresh () Methode führt zu einer vollständigen Regeneration der Ansicht. Wenn ein Refresh () in der Ansicht auf, wirft er eine Benachrichtigung Collection und liefert die Aktion als „Reset“. Der ItemContainerGenerator für die List-Box empfängt diese Meldung und reagiert darauf, indem alle vorhandenen Visuals für die Elemente zu verwerfen. Es dann regeneriert völlig neue Elementcontainer und Visuals.

Er beschreibt dann eine Hacky Abhilfe, die Leistung verbessert. Anstatt Refresh aufrufen, entfernen, ändern Sie das Element erneut hinzuzufügen.

Ich habe gedacht, es möglich, dass die Listenansicht ein Element verfolgen könnte, dass Änderungen und wissen, dass die Artikel allein wieder Position innerhalb der Ansicht.

Ein neuer Ansatz wurde in .NET 3.5 SP1 die Schnittstelle IEditableObject beteiligt, die über Datenbindungen in die Vorlage mit Methoden BeginEdit(), CancelEdit() und EndEdit() Transaktionsbearbeitung zur Verfügung stellt. Lesen Sie Artikel für weitere Informationen.

Bearbeiten Wie user346528 weist darauf hin , IEditableObject war in der Tat nicht neu in 3.5SP1. Es sieht tatsächlich aus wie es im Rahmen seit 1.0 .

Andere Tipps

Wie die akzeptierte Antwort mich lenkt, ich bin in der Lage mit dem Code einzelnes Element neu positionieren zu zwingen

IEditableCollectionView collectionView = DataGrid.Items;

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

Wo changedItem ist das Ansicht Modell (das Element in der ItemsSource Sammlung).

Auf diese Weise brauchen Sie nicht Ihre Artikel alle Schnittstellen wie IEditableObject zu implementieren (was meiner Meinung nach sehr komplex ist und hart Vertrag in einigen Fällen zu implementieren).

eine alte Post Bumping, sondern nur eine neue Kollektion Klasse machen, die von ListViewCollection und Überschreibungen OnPropertyChanged erbt (für eine IBindingList, Listchanged Ereignisse werden die Eigenschaftsänderung im ListChangedEventArgs Parameter enthalten). Und stellen Sie sicher, dass die Einzelteile innerhalb der Kollektion implementiert und verwendet INotifyPropertyChange wenn sich ein Eigenschaftsänderungen (von Ihnen erhoben), oder die Sammlung nicht auf Eigenschaftsänderungen binden.

Dann in dieser OnPropertyChanged Methode wird Absender das Element sein. Entfernen Sie die Option, wenn - und nur wenn - eine Eigenschaft, die ein Resort würde dazu führen, geändert wird, dann erneut hinzufügen (setzen Sie sie in sortierter Position es, wenn das Hinzufügen nicht tun dies bereits). ein Element bewegt, ist bevorzugt, wenn es statt Entfernen / Hinzufügen es verfügbar ist. In ähnlicher Weise sollte dies auch mit Filtern (Überprüfung der Filterprädikat) durchgeführt werden.

IEditableObject ist nicht erforderlich! Wenn Sie mehrere Eigenschaften bearbeiten wollen, dann der Bearbeitung fertig sind (wie Bearbeitung 3 Eigenschaften und dann auf einer anderen Zeile in WinForm Datagridview-Auswahl), dann sortieren, die, würde dies die richtige Methode des Erhaltens dies funktioniert. Aber viele Male werden Sie wahrscheinlich die Sammlung nach zurückgreifen wollen jede Eigenschaft zu ändern, ohne manuell zu BeginEdit / EndEdit nennen. IEditableObject, btw ist, in dem 2.0-Framework .NET und ist nicht neu in .NET 3.5 (wenn Sie den von Dr Artikel lesen).

Hinweis: Probleme können mit BeginEdit () und EndEdit () mit mehreren Änderungen an demselben Element auftreten - es sei denn, Sie (für true) erhöhen / Abnahme (für falsch) eine ganze Zahl stattdessen einen Booleschen der Einstellung! Denken Sie daran, erhöht / verringert eine ganze Zahl, um wirklich wissen, wann die Bearbeitung beendet ist.

eine ständig sortierte Liste zu halten, ist zeitaufwendig und fehleranfällig (und es kann den Einsatz Vertrag brechen, wenn Sie sortierte Einsätze zwingen), und sollte nur an bestimmten Orten wie Comboboxen verwendet werden. An jedem Raster ist, das ist eine sehr schlechte Idee, wie eine Zeile zu ändern bewirkt, dass es unter dem Benutzer aktuellen Position zurückgreifen werden.

Public Class ListView
  Inherits ListCollectionView

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

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

Das beste Beispiel für eine Sammlung, die ähnlich tut, was Sie brauchen, ist Jesse Johnsons Object, obwohl dies .NET 2.0 spezifisch (IBindingList statt INotifyCollectionChanged / ObservableCollection / Listcollectionview) ist und verwendet eine sehr restriktive Lizenz. Sein Blog kann immer noch sehr wertvoll sein, wie er dies erreicht.

Edit:

vergessen, dass Absender hinzufügen wird das das Element sein, das Sie zurückgreifen zu müssen, und e.PropertyName ist, was Sie verwenden müssen, um festzustellen, ob es innerhalb der SortDescriptions ist. Wenn es nicht, eine Änderung ist, wird auf diese Eigenschaft nicht dazu führen, in einem Resort benötigt wird. Wenn e.PropertyName Nichts ist, dann ist es genau wie ein Refresh, wo viele Eigenschaften geändert haben können und Umsortierung getan werden sollte.

Um festzustellen, ob es gefiltert werden muss, führen Sie es einfach durch die FilterPredicate, und entfernen Sie es, wenn nötig. Das Filtern ist viel weniger teuer als zu sortieren.

hoffentlich hilfreiche,

TamusJRoyce

Da Sie benutzerdefinierte Sortierung verwenden, gibt es keine Möglichkeit für die ListCollectionView zu wissen, welche Kriterien eine Aktualisierung auslösen soll. Daher müssen Sie Refresh() auf der Sammlung rufen selbst anzuzeigen.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top