由INotifyPropertyChanged.PropertyChanged清除的SortDescriptions

时间:2016-03-25 19:37:33

标签: wpf mvvm collections binding

当我触发我的视图模型的INotifyPropertyChanged.PropertyChanged事件时,我看到我的ICollectionView.SortDescriptions列表被清除,弄乱了我的DataGrid中的排序。

我有一个DataGrid绑定到我的视图模型的People属性:

        <DataGrid Margin="23,17,21,66" x:Name="dataGrid1" DockPanel.Dock="Top"
            AlternatingRowBackground="LightGray" Background="DarkGray" 
            AutoGenerateColumns="False"  ItemsSource ="{Binding Path=People}" 
            CanUserAddRows="False"
            Loaded="DataGrid1_OnLoaded"
            SelectionMode="Single"
         >

People属性只返回Person个对象的列表:

    public IEnumerable<Person> People
    {
        get
        {
            int index = 0;
            foreach (Tuple<string, string, bool> personData in repository.PersonData)
            {
                yield return new Person()
                {
                    FirstName = personData.Item1,
                    LastName = personData.Item2,
                    Male = personData.Item3,
                    Index = ++index
                };
            }
        }
    }

我将SortDescription添加到DataGrid的CollectionView中:

    private void DataGrid1_OnLoaded(object sender, RoutedEventArgs e)
    {
        // Do sorting
        ICollectionView cv = CollectionViewSource.GetDefaultView(dataGrid1.ItemsSource);
        SortDescription sd = new SortDescription("LastName", ListSortDirection.Ascending);
        cv.SortDescriptions.Clear();
        cv.SortDescriptions.Add(sd);
    }

最后,我有一个按钮,将Person对象添加到我的ViewModel中的People列表中:

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        ViewModel vm = dataGrid1.DataContext as ViewModel;
        Person addPerson = new Person() { FirstName = "Greg", LastName = "Quick", Male = true };
        ICollectionView cv = CollectionViewSource.GetDefaultView(dataGrid1.ItemsSource);
        Console.WriteLine(String.Format("Before add: Number of SortDescriptions = {0}", cv.SortDescriptions.Count));
        vm.AddPerson(addPerson);
        Console.WriteLine(String.Format("After add: Number of SortDescriptions = {0}", cv.SortDescriptions.Count));
        //cv.Refresh();

    }

public void AddPerson(Person person)
{
    repository.PersonData.Add(new Tuple<string,string,bool>(person.FirstName, person.LastName, person.Male));
    PropertyChanged(this, new PropertyChangedEventArgs("People"));
}

但显然,PropertyChanged的触发会清除CollectionView中的SortDescriptions,正如WriteLine中所见。当然,这会混淆DataGrid中的排序。

我有解决方法,比如重新添加SortDescription。但是我想知道这是否是预期的行为,如果有办法防止SortDescription被删除的话。

3 个答案:

答案 0 :(得分:2)

您遇到的场景完全符合预期,因为您在添加person对象时重新绑定ItemsSource。即使您没有添加Person对象,只是为Person对象引发属性更改事件,您也会看到相同的行为。

上述声明背后的一些推理:

每当我们绑定一个UI元素的ItemsSource时,WPF会在内部创建ICollectionView的对象而不是源集合,并将ItemsSource与它绑定。它可以是ListCollectionView,EnumerableCollectionView或BindingListCollectionView,具体取决于源集合实现的接口类型(在您的情况下,它将是EnumerableCollectionView,因为People是IEnumerbale)

话虽如此,每当重新创建ItemsSource时,WPF也会重新创建ICollectionView对象(显然它不会在之前的对象上设置SortDescriptions)。您可以通过此代码示例验证:

ICollectionView cv = CollectionViewSource.GetDefaultView(dg.ItemsSource);
vm.AddObject(person);
ICollectionView cv1 = CollectionViewSource.GetDefaultView(dg.ItemsSource);
bool areEqual = cv == cv1; // This will output false.

现在,您可能有疑问:

  

如果创建了新的CollectionView对象,那么为什么要删除SortDescriptions   来自旧的collectionView对象?

在ItemsSource更改事件上,只要ItemsSource发生更改,datagrid就会清除其SortDescriptions。如果你通过反射器查看dataGrid代码,这就是你得到的:

private static object OnCoerceItemsSourceProperty(DependencyObject d, object baseValue) 
{
    DataGrid dataGrid = (DataGrid)d; 
    if (baseValue != dataGrid._cachedItemsSource && dataGrid._cachedItemsSource != null)
    {
        dataGrid.ClearSortDescriptionsOnItemsSourceChange();
        // Responsible for clearing Sort Descriptions.
    }
    return baseValue; 
 }

因此,解决问题的方法是再次在collectionView对象上设置SortDescriptions(请记住将其取回,因为旧的cv对象不再绑定了)或者理想的解决方案是不要#39 ; t再次重新创建列表。使用ObservableCollection或在列表中添加对象,并在集合视图对象上调用Refresh()。

答案 1 :(得分:0)

尝试People ObservableCollection<Person>

然后,当创建新Person时,只需将其添加到People并让ObservableCollection处理通知。以与您现在相同的方式创建SortDescriptions。如果您没有使用新对象替换People,则SortDescriptions不应更改。在我掀起的测试代码中,至少它并没有改变我。

在WPF中,将不断变化的对象集合暴露给视图的规范方法是ObservableCollection

视图模型。请注意,我在构造函数中设置People并保持相同的ObservableCollection实例。我添加了东西,我可以从中删除东西,但它仍然是相同的集合实例。

public class ViewModel
{
    public ViewModel()
    {
        People = new ObservableCollection<Person>
        {
            new Person { FirstName = "Bob", LastName = "Dobbs", Male = true },
            new Person { FirstName = "Hal", LastName = "Lindsey", Male = true },
            new Person { FirstName = "Alexander", LastName = "The Great", Male = true },
            new Person { FirstName = "Boris", LastName = "Karloff", Male = true },
        };
    }

    public ObservableCollection<Person> People { get; private set; }
}

出于所有实际目的,代码隐藏和XAML与您的相同

private void Button_Click(object sender, RoutedEventArgs e)
{
    (DataContext as ViewModel).People.Add(new Person
    {
        FirstName = "Fred",
        LastName = "Flintstone",
        Male = true
    });
}

当我点击按钮时,Fred在Dobbs和Karloff之间的列表中弹出第二个。

答案 2 :(得分:0)

这个东西真的很复杂,我不想花时间确保答案绝对是你想要的,但是有以下帮助吗?

dgrApplications.ColumnFromDisplayIndex(0).SortDirection = ListSortDirection.Ascending