C# DataGrid无法取消选择已修改的项
我有一个带有DataGrid的WPF应用程序。 此DataGrid绑定到包含一组模型的ObservableCollection。 此DataGrid的选择模式设置为“扩展” 如果当前选择了某个项目,然后收到更新(例如,所选项目的某些内容在刷新期间发生更改),然后用户尝试选择另一个项目,则不会取消选择上一个项目 OnselectionChanged将激发,但不包含上一项,并且似乎无法取消选择它 运行此程序的代码(使用.net core 3.1清理wpf应用程序) DataGrid(如您所见)也不存储任何选定项C# DataGrid无法取消选择已修改的项,c#,.net,wpf,.net-core,C#,.net,Wpf,.net Core,我有一个带有DataGrid的WPF应用程序。 此DataGrid绑定到包含一组模型的ObservableCollection。 此DataGrid的选择模式设置为“扩展” 如果当前选择了某个项目,然后收到更新(例如,所选项目的某些内容在刷新期间发生更改),然后用户尝试选择另一个项目,则不会取消选择上一个项目 OnselectionChanged将激发,但不包含上一项,并且似乎无法取消选择它 运行此程序的代码(使用.net core 3.1清理wpf应用程序) DataGrid(如您所见)也不存
<DataGrid Grid.Row="1"
x:Name="ItemsDataGrid"
SelectionMode="Extended"
ItemsSource="{Binding Items}">
<DataGrid.Resources>
<Style TargetType="{x:Type DataGridCell}">
<Style.Triggers>
<Trigger Property="DataGridCell.IsSelected" Value="True">
<Setter Property="Background" Value="Red" />
</Trigger>
</Style.Triggers>
</Style>
</DataGrid.Resources>
</DataGrid>
根据请求,这里是将项目添加到列表框的代码
private void Button_Click(object sender, RoutedEventArgs e)
{
SelectedItemsListBox.Items.Clear();
foreach(var item in ItemsDataGrid.SelectedItems)
{
SelectedItemsListBox.Items.Add(item);
}
SelectedItemsListBox.Items.Refresh();
}
在这里,应用程序刚刚打开,单击“刷新”按钮显示3项
这里选择了第一个项目,单击了修改维度的“更改项目”按钮,还请注意此项目现在如何显示其下面的描述,这是一个列表框,显示所有当前选择的项目,并在单击“显示所选项目”后刷新(包括列表框.clear)
在这里,第三个项目已被单击并选中,反过来也应取消选中以前选中的项目,但如列表框所示,它仍然处于选中状态
我已经把这个问题缩减到了这一点,结果不是我的选择方法,不是我使用的多选择器,也不是我使用的UI框架,这个简单的解决方案仍然存在一个问题,让我不知所措,不知道它可能是什么。您观察到的行为与您的
GetHashCode
实现有关
从您发布的类定义(在Pastebin上)中,我了解到您的数据项正在实现IEquatable
,并且在此上下文中还重写object.GetHashCode
您的实现基于可变字段计算哈希代码这通常应该避免,因为它可能导致意外的行为(比如你现在正在经历的) “通常,对于可变引用类型,只有在以下情况下,才应重写GetHashCode():
- 您可以从不可变的字段计算哈希代码;或者
- 可以确保可变对象包含在依赖于的集合中时,其哈希代码不会更改 它的散列码。”
如果用于哈希代码计算的字段发生更改,则哈希代码也将发生更改,因此原始哈希键的存储值将丢失 现在您必须知道,
DataGrid
和Selector
通常使用哈希表来存储所选项目,以提高查找性能。由于您的类型实现了IEquatable
,DataGrid
试图使用GetHashCode
返回的值作为键,因为它假定一个重写的实现。GetHashCode
的这种实现在使用前由DataGrid
检查可靠性,但显然这种可靠性检查没有考虑用于计算的字段的可变性。当然,这需要反思。避免反射并在连续调用后测试GetHashCode
的结果是否为常量似乎是很合理的
考虑到这一点,我们现在可以解释这种行为:
- 项目被选中并存储在“选定项目”哈希表中
使用项本身作为基于哈希的选定项备份集合的键,以提高查找速度DataGrid
- 由于项实现了
,哈希表愉快地调用所选项上的IEquatable
,以获取对象哈希作为值(所选项)的键GetHashCode
- 现在,您可以通过编辑
的一个单元格来修改该项,这也会导致计算出的哈希代码发生意外的更改DataGrid
- 接下来,选择一个不同的项目。
现在尝试从selected items集合中删除以前选择的项。但由于项目的哈希代码已更改,因此查找不会返回任何项目。因此,旧的和取消选择的项目仍保留在“选定项目”集合中DataGrid
对象.GetHashCode
,因为在您的情况下:
- 无法从不可变(只读)字段和
- 当可变对象包含在依赖其哈希代码的集合中时,无法确保该对象的哈希代码不会更改
GetHashCode
覆盖将解决此问题
实施改进 添加类型为
ObservableCollection
的SelectedItems
集合作为列表框的绑定源,并添加数据网格。SelectionChanged
事件处理程序:
查看模型
private ObservableCollection<Window> _items = new ObservableCollection<Window>();
public ObservableCollection<Window> Items
{
get => _items;
set => SetProperty(ref _items, value);
}
private ObservableCollection<Window> _selectedItems = new ObservableCollection<Window>();
public ObservableCollection<Window> SelectedItems
{
get => _selectedItems ;
set => SetProperty(ref _selectedItems, value);
}
private observetecollection\u items=new observetecollection();
公共可观测收集项目
{
获取=>\u项;
set=>SetProperty(参考项,值);
}
私有ObservableCollection _selectedItems=新ObservableCollection();
公共可观测集合SelectedItems
{
get=>\u selectedItems;
set=>SetProperty(ref\u selectedItems,value);
}
查看(代码隐藏)
DataGridSelectionChanged上的私有void(对象发送者,SelectionChangedEventArgs e)
{
var viewModel=this.DataContext作为viewModel;
foreach(e.AddedIte中的窗口addedItem
private void Button_Click(object sender, RoutedEventArgs e)
{
SelectedItemsListBox.Items.Clear();
foreach(var item in ItemsDataGrid.SelectedItems)
{
SelectedItemsListBox.Items.Add(item);
}
SelectedItemsListBox.Items.Refresh();
}
private ObservableCollection<Window> _items = new ObservableCollection<Window>();
public ObservableCollection<Window> Items
{
get => _items;
set => SetProperty(ref _items, value);
}
private ObservableCollection<Window> _selectedItems = new ObservableCollection<Window>();
public ObservableCollection<Window> SelectedItems
{
get => _selectedItems ;
set => SetProperty(ref _selectedItems, value);
}
private void OnDataGridSelectionChanged(object sender, SelectionChangedEventArgs e)
{
var viewModel = this.DataContext as ViewModel;
foreach (Window addedItem in e.AddedItems.Cast<Window>())
{
viewModel.SelectedItems.Add(addedItem);
}
foreach (Window removedItem in e.RemovedItems.Cast<Window>())
{
viewModel.SelectedItems.Remove(removedItem);
}
}
<DataGrid SelectionMode="Extended"
SelectionChanged="OnDataGridSelectionChanged">
...
</DataGrid>
<ListBox ItemsSource="{Binding SelectedItems}" />
<DataGrid Grid.Row="1"
x:Name="ItemsDataGrid"
SelectionMode="Extended"
ItemsSource="{Binding ItemsCollection}">
<DataGrid.ItemContainerStyle>
<Style TargetType="DataGridRow">
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="Red" />
</Trigger>
</Style.Triggers>
</Style>
</DataGrid.ItemContainerStyle>
</DataGrid>
<ListBox Grid.Row="2" ItemsSource="{Binding SelectedItems, ElementName=ItemsDataGrid}"/>