有界数据更改后重新排序 WPF DataGrid
Posted
技术标签:
【中文标题】有界数据更改后重新排序 WPF DataGrid【英文标题】:Re-sort WPF DataGrid after bounded Data has changed 【发布时间】:2012-07-15 08:35:51 【问题描述】:当基础数据更改时,我正在寻找一种方法来重新排序我的DataGrid
。
(设置非常标准:DataGrid 的 ItemSource
属性绑定到 ObservableCollection
;列是 DataGridTextColumns
;DataGrid 内的数据对 ObservableCollection 内的更改做出正确反应;单击时排序工作正常鼠标)
有什么想法吗?
【问题讨论】:
你探索过CollectionViewSource
吗?
@WPF-it 现在我做到了;) 你给了我正确的提示,我能够用CollectionViewSource
解决我的问题。看看我自己的答案,看看我做了什么。谢谢!
【参考方案1】:
sellmeadog 的回答要么过于复杂,要么已经过时。超级简单。您所要做的就是:
<UserControl.Resources>
<CollectionViewSource
Source="Binding MyCollection"
IsLiveSortingRequested="True"
x:Key="MyKey" />
</UserControl.Resources>
<DataGrid ItemsSource="Binding Source=StaticResource MyKey " >...
【讨论】:
请注意,IsLiveSortingRequested 需要 .NET 4.5 它如何知道按哪一列排序?它不需要某个地方的 CollectionViewSource SortDescription 吗? 其实你没有提到它是如何识别要排序的列的.. 根据问题,用户通过点击列标题选择排序。在这种情况下,无需在 CollectionViewSource 中定义排序。【参考方案2】:我花了整个下午,但我终于找到了一个解决方案,它出奇地简单、短和高效:
要控制相关 UI 控件的行为(这里是 DataGrid
),可以简单地使用 CollectionViewSource
。它充当 ViewModel 中 UI 控件的一种代表,而不会完全破坏 MVMM 模式。
在 ViewModel 中声明一个 CollectionViewSource
和一个普通的 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 部分中,您无需将 CollectionControl
的 ItemsSource
绑定到 CollectionViewSource
的 View 属性,而不是直接绑定到 ObservableCollection
:
<DataGrid ItemsSource="Binding ViewSource.View" />
从此时起,您可以在 ViewModel 中使用 CollectionViewSource
对象直接操作 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();
你看,非常非常简单和直观。希望这可以帮助其他喜欢它帮助我的人。
【讨论】:
这很棒。唯一的问题是,一旦将 ViewSource.Source 设置为 ObservableCollection,就不能再在 BackgroundWorker 中更改 ObservableCollection。或者至少我不能。 如果您将控件直接绑定到 ObservableCollection,也会发生这种情况。在这两种情况下,您实际上都可以对集合进行更改,只需在应用程序调度程序上调用一个操作。 嗨 Marc Wellman,我正在尝试使用 WPF 数据网格进行排序,但数据网格甚至没有显示我绑定到它的数据,更不用说排序了。在我使用 gridName.ItemsSource = myObservableCollection; 进行基本的非排序数据网格之前加上 gridName.Items.Refresh();并且网格使用这种方法正确加载数据,但是当我切换到你的数据网格时,数据网格根本没有显示任何数据。我正在考虑开一个新帖子,这样我就可以展示我的代码,然后参考这篇帖子,但不确定这是否是 *** 世界中的失礼。有什么建议吗? 它正在工作,但刷新需要很长时间。我在 DataGrid 中只有 43 条记录 我和@C有同样的问题【参考方案3】:对于遇到此问题的其他人,这可能会让您开始... 如果你有一个 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);
【讨论】:
【参考方案4】:这更多是为了澄清而不是答案,但 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 中配置它会更容易。
【讨论】:
非常感谢您的评论——确实对我帮助很大。 这是一个很好的建议——CollectionViewSource 应该是 View 背后代码中的一个属性(并且可以反过来绑定到 VM 上的 ObservableCollection),或者将其声明为本地静态资源正如你所做的那样。 这是最好的解决方案,您的 XAML 中只有一个错误 - 它是 SortDescription 元素中的“PropertyName”而不是“Property”。 谢谢!这有帮助。我只是很难弄清楚 scm 命名空间。这里是为试图找到它的人准备的:xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase" 那个链接失效了,但是我在The Internet Archive.找到了文字【参考方案5】:我看不到任何明显简单的方法,所以我会尝试附加行为。这有点混蛋,但会给你你想要的:
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>
【讨论】:
非常感谢您的详细回答,但不幸的是我无法使其工作。我设法将您的属性附加到我的 DataGrid 并在我的基础集合的每个CollectionChange
事件上调用 _storedDataGrid.ItemsCollection.Refresh()
。但由于某种原因,DataGrid 不会排序。我不确定,但可能是因为我使用了多个线程,并且在将刷新调用传播到 UIThread 时遇到了一些问题。无论如何,我找到了另一个很好的幸运解决方案 - 请参阅我自己的答案。不过,再次感谢您!以上是关于有界数据更改后重新排序 WPF DataGrid的主要内容,如果未能解决你的问题,请参考以下文章
在 DataTable 更改 WPF 后更新绑定到 DataTable 的 DataGrid