繁体   English   中英

更改DataGrid的selectedItem会中断绑定

[英]Changing DataGrid's selectedItem breaks binding

我的绑定有一个烦人的问题,但我找不到我做错了什么。

我有一个绑定到ObservableCollection的datagrid和一个绑定到该datagrid的SelectedItem的名称的Label。

现在,当我用一个按钮以编程方式更改所选项目的名称时,这会以某种方式阻止标签和SelectedItem名称之间的绑定。 通过测试,我发现这与我的Class成员重写的Equals和GetHashCode方法有关。

我将一切分解为一个小例子:

XAML代码:

<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;
    }
}

如果我运行此代码并选择两个人中的一个,然后按一下按钮,则装订制动器会刹车,无论我后来选择什么,标签都保持不变。 我想念什么?

您需要在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的更多信息

选择器在内部使用InternalSelectedItemsStorage ,其中存储着SelectedItems的ItemInfo

选择任何项目时,它将尝试从SelectedItems集合中删除以前的selectedItem,并从InternalSelectedItemsStorage中删除相应的ItemInfo。 然后,在SelectedItems集合中添加所选项目条目,并在InternalSelectedItemsStorage中添加其ItemInfo。

-这是我的假设,如果我将断点放在GetHashCode()方法上,则可以查看堆栈跟踪。如果需要,可以使用反射器窥视dataGrid的实际代码)

问题 -单击按钮时更改SelectedItem属性的名称。 (属性在计算对象的HashCode方面起着至关重要的作用。)当您在dataGrid中更改选择时,它会尝试在InternalSelectedItemsStorage中查找无法找到的条目,这是因为ItemInfo哈希码已更改,而该更改早于将项目存储在其中时。 因此,它永远不会从selectedItems集合中删除。 因此,标签中没有绑定更新。


如果将GetHashCode()方法替换为此,您将看到示例工作正常:

public override int GetHashCode()
{
    return Name == null ? 0 : base.GetHashCode();
}

我可以建议一个解决方法是在更新Name之前将SelectedItem设置为null,以便将其从InternalSelectedItemsStorage中删除。 完成Name属性更新后,将SelectedItem设置回相同的值

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,我假设您已经知道为什么需要它。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM