Re-sort WPF DataGrid après que les données limitées ont changé

je suis à la recherche d'un moyen de re-trier mon DataGrid lorsque les données sous-jacentes modifié.

(le paramètre est assez standard: le DataGrid est ItemSource la propriété est liée à un ObservableCollection; Les colonnes sont DataGridTextColumns; les données à L'intérieur du DataGrid réagissent correctement sur les changements à l'intérieur de la collection observable; le tri fonctionne très bien lorsque cliqué avec la souris)

des idées ?

25
demandé sur slugster 2012-07-16 17:22:51

6 réponses

il m'a fallu toute l'après-midi mais j'ai finalement trouvé une solution qui est étonnamment simple,court et efficace:

pour contrôler les comportements du contrôle de L'UI en question (ici a DataGrid) on peut simplement utiliser un CollectionViewSource. Il agit comme une sorte de Représentant pour le contrôle UI à l'intérieur de votre modèle de vue sans briser complètement le modèle MVMM.

dans le modèle de vue déclarer à la fois CollectionViewSource et ordinaire ObservableCollection<T> et l'envelopper CollectionViewSourceObservableCollection:

// 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;
}

alors dans la partie View de l'application vous n'avez rien d'autre à faire pour lier le ItemsSourceCollectionControl à la propriété View de CollectionViewSource au lieu de <à la!--8-->:

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

à Partir de ce point vous pouvez utiliser le CollectionViewSource object dans votre modèle de vue pour manipuler directement le contrôle de L'interface utilisateur dans la vue.

Tri par exemple - comme cela a été mon le problème principal - devrait ressembler à ceci:

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

Vous voyez, très très simple et intuitive. J'espère que ça aidera les autres comme ça m'a aidé.

26
répondu marc wellman 2016-07-13 15:04:56

C'est plus pour les éclaircissements que c'est une réponse, mais WPF toujours se lie à un ICollectionView et pas la collection source. CollectionViewSource est juste un mécanisme utilisé pour créer/récupérer la vue collection.

Voici une excellente ressource sur le sujet qui devrait vous aider à faire un meilleur usage des vues de collection dans WPF:http://bea.stollnitz.com/blog/?p=387

en utilisant CollectionViewSource dans XAML peut réellement simplifier votre code quelques:

<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>

certaines personnes soutiennent qu'en suivant le modèle MVVM, le modèle view devrait toujours exposer la vue collection, mais à mon avis, Cela dépend juste du cas d'utilisation. Si le modèle de vue ne va jamais interagir directement avec la vue collection, il est juste plus facile de le configurer dans XAML.

17
répondu sellmeadog 2014-02-19 18:26:06
2
répondu AnjumSKhan 2016-09-11 06:56:18

la réponse de sellmeadog est soit trop compliquée, soit périmée. C'est super simple. Tout ce que vous avez à faire c'est:

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

<DataGrid ItemsSource="{Binding Source={StaticResource MyKey} }" >...
1
répondu Anthony Nichols 2016-12-07 23:35:27

Je ne vois pas de façon évidente facile, donc je voudrais essayer un comportement attaché. C'est un peu d'un métissage, mais vous donnera ce que vous voulez:

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()
    }
}

Ensuite, vous pouvez le joindre via:

<DataGrid.Style>
  <Style TargetType="DataGrid">
    <Setter 
      Property="AttachedProperties:DataGridAttachedProperty.ResortOnCollectionChangedProperty" 
                                    Value="true" 
      />
   </Style>
 </DataGrid.Style>
0
répondu Justin Pihony 2012-07-16 13:52:06

pour toute autre personne ayant ce problème, cela peut vous aider à démarrer... Si vous avez une collection D'articles INotifyPropertyChanged, vous pouvez l'utiliser à la place de ObservableCollection - il se rafraîchira lorsque des articles individuels dans la collection changer: note: puisque ceci signale les éléments supprimés puis lus (même s'ils ne sont pas réellement supprimés et ajoutés), les sélections peuvent se désynchroniser. Il est assez bon pour mes petits projets personnels, mais il n'est pas prêt à libérer à client...

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
répondu DanW 2016-06-14 16:15:06