更改DataContext后,CollectionViewSource排序将在DataGrid中停止工作

时间:2018-08-13 19:28:49

标签: c# wpf datagrid collectionviewsource

我有一个类“ Widget”的集合,该类具有属性Name(字符串)和Rank(int)。我正在我的视图模型中创建一个CollectionViewSource实例,该实例按Rank然后按Name声明排序顺序。在我的视图模型构造函数中,我正在创建三个小部件,这些小部件中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    |

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

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

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

并且更改等级名称值无效。好像所有排序逻辑都已清除,实时排序根本不再发生。

当DataContext更改时,行为为何不同?

更新: 我用具有类似功能(名称和等级的文本框)的ItemsControl替换了DataGrid,并且它继续按我希望的方式通过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 控件又会坏掉。这可能是一个实际的错误吗?

1 个答案:

答案 0 :(得分:0)

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

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

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

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

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集合发生更改,将触发该绑定,并且该触发会导致排序工作。因此,如果SortDescriptions在设置{{1}之后没有改变,那么DataContext就会以某种方式错过应该进行排序的事实。为了验证该理论,我将代码改回为在构造函数中添加DataGridView,然后在{{1之后,在单击处理程序中基于不存在的属性“ Foo”添加了新的SortDescriptions }}分配。此解决了该问题(尽管这不是我建议的最终解决方法)。