当我触发我的视图模型的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被删除的话。
答案 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