【问题标题】:CollectionViewSource sorting stops working in DataGrid when DataContext is changed当 DataContext 更改时,CollectionViewSource 排序在 DataGrid 中停止工作
【发布时间】:2018-08-13 19:28:48
【问题描述】:

我有一个类“Widget”的集合,它具有 Name(字符串)和 Rank(int)属性。我在我的视图模型中创建了一个 CollectionViewSource 实例,该实例按 Rank 然后按名称声明排序顺序。在我的视图模型构造函数中,我创建了三个小部件,其中 Rank 产生的顺序与 Name 产生的顺序相反。

这是我的主视图模型代码:

class MainViewModel : INotifyPropertyChanged
{
   public event PropertyChangedEventHandler PropertyChanged;
   private void FirePropertyChanged(string property)
   {
      if (null != PropertyChanged)
         PropertyChanged(this, new PropertyChangedEventArgs(property));
   }

   private CollectionViewSource _widgetView;

   public CollectionViewSource WidgetView
   {
      get { return _widgetView; }
      set { _widgetView = value; }
   }

   public MainViewModel()
   {
      WidgetView = new CollectionViewSource();
      WidgetView.IsLiveSortingRequested = true;
      WidgetView.SortDescriptions.Add(new SortDescription("Rank", ListSortDirection.Ascending));
      WidgetView.SortDescriptions.Add(new SortDescription("Name", ListSortDirection.Ascending));
      WidgetView.Source = new ObservableCollection<Widget>()
      {
         new Widget() { Name = "W_1", Rank = 3 },
         new Widget() { Name = "W_2", Rank = 2 },
         new Widget() { Name = "W_3", Rank = 1 },
      }; ;
   }
}

我的 MainWindow.xaml 看起来像这样。

<Window x:Class="CollectionViewSortingBug.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:CollectionViewSortingBug"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <DockPanel>
      <Button DockPanel.Dock="Bottom" Content="New View Model" Name="newViewModelButton" Click="newViewModelButton_Click" />
      <DataGrid Name="dgv" ItemsSource="{Binding WidgetView.View}" AutoGenerateColumns="False" CanUserSortColumns="False">
         <DataGrid.Columns>
            <DataGridTextColumn Binding="{Binding Name}" Header="Name" />
            <DataGridTextColumn Binding="{Binding Rank}" Header="Rank" />
         </DataGrid.Columns>
      </DataGrid>
    </DockPanel>
</Window>

点击处理程序窗口后面的代码是这样的:

public partial class MainWindow : Window
{
   public MainWindow()
   {
      InitializeComponent();
   }

   private void newViewModelButton_Click(object sender, RoutedEventArgs e)
   {
      DataContext = new MainViewModel();
   }
}

最后,这里是 Widget.cs 代码:

class Widget : INotifyPropertyChanged
{
   public event PropertyChangedEventHandler PropertyChanged;
   private void FirePropertyChanged(string property)
   {
      if (null != PropertyChanged)
         PropertyChanged(this, new PropertyChangedEventArgs(property));
   }

   private string _name;

   public string Name
   {
      get { return _name; }
      set
      {
         _name = value;
         FirePropertyChanged("Name");
      }
   }

   private int _rank;

   public int Rank
   {
      get { return _rank; }
      set
      {
         _rank = value;
         FirePropertyChanged("Rank");
      }

   }
}

如您所见,当我单击按钮时,会构建一个新的 MainViewModel,并将 MainWindow 的 DataContext 分配给该新实例。我第一次单击按钮时,一切都按预期工作,这在我的表格中:

| Name | Rank |
|------|------|
| W_3  | 1    |
| W_2  | 2    |
| W_1  | 3    |

如果我编辑排名值,行将相应移动。例如,如果我将 W_2 的 Rank 设置为 10,它将移动到 W_1 以下。如果我将所有 Rank 值设置为相等,然后更改名称,则表格将按预期按新名称的字母顺序排序。

但是,如果我再次单击该按钮,表格现在将按名称排序。

| Name | Rank |
|------|------|
| W_1  | 3    |
| W_2  | 2    |
| W_3  | 1    |

并且更改排名名称值没有效果。就好像所有的排序逻辑都被清除了,实时排序根本不再发生。

为什么当 DataContext 发生变化时行为会有所不同?

更新: 我将 DataGrid 替换为具有类似功能的 ItemsControl(名称和排名的 TextBox),它继续按照我的预期使用 CollectionViewSource 进行排序(即它没有出现此问题)。所有其他代码都是一样的。这是我将 DataGrid 块替换为:

<ItemsControl Name="ic" ItemsSource="{Binding WidgetView.View}" DockPanel.Dock="Bottom">
   <ItemsControl.ItemTemplate>
      <DataTemplate DataType="{x:Type local:Widget}">
         <StackPanel Orientation="Horizontal">
            <TextBox Text="{Binding Name}" Width="100"/>
            <TextBox Text="{Binding Rank}"  Width="75"/>
         </StackPanel>
      </DataTemplate>
   </ItemsControl.ItemTemplate>
</ItemsControl>

奇怪的是,如果我将 both 控件留在其中(堆叠在 DockPanel 中),那么 both 控件的情况会再次中断。这可能是一个实际的错误吗?

【问题讨论】:

    标签: c# wpf datagrid collectionviewsource


    【解决方案1】:

    我想出了如何让它发挥作用,尽管感觉更像是一种变通方法而不是正确的答案。

    如果我从 MainViewModel 构造函数中删除以下两行:

    WidgetView.SortDescriptions.Add(new SortDescription("Rank", ListSortDirection.Ascending));
    WidgetView.SortDescriptions.Add(new SortDescription("Name", ListSortDirection.Ascending));
    

    而是在 MainWindow.xaml.cs 的点击处理程序中添加 SortDescriptions,然后我得到预期的行为。现在我的点击处理程序方法如下所示:

    private void newViewModelButton_Click(object sender, RoutedEventArgs e)
    {
        var mvm = new MainViewModel();
        DataContext = mvm;
        mvm.WidgetView.SortDescriptions.Add(new SortDescription("Rank", ListSortDirection.Ascending));
        mvm.WidgetView.SortDescriptions.Add(new SortDescription("Name", ListSortDirection.Ascending));
    }
    

    由于某种原因,它适用于第一次点击和所有后续点击;当我相应地修改Rank 和/或Name 时,表格会按我的预期更新,并且顺序会发生变化。我仍然不明白为什么它第一次工作,但如果 SortDescriptions 在构造函数中更新,则在后续点击后失败。

    我的理论是,DataGrid 中可能存在某种绑定,如果SortDescriptions 集合发生更改,则会触发该绑定,并且该触发会导致排序工作。因此,如果在设置DataContextSortDescriptions 没有改变,那么DataGridView 不知何故错过了它应该排序的事实。为了测试这个理论,我将代码改回为在构造函数中添加了SortDescriptions,然后在DataContext 分配之后基于单击处理程序中不存在的属性“Foo”添加了一个新的SortDescription。这解决了这个问题(虽然它不是我推荐的最终修复)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-08-28
      • 2015-11-16
      • 2012-01-16
      • 2011-01-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多