更改DataGrid's selectedItem会破坏绑定
本文关键字:绑定 selectedItem DataGrid 更改 | 更新日期: 2023-09-27 18:11:26
我的绑定有一个恼人的问题,我不知道我做错了什么
我有一个绑定到ObservableCollection的数据网格和一个绑定到数据网格的SelectedItem的Name的标签。
当我现在用一个按钮以编程的方式改变选中Item的Name时,这在某种程度上中断了标签和SelectedItem的名称之间的绑定。通过测试,我发现它与我的类person的重写Equals和GetHashCode方法有关。
我把这些都分解成一个小例子:
XAML-Code:
<Window x:Class="IndependentTesting.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<DataGrid Grid.RowSpan="2" AutoGenerateColumns="True" HorizontalAlignment="Stretch" Name="dataGrid1" VerticalAlignment="Stretch" ItemsSource="{Binding}"/>
<Button Content="Button" Grid.Column="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Click="button1_Click" Grid.Row="1" />
<Label Grid.Column="1" Content="{Binding ElementName=dataGrid1, Path=SelectedItem.Name}" />
</Grid>
</Window>
后台代码:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
ObservableCollection<Person> persons = new ObservableCollection<Person>();
persons.Add(new Person("p1"));
persons.Add(new Person("p2"));
dataGrid1.DataContext = persons;
}
private void button1_Click(object sender, RoutedEventArgs e)
{
if (dataGrid1.SelectedItem != null && dataGrid1.SelectedItem is Person)
{
((Person)dataGrid1.SelectedItem).Name = "changed";
}
}
}
public class Person
{
public string Name { get; set; }
public override bool Equals(object obj)
{
if (!(obj is Person))
{
return false;
}
Person p = (Person)obj;
return (Name == null && p.Name == null) || (Name != null && Name.Equals(p.Name));
}
public override int GetHashCode()
{
return Name == null ? 0 : Name.GetHashCode();
}
public Person(string name)
{
Name = name;
}
}
如果我运行这段代码并选择两个人中的一个,然后按下按钮,绑定就会停止,无论我选择什么,标签都保持不变。我错过了什么?
选择器内部使用 InternalSelectedItemsStorage
,其中保存SelectedItems的ItemInfo。
当选择任何项时,它尝试从SelectedItems集合中删除先前的selectedItem,并从InternalSelectedItemsStorage中删除相应的ItemInfo。然后,在SelectedItems集合中添加选中的项目条目,并在InternalSelectedItemsStorage中添加其ItemInfo。
(注 -这是我的假设,通过查看堆栈跟踪,如果我把断点在GetHashCode()方法。如果你愿意,你可以使用reflector来窥视dataGrid的实际代码。
Issue -单击按钮更改SelectedItem属性的名称。(在计算对象的HashCode时起重要作用的属性)和当您更改dataGrid中的选择时,它会尝试在InternalSelectedItemsStorage找不到,因为ItemInfoHashcode已经改变了它之前存储item时的状态。因此,它永远不会从selectedItems集合中删除。因此,标签中没有绑定更新。
如果将GetHashCode()方法替换为this,您将看到示例工作正常:
public override int GetHashCode()
{
return Name == null ? 0 : base.GetHashCode();
}
我可以建议一个解决办法,这是设置SelectedItem为空之前更新它的名称,以便它从InternalSelectedItemsStorage删除。将SelectedItem设置回相同的值,一旦您完成更新Name属性。
private void button1_Click(object sender, RoutedEventArgs e)
{
if (dataGrid1.SelectedItem != null && dataGrid1.SelectedItem is Person)
{
Person selectedItem = (Person)dataGrid1.SelectedItem;
dataGrid1.SelectedItem = null;
selectedItem.Name = "changed";
dataGrid1.SelectedItem = selectedItem;
}
}
此外,您需要INPC,我假设您已经知道为什么需要它。
您需要在Person
类上实现INotifyPropertyChanged
,以便将更改通知传播到UI
INotifyPropertyChanged
接口用于通知客户端,通常是绑定客户端,属性值已经更改。
public class Person : INotifyPropertyChanged
{
private string _Name;
public string Name
{
get
{
return _Name;
}
set
{
_Name = value;
RaisePropertyChanged("Name");
}
}
public override bool Equals(object obj)
{
if (!(obj is Person))
{
return false;
}
Person p = (Person)obj;
return (Name == null && p.Name == null) || (Name != null && Name.Equals(p.Name));
}
public override int GetHashCode()
{
return Name == null ? 0 : Name.GetHashCode();
}
public Person(string name)
{
Name = name;
}
public event PropertyChangedEventHandler PropertyChanged;
void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
关于INotifyPropertyChanged的更多信息