当嵌套对象更改时更新绑定

发布于 2024-12-04 02:18:00 字数 1713 浏览 3 评论 0原文

我有一些 XAML 如下(一个简单的标签和按钮):

<Window x:Class="WpfApplication2.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:Test="clr-namespace:WpfApplication2">
<StackPanel DataContext="{Binding Path=TestPerson}">
    <Label Content="{Binding Path=Name}"></Label>
    <Button Content="Button" Click="button1_Click" />
</StackPanel>
</Window>

在后面的代码中我有:

public partial class MainWindow : Window, INotifyPropertyChanged
{
    private Person _person = new Person();
    public Person TestPerson { get { return _person; } }

    public MainWindow()
    {           
        DataContext = this;
        InitializeComponent();
    }

    private void button1_Click(object sender, RoutedEventArgs e)
    {
        _person.Name = "Bill";
        //_person = new Person() { Name = "Bill" };
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs("TestPerson"));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

并且类 Person 是:

public class Person
{
    string _name = "Bob";

    public string Name
    {
        get { return _name; }
        set { _name = value; }
    }
}

事实上,触发 Propertychanged 事件不会导致标签的内容更改为 Bill。

我发现我可以通过以下任一方法来克服这个问题:

  • 将一个新对象分配给 _person (如注释掉的行中所示)
  • 从 StackPanel 中删除 DataContext 并将 Label 绑定到 Path=TestPerson.Name

我不明白为什么我必须实际为 _person 分配一个新对象才能更新标签,或者在绑定中使用完整路径。

有没有一种方法可以在不提供完整路径(依赖于 DataContext)并且不向 _person 分配新对象的情况下更新标签?

I have a some XAML as follows (a simple Label and Button):

<Window x:Class="WpfApplication2.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:Test="clr-namespace:WpfApplication2">
<StackPanel DataContext="{Binding Path=TestPerson}">
    <Label Content="{Binding Path=Name}"></Label>
    <Button Content="Button" Click="button1_Click" />
</StackPanel>
</Window>

in the code behind I have:

public partial class MainWindow : Window, INotifyPropertyChanged
{
    private Person _person = new Person();
    public Person TestPerson { get { return _person; } }

    public MainWindow()
    {           
        DataContext = this;
        InitializeComponent();
    }

    private void button1_Click(object sender, RoutedEventArgs e)
    {
        _person.Name = "Bill";
        //_person = new Person() { Name = "Bill" };
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs("TestPerson"));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

and the class Person is:

public class Person
{
    string _name = "Bob";

    public string Name
    {
        get { return _name; }
        set { _name = value; }
    }
}

As it is, firing the Propertychanged event does not cause the Label's contents to change to Bill.

I've found I am able to overcome this by either:

  • Assigning a new object to _person (as in the commented out line)
  • Removing the DataContext from the StackPanel and have Label bind to Path=TestPerson.Name

I don't understand why I have to actually assign a NEW object to _person for the Label to update, or use the FULL path in the binding.

Is there a way to update the Label without supplying the full path (relying on the DataContext), and without assigning a new object to _person?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

宛菡 2024-12-11 02:18:00

您为 Person 实例 TestPerson 引发 PropertyChanged。但是,TestPerson 没有改变,改变的是 TestPersonName 属性,即 Label< 的属性。 /code> 绑定到。

编辑:回答为什么前两个版本有效

将一个新对象分配给_person(如注释掉的行中所示)

这里您实际上正在更改 TestPerson 的值,并且因为 DataContext 是由子级继承的,所以 Label 也会获得一个新的 DataContext,因此这就是更新 Binding 的原因。

从 StackPanel 中删除 DataContext 并将 Label 绑定到
路径=TestPerson.Name

这是我从未见过的。相同的绑定为 Person 中的 TestPersonName 订阅 PropertyChanged,因此引发 PropertyChanged > 对于任何这些属性都可以。

如果您想在不为 Person 实现 INotifyPropertyChanged 的情况下解决此问题,您可以将 UpdateSourceTrigger 设置为 Explicit

<Label Name="label"
       Content="{Binding Path=Name, UpdateSourceTrigger=Explicit}"/>

并更新每当 Name 更改时手动 Bining

private void button1_Click(object sender, RoutedEventArgs e)
{
    _person.Name = "Bill";
    BindingExpression be = label.GetBindingExpression(Label.ContentProperty);
    be.UpdateTarget();
    if (PropertyChanged != null)
    {
        PropertyChanged(this, new PropertyChangedEventArgs("TestPerson"));
    }
}

否则,只需为 Person 实现 INotifyPropertyChanged 即可。

public class Person : INotifyPropertyChanged
{ 
    string _name = "Bob"; 

    public string Name 
    { 
        get { return _name; } 
        set
        {
            _name = value;
            OnPropertyChanged("Name");
        } 
    } 

    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
} 

You raise PropertyChanged for the Person instance TestPerson. However, TestPerson hasn't changed, it is the Name property of TestPerson that has changed and that is the property the Label is binding to.

Edit: To answer why your first two versions work

Assigning a new object to _person (as in the commented out line)

Here you are actually changing the value of TestPerson and because DataContext is inherited by the children, the Label gets a new DataContext as well so that's why the Binding is updated.

Removing the DataContext from the StackPanel and have Label bind to
Path=TestPerson.Name

This is something I've never seen. The same binding subscribes to PropertyChanged for both TestPerson and Name in Person so raising PropertyChanged for any of these properties will work.

If you want to overcome this without implementing INotifyPropertyChanged for Person, you can change set UpdateSourceTrigger to Explicit

<Label Name="label"
       Content="{Binding Path=Name, UpdateSourceTrigger=Explicit}"/>

And update the Binding manually whenever Name changes

private void button1_Click(object sender, RoutedEventArgs e)
{
    _person.Name = "Bill";
    BindingExpression be = label.GetBindingExpression(Label.ContentProperty);
    be.UpdateTarget();
    if (PropertyChanged != null)
    {
        PropertyChanged(this, new PropertyChangedEventArgs("TestPerson"));
    }
}

Otherwise, just implement INotifyPropertyChanged for Person as well and it will work

public class Person : INotifyPropertyChanged
{ 
    string _name = "Bob"; 

    public string Name 
    { 
        get { return _name; } 
        set
        {
            _name = value;
            OnPropertyChanged("Name");
        } 
    } 

    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
} 
苏辞 2024-12-11 02:18:00

您需要对 XAML 进行一些更改...

在后面的代码中,不要将 DataContext 设置为 this,而是通过 Binding 在 XAML 中设置它...

     <StackPanel DataContext="{Binding RelativeSource={RelativeSource
                                            AncestorType= {x:Type Window},
                                                Mode=FindAncestor}, 
                                       Path=TestPerson}">
          <Label Content="{Binding Path=Name}"></Label>
          <Button Content="Button" Click="button1_Click" />
     </StackPanel>

代码中删除

     DataContext = this;

从您的 代码后面。

让我知道这是否有帮助。

You need a little change in your XAML...

In your code behind, instead of setting DataContext as this, set it in XAML via Binding...

     <StackPanel DataContext="{Binding RelativeSource={RelativeSource
                                            AncestorType= {x:Type Window},
                                                Mode=FindAncestor}, 
                                       Path=TestPerson}">
          <Label Content="{Binding Path=Name}"></Label>
          <Button Content="Button" Click="button1_Click" />
     </StackPanel>

Remove the

     DataContext = this;

from your code behind.

Let me know if this helps.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文