首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >鼓泡NotifyPropertyChanged

鼓泡NotifyPropertyChanged
EN

Stack Overflow用户
提问于 2018-07-30 22:20:03
回答 1查看 285关注 0票数 1

根据这个答案的说法,我不需要为NotifyPropertyChanges的冒泡而费心,尽管如此,我还是不能让它与(简化的测试)结构一起工作:

数据保持类

代码语言:javascript
运行
复制
public class TestNotifyChanged : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private string _Test = "default";
    public string Test
    {
        get
        {
            return _Test;
        }
        set
        {
            if(_Test!=value)
            {
                _Test = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Test"));
            }
        }
    }
}

使用该测试类和测试属性的ViewModel:

代码语言:javascript
运行
复制
public class ViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private TestNotifyChanged tnc = new TestNotifyChanged(); // only to init, otherwise VS screams at me

    public ViewModel(TestNotifyChanged tnc)
    {
        tnc = tnc; // getting an instance of TestNotifyChanged from "Master" passed in, which hopefully will be replaces by a singleton class.
    }

    private string _Test;
    public string Test
    {
        get
        {
            return tnc.Test;  // this might be the crucial part!?
        }
        set
        {
            if (_Test != value) // never hits that, as I would expect, but you never know..
            {
                _Test = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Test"));  // of course also never hit, as expected
            }
        }
    }
}

最后我的MainWindow cs

代码语言:javascript
运行
复制
public partial class MainWindow : Window
{
    TestNotifyChanged tnc;
    public MainWindow()
    {
        InitializeComponent();
        tnc = new TestNotifyChanged();
        DataContext = new ViewModel(tnc); // pass in my Test-Object that has the Values.
    }

    private void ButtonGet_Click(object sender, RoutedEventArgs e)
    {
        tnc.Test = "new Value";
        MessageBox.Show($"{tnc.Test}"); // got "new Value" here!
    }
}

在xaml中,除了这个按钮之外,还有一个简单的TextBlock,它绑定到ViewModel的Test属性:

代码语言:javascript
运行
复制
 <TextBlock x:Name="OutputId" Text="{Binding Path=Test, Mode=OneWay}"/>

现在发生了什么:

  • 默认值" default“显示在TextBlock中。
  • 当我单击按钮时,messageBox会显示“新值”
  • TextBlock是而不是更新为“新值”

我想达到的目标是:

  • 似乎很简单: TextBlock应该更新为“新值”

当我直接在ViewModel上设置测试值时,我可以轻松地完成这个任务--但这似乎不正确,而且与我认为可以构造我的应用程序/代码的东西相去甚远。未来的目标是拥有一个拥有大部分数据的"RecordStore“(静态不起作用)(并从API、本地数据库或内存中获得这些数据)。

所以问题是:

为什么NotifyPropertyChange没有冒泡到视图/视图模型?

或者还有什么我看不出来的问题?

我读过类层次结构中的INotifyPropertyChanged冒泡通过MVVM的INotifyPropertyChanged属性将ViewModel事件泡起来的好方法是什么?https://learn.microsoft.com/en-us/dotnet/framework/winforms/how-to-implement-the-inotifypropertychanged-interfaceOnPropertyChange调用但对UI没有任何影响

这些问题大多也很古老..。

编辑:

我用这种方式尝试了@MineR的建议:

代码语言:javascript
运行
复制
// made tnc public in ViewModel
public TestNotifyChanged tnc = new TestNotifyChanged();

// changed Binding directly to that (and it's Property):
<TextBlock x:Name="OutputId" Text="{Binding Path=tnc.Test, Mode=OneWay}"/>

不幸的是,现在我甚至没有得到默认,所以我一定是误会了。

EDIT2

我在第一次编辑时做了一件错事:

代码语言:javascript
运行
复制
// this isn't recognized as bindable parameter:
public TestNotifyChanged tnc = new TestNotifyChanged();
// it instead has to be
public TestNotifyChanged tnc { get; }

我将其设置为TNC,删除了本地Test参数,直接绑定到Path=TNC.Test

因此,我理解,PropertyChanges并不像我所希望的那样浮出水面,最好直接绑定到嵌套对象。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2018-07-31 05:29:18

“冒泡”是路由事件的一个概念。像PropertyChanged这样的常规活动不会“泡汤”。

除了ViewModel中明显的bug ViewModel(应该是this.tnc = tnc;)之外,这两个类的测试属性是不相关的。为了更新自己的Test,ViewModel必须在tnc注册一个PropertyChanged事件处理程序。当它自己的Test属性发生变化时,它必须更新tnc的属性。

代码语言:javascript
运行
复制
public class ViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private TestNotifyChanged tnc;

    public ViewModel(TestNotifyChanged t)
    {
        tnc = t;
        tnc.PropertyChanged += (s, e) =>
        {
            if (e.PropertyName == nameof(Test) || string.IsNullOrEmpty(e.PropertyName))
            {
                Test = tnc.Test; // update ViewModel.Test from TestNotifyChanged.Test
            }
        };
    }

    private string test;
    public string Test
    {
        get
        {
            return test; // always return own value
        }
        set
        {
            if (test != value)
            {
                test = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Test)));

                tnc.Test = Test; // update TestNotifyChanged.Test from ViewModel.Test
            }
        }
    }
}

或者,删除Test的后备字段,只在tnc.Test上操作

代码语言:javascript
运行
复制
public class ViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private TestNotifyChanged tnc;

    public ViewModel(TestNotifyChanged t)
    {
        tnc = t;
        tnc.PropertyChanged += (s, e) =>
        {
            if (e.PropertyName == nameof(Test) || string.IsNullOrEmpty(e.PropertyName))
            {
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Test)));
            }
        };
    }

    public string Test
    {
        get { return tnc.Test; }
        set { tnc.Test = Test; }
    }
}

,幸运的是,它根本没有必要.

取而代之的可能是一个公共的Tnc属性,如

代码语言:javascript
运行
复制
public class ViewModel
{
    public TestNotifyChanged Tnc { get; }

    public ViewModel(TestNotifyChanged tnc)
    {
        Tnc = tnc;
    }
}

有这样的装订:

代码语言:javascript
运行
复制
<TextBlock Text="{Binding Tnc.Test}"/>
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/51603023

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档