当基础数据发生变化时,我正在寻找一种重新排序的方法。DataGrid
(设置非常标准:DataGrid 的ItemSource
属性绑定到ObservableCollection
; 列是DataGridTextColumns
; DataGrid 内的数据对 ObservableCollection 内的更改做出正确反应;用鼠标单击时排序工作正常)
有任何想法吗 ?
当基础数据发生变化时,我正在寻找一种重新排序的方法。DataGrid
(设置非常标准:DataGrid 的ItemSource
属性绑定到ObservableCollection
; 列是DataGridTextColumns
; DataGrid 内的数据对 ObservableCollection 内的更改做出正确反应;用鼠标单击时排序工作正常)
有任何想法吗 ?
我花了整个下午,但我终于找到了一个非常简单、简短和高效的解决方案:
要控制相关 UI 控件(此处为 a DataGrid
)的行为,可以简单地使用 a CollectionViewSource
。它充当 ViewModel 中 UI 控件的一种代表,而不会完全破坏 MVMM 模式。
在 ViewModel 中声明 aCollectionViewSource
和一个普通的ObservableCollection<T>
并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;
}
然后在应用程序的 View 部分中,您无需将 的 绑定ItemsSource
到CollectionControl
View 的属性,CollectionViewSource
而不是直接绑定到ObservableCollection
:
<DataGrid ItemsSource="{Binding ViewSource.View}" />
从此时起,您可以使用CollectionViewSource
ViewModel 中的对象直接操作 View 中的 UI 控件。
例如排序 - 这是我的主要问题 - 看起来像这样:
// 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();
你看,非常非常简单和直观。希望这可以帮助其他喜欢它帮助我的人。
这更多是为了澄清而不是答案,但 WPF始终绑定到一个ICollectionView
而不是源集合。CollectionViewSource
只是一种用于创建/检索集合视图的机制。
这是关于该主题的一个很好的资源,可以帮助您更好地利用 WPF 中的集合视图:http ://bea.stollnitz.com/blog/?p=387
在 XAML 中使用CollectionViewSource
实际上可以简化一些代码:
<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>
有人争辩说,当遵循 MVVM 模式时,视图模型应该始终公开集合视图,但在我看来,这仅取决于用例。如果视图模型永远不会直接与集合视图交互,那么在 XAML 中配置它会更容易。
sellmeadog 的答案要么过于复杂,要么已经过时。超级简单。你所要做的就是:
<UserControl.Resources>
<CollectionViewSource
Source="{Binding MyCollection}"
IsLiveSortingRequested="True"
x:Key="MyKey" />
</UserControl.Resources>
<DataGrid ItemsSource="{Binding Source={StaticResource MyKey} }" >...
我看不到任何明显简单的方法,所以我会尝试附加行为。这有点混蛋,但会给你你想要的:
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()
}
}
然后,您可以通过以下方式附加它:
<DataGrid.Style>
<Style TargetType="DataGrid">
<Setter
Property="AttachedProperties:DataGridAttachedProperty.ResortOnCollectionChangedProperty"
Value="true"
/>
</Style>
</DataGrid.Style>
对于遇到此问题的其他任何人,这可能会让您入门...如果您有 INotifyPropertyChanged 项目的集合,则可以使用它而不是 ObservableCollection - 当集合中的单个项目更改时它将刷新:注意:因为这会标记项目删除然后读取(即使它们实际上并未被删除和添加)选择可能会不同步。对于我的小型个人项目来说已经足够了,但它还没有准备好发布给客户......
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);
}
}