c# - filtrar - Vuelva a ordenar WPF DataGrid después de que los datos hayan sido limitados.
filtrar datagridview c# (6)
Esto es más una aclaración que una respuesta, pero WPF siempre se une a un ICollectionView
y no a la colección fuente. CollectionViewSource
es solo un mecanismo utilizado para crear / recuperar la vista de colección.
Aquí hay un gran recurso sobre el tema que debería ayudarlo a hacer un mejor uso de las vistas de colección en WPF: http://bea.stollnitz.com/blog/?p=387
Usar CollectionViewSource
en XAML en realidad puede simplificar su código:
<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>
Algunas personas argumentan que al seguir el patrón MVVM, el modelo de vista siempre debe exponer la vista de colección, pero en mi opinión, solo depende del caso de uso. Si el modelo de vista nunca va a interactuar directamente con la vista de colección, es más fácil configurarlo en XAML.
Estoy buscando una forma de volver a ordenar mi DataGrid
cuando los datos subyacentes hayan cambiado .
(La configuración es bastante estándar: la propiedad ItemSource
de DataGrid está vinculada a una ObservableCollection
; Las columnas son DataGridTextColumns
; Los datos dentro de DataGrid reaccionan correctamente sobre los cambios dentro de ObservableCollection; La ordenación funciona bien cuando se hace clic con el mouse)
Algunas ideas ?
La respuesta de sellmeadog es demasiado complicada o desactualizada. Es super simple Todo lo que tienes que hacer es:
<UserControl.Resources>
<CollectionViewSource
Source="{Binding MyCollection}"
IsLiveSortingRequested="True"
x:Key="MyKey" />
</UserControl.Resources>
<DataGrid ItemsSource="{Binding Source={StaticResource MyKey} }" >...
Me tomó toda la tarde pero finalmente encontré una solución que es sorprendentemente simple , corta y eficiente :
Para controlar los comportamientos del control de la interfaz de usuario en cuestión (aquí, un DataGrid
) uno simplemente podría usar un CollectionViewSource
. Actúa como un tipo de representante para el control de la interfaz de usuario dentro de su ViewModel sin romper completamente el patrón de MVMM.
En ViewModel, declara un CollectionViewSource
y un ordinario ObservableCollection<T>
y ajusta el CollectionViewSource
alrededor del 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;
}
Luego, en la parte Vista de la aplicación, no tiene nada más que hacer para vincular el ItemsSource
de CollectionControl
a la propiedad View de CollectionViewSource
lugar de vincularlo directamente a ObservableCollection
:
<DataGrid ItemsSource="{Binding ViewSource.View}" />
A partir de este punto, puede usar el objeto CollectionViewSource
en su ViewModel para manipular directamente el control de UI en la Vista.
Clasificar por ejemplo, como ha sido mi problema principal, se vería así:
// 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();
Ya ves, muy, muy simple e intuitivo. Espero que esto ayude a otras personas me gusta me ayudó.
No puedo ver ninguna manera obviamente fácil, así que probaría un comportamiento adjunto. Es un poco bastardeante, pero te dará lo que quieras:
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()
}
}
Luego, puede adjuntarlo a través de:
<DataGrid.Style>
<Style TargetType="DataGrid">
<Setter
Property="AttachedProperties:DataGridAttachedProperty.ResortOnCollectionChangedProperty"
Value="true"
/>
</Style>
</DataGrid.Style>
Para cualquier otra persona que tenga este problema, esto puede ayudarlo a comenzar ... Si tiene una colección de elementos INotifyPropertyChanged, puede usar esto en lugar de ObservableCollection; se actualizará cuando cambien los elementos individuales en la colección: nota: ya que esta marca los elementos como eliminados y luego readded (aunque no se eliminen ni se agreguen), las selecciones pueden perder sincronía. Es lo suficientemente bueno para mis pequeños proyectos personales, pero no está listo para lanzarlo a los clientes ...
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);
}
}