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 ?
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 CollectionViewSource
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;
}
alors dans la partie View de l'application vous n'avez rien d'autre à faire pour lier le ItemsSource
CollectionControl
à 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é.
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.
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} }" >...
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>
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);
}
}