有界数据更改后重新排序 WPF DataGrid

问题描述 投票:0回答:5

我正在寻找一种方法,当基础数据更改时,

重新排序
我的DataGrid

(设置非常标准:DataGrid 的

ItemSource
属性绑定到
ObservableCollection
;列是
DataGridTextColumns
;DataGrid 内的数据对 ObservableCollection 内的更改做出正确反应;用鼠标单击时排序工作正常)

有什么想法吗?

c# wpf data-binding datagrid collectionviewsource
5个回答
32
投票

花了我整个下午的时间,但我终于找到了一个解决方案,令人惊讶的是简单高效

要控制相关 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;
}

然后,在应用程序的“视图”部分中,您无需执行任何其他操作,只需将

ItemsSource
CollectionControl
绑定到
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();

你看,非常非常简单直观。希望这能帮助其他人,就像帮助我一样。


21
投票

这更多的是为了澄清而不是答案,但 WPF always 绑定到

ICollectionView
而不是源集合。
CollectionViewSource
只是一种用于创建/检索集合视图的机制。

这里有一个关于该主题的很棒的资源,可以帮助您更好地使用 WPF 中的集合视图:http://bea.stollnitz.com/blog/?p=387

在 XAML 中使用

CollectionViewSource
实际上可以简化你的代码:

<Window
    ...
    xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase">

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

有些人认为,在遵循 MVVM 模式时,视图模型应该始终公开集合视图,但在我看来,这仅取决于用例。如果视图模型永远不会直接与集合视图交互,那么在 XAML 中配置它会更容易。


6
投票

sellmeadog 的答案要么过于复杂,要么已经过时。这非常简单。您所要做的就是:

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

<DataGrid ItemsSource="{Binding Source={StaticResource MyKey} }" >...

0
投票

我看不到任何明显简单的方法,所以我会尝试附加行为。这有点混蛋,但会给你你想要的:

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>

-1
投票

对于遇到此问题的其他人,这可能会让您开始...... 如果您有 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);
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.