webentwicklung-frage-antwort-db.com.de

Sortieren Sie WPF DataGrid neu, nachdem sich die gebundenen Daten geändert haben

Ich suche nach einem Weg, um neu sortieren meine DataGrid, wenn die zugrunde liegenden Daten geändert haben .

(Die Einstellung ist ziemlich normal: Die ItemSource-Eigenschaft des DataGrid ist an eine ObservableCollection gebunden. Die Spalten sind DataGridTextColumns; Die Daten innerhalb des DataGrid reagieren korrekt auf Änderungen in der ObservableCollection; das Sortieren funktioniert gut, wenn Sie mit der Maus darauf klicken.

Irgendwelche Ideen ?

26
marc wellman

Es hat den ganzen Nachmittag gedauert, aber ich habe endlich eine Lösung gefunden das ist überraschend einfach , kurz und effizient :

Um das Verhalten des fraglichen UI-Steuerelements (hier ein DataGrid) zu steuern, kann man einfach ein CollectionViewSource verwenden. Es fungiert als eine Art Repräsentant für die Benutzeroberflächensteuerung in Ihrem ViewModel, ohne das MVMM-Muster vollständig zu brechen.

Deklarieren Sie im ViewModel sowohl einen CollectionViewSource als auch einen gewöhnlichen ObservableCollection<T> und wickeln Sie den CollectionViewSource um den ObservableCollection:

// Gets or sets the CollectionViewSource
public CollectionViewSource ViewSource { get; set; }

// Gets or sets the ObservableCollection
public ObservableCollection<T> Collection { get; set; }

// Instantiates the objets.
public ViewModel () {

    this.Collection = new ObservableCollection<T>();
    this.ViewSource = new CollectionViewSource();
    ViewSource.Source = this.Collection;
}

Dann haben Sie im View-Teil der Anwendung nichts weiter zu tun, als das ItemsSource des CollectionControl an die View-Eigenschaft des CollectionViewSource zu binden, anstatt direkt an das ObservableCollection:

<DataGrid ItemsSource="{Binding ViewSource.View}" />

Ab diesem Zeitpunkt können Sie das Objekt CollectionViewSource in Ihrem ViewModel verwenden, um das UI-Steuerelement in der Ansicht direkt zu bearbeiten.

Das Sortieren zum Beispiel - wie es mein Hauptproblem war - würde so aussehen:

// Specify a sorting criteria for a particular column
ViewSource.SortDescriptions.Add(new SortDescription ("columnName", ListSortDirection.Ascending));

// Let the UI control refresh in order for changes to take place.
ViewSource.View.Refresh();

Sie sehen, sehr, sehr einfach und intuitiv. Hoffe, dass dies anderen Menschen hilft, wie es mir geholfen hat.

28
marc wellman

Dies ist mehr zur Verdeutlichung als eine Antwort, aber WPF always bindet an eine ICollectionView und nicht an die Quellensammlung. CollectionViewSource ist nur ein Mechanismus zum Erstellen/Abrufen der Auflistungsansicht.

Hier finden Sie eine großartige Ressource zu diesem Thema, die Ihnen helfen soll, die Sammlungsansichten in WPF besser zu nutzen: http://bea.stollnitz.com/blog/?p=387

Die Verwendung von CollectionViewSource in XAML kann den Code tatsächlich vereinfachen:

<Window.Resources>
    <CollectionViewSource Source="{Binding MySourceCollection}" x:Key="cvs">
      <CollectionViewSource.SortDescriptions>
        <scm:SortDescription PropertyName="ColumnName" />
      </CollectionViewSource.SortDescriptions>
    </CollectionViewSource>
</Window.Resources>

...

<DataGrid ItemsSource="{Binding Source={StaticResource cvs}}">
</DataGrid>

Einige Leute argumentieren, dass das Ansichtsmodell immer die Ansicht der Sammlung anzeigen sollte, wenn es dem MVVM-Muster folgt, aber meiner Meinung nach hängt es nur vom Anwendungsfall ab. Wenn das Ansichtsmodell niemals direkt mit der Sammlungsansicht interagiert, ist es einfacher, es in XAML zu konfigurieren.

17
sellmeadog
2
AnjumSKhan

Die Antwort von sellmeadog ist entweder zu kompliziert oder veraltet. Es ist super einfach. Alles was du tun musst, ist:

<UserControl.Resources>
    <CollectionViewSource 
        Source="{Binding MyCollection}" 
        IsLiveSortingRequested="True" 
        x:Key="MyKey" />
</UserControl.Resources>

<DataGrid ItemsSource="{Binding Source={StaticResource MyKey} }" >...
1
Anthony Nichols

Ich kann keine offensichtlich einfachen Wege erkennen, also würde ich ein Attached Behavior ausprobieren. Es ist ein bisschen wie eine Bastardisierung, aber es gibt dir, was du willst:

public static class DataGridAttachedProperty
{
     public static DataGrid _storedDataGrid;
     public static Boolean GetResortOnCollectionChanged(DataGrid dataGrid)
     {
         return (Boolean)dataGrid.GetValue(ResortOnCollectionChangedProperty);
     }

     public static void SetResortOnCollectionChanged(DataGrid dataGrid, Boolean value)
     {
         dataGrid.SetValue(ResortOnCollectionChangedProperty, value);
     }

    /// <summary>
    /// Exposes attached behavior that will trigger resort
    /// </summary>
    public static readonly DependencyProperty ResortOnCollectionChangedProperty = 
         DependencyProperty.RegisterAttached(
        "ResortOnCollectionChangedProperty", typeof (Boolean),
         typeof(DataGridAttachedProperty),
         new UIPropertyMetadata(false, OnResortOnCollectionChangedChange));

    private static void OnResortOnCollectionChangedChange
        (DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
    {
      _storedDataGrid = dependencyObject as DataGrid;
      if (_storedDataGrid == null)
        return;

      if (e.NewValue is Boolean == false)
        return;

      var observableCollection = _storedDataGrid.ItemsSource as ObservableCollection;
      if(observableCollection == null)
        return;
      if ((Boolean)e.NewValue)
        observableCollection.CollectionChanged += OnCollectionChanged;
      else
        observableCollection.CollectionChanged -= OnCollectionChanged;
    }

    private static void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
    {
      if (e.OldItems == e.NewItems)
        return;

      _storedDataGrid.Items.Refresh()
    }
}

Dann können Sie es anhängen:

<DataGrid.Style>
  <Style TargetType="DataGrid">
    <Setter 
      Property="AttachedProperties:DataGridAttachedProperty.ResortOnCollectionChangedProperty" 
                                    Value="true" 
      />
   </Style>
 </DataGrid.Style>
0
Justin Pihony

Für alle anderen Benutzer, die dieses Problem haben, kann dies den Einstieg erleichtern ... Wenn Sie über eine Auflistung von INotifyPropertyChanged-Elementen verfügen, können Sie dies anstelle von ObservableCollection verwenden. Diese wird aktualisiert, wenn sich einzelne Elemente in der Auflistung ändern: : Da dadurch gekennzeichnet wird, dass die Elemente als entfernt und dann gelesen werden (obwohl sie nicht wirklich entfernt und hinzugefügt werden), kann die Auswahl möglicherweise nicht mehr synchronisiert werden. Es ist gut genug für meine kleinen persönlichen Projekte, aber es ist nicht bereit, es an Kunden weiterzugeben ...

public class ObservableCollection2<T> : ObservableCollection<T>
{
    public ObservableCollection2()
    {
        this.CollectionChanged += ObservableCollection2_CollectionChanged;
    }

    void ObservableCollection2_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        if (e.OldItems != null)
            foreach (object o in e.OldItems)
                remove(o);
        if (e.NewItems != null)
            foreach (object o in e.NewItems)
                add(o);
    }
    void add(object o)
    {
        INotifyPropertyChanged ipc = o as INotifyPropertyChanged;
        if(ipc!=null)
            ipc.PropertyChanged += Ipc_PropertyChanged;
    }
    void remove(object o)
    {
        INotifyPropertyChanged ipc = o as INotifyPropertyChanged;
        if (ipc != null)
            ipc.PropertyChanged -= Ipc_PropertyChanged;
    }
    void Ipc_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        NotifyCollectionChangedEventArgs f;

        f = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, sender);
        base.OnCollectionChanged(f);
        f = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, sender);
        base.OnCollectionChanged(f);
    }
}
0
DanW