Child 的 Parent 的 C# 触发器 RaisePropertyChanged?

C# Trigger RaisePropertyChanged of Parent from Child?

我有这段代码,其中有我的 ViewModel,而 ViewModel 有一个 属性,它在其中获取其所有属性。

这很粗糙pseudo-code:

public class MyClassViewModel : INotifyPropertyChanged
{

    public MyClassViewModel ()
    {

    }

    public BaseClass myClassBase { get ; set; }

    public string Title
    {
        get
        {
            return myClassBase.Title;
        }
        set
        {
            myClassBase.Title = value;
            RaisePropertyChanged("Title");
        }
    }

    public string Description
    {
        get
        {
            return myClassBase.Description;
        }
        set
        {
            myClassBase.Description = value;
            RaisePropertyChanged("Description");
        }
    }
}

这是基类:

public class BaseClass
{
    public BaseClass()
    {

    }

    public string Title {get;set;}
    public string Description {get;set;}
}

CheckItemViewModel 是绑定到 UI 的那个。因此,如果我执行 MyClassViewModel .Title = "Test"; 之类的操作,它会正确刷新 UI.

但是,出于特定原因(Javascript - Chakra 接口),我需要做一些类似 MyClassViewModel.myClassBase.Title = "Test" 的事情。问题是 UI 不再刷新,因为它没有 RaisePropertyChanged。

即使我在 BaseClass 本身内部实现了 RaisePropertyChanged,它仍然不起作用。它不起作用,因为 BaseClass 中的 PropertyChanged 始终为 null.

我怀疑这是因为 MyClassViewModel 是绑定到 UI 的那个。所以 BaseClass 中的 PropertyChanged 永远不会被绑定。

有没有办法触发 Parent 的 RaisePropertyChanged?

谢谢

我建议在 类 上实施 INotifyPropertyChanged,然后让 MyClassViewModel 订阅 BaseClass 中的事件并将其转发给 UI:

public class MyClassViewModel : INotifyPropertyChanged, IDisposable
{
    private BaseClass myClassBase;

    public void Dispose()
    {
        if (myClassBase != null) myClassBase.PropertyChanged -= OnBaseClassPropertyChanged;
    }

    public BaseClass MyClassBase {
        get {
            return myClassBase;
        }

        set {
            if (myClassBase != null) myClassBase.PropertyChanged -= OnBaseClassPropertyChanged;
            myClassBase = value;
            myClassBase.PropertyChanged += OnBaseClassPropertyChanged;
        }
    }

    private void OnBaseClassPropertyChanged(object sender, PropertyChangedEventArgs args) {
        RaisePropertyChanged(args.PropertyName);
    }

    // forwarded properties (Title and Description) go here
}

首先,您可以这样简化 RaisePropertyChanged

public void RaisePropertyChanged([CallerMemberName] string propertyName = null)
{
    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

所以你不需要写RaisePropertyChanged("Description"),只写:RaisePropertyChanged()propertyName会自动注入。如果您经常重构,那就太棒了:您不必处理记住整个解决方案中所有 "Title" 和 "Description" 字符串的噩梦:)

其次,如果BaseClassPropertyChangedEvent,可以在MyClassViewModel.

中收听
myClassBase.PropertyChanged += (s, e) => { RaisePropertyChanged(e.PropertyName); };

但是,如果您不立即在 MyClassViewModel 的构造函数中注入 myClassBase,或者如果 myClassBase 有时会发生变化,事情就会变得有点复杂。

你必须让 MyClassViewModel 也实现 INotifyPropertyChanging:

public event PropertyChangingEventHandler PropertyChanging;

public void RaisePropertyChanging([CallerMemberName] string propertyName = null)
{
    PropertyChanging?.Invoke(this, new PropertyChangingEventArgs(propertyName));
}

您还必须为 myClassBase:

发出通知
public BaseClass myClassBase
{
    get { return _myClassBase; }
    set
    {
        RaisePropertyChanging();
        _myClassBase = value;
        RaisePropertyChanged();
    }
}
private BaseClass _myClassBase;

那么,你只需要这个代码:

public MyClassViewModel()
{
    PropertyChanging += OnPropertyChanging;
    PropertyChanged += OnPropertyChanged;
}

private void OnPropertyChanging(object sender, PropertyChangingEventArgs e)
{
    if (e.PropertyName != nameof(MyClassViewModel.myClassBase))
        return; //or do something with the other properties

    if (myClassBase == null)
        return;

    myClassBase.PropertyChanged -= OnMyBaseClassPropertyChanged;
}

private void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
{
    if (e.PropertyName != nameof(MyClassViewModel.myClassBase))
        return; //or do something with the other properties

    if (myClassBase == null)
        return;

    myClassBase.PropertyChanged += OnMyBaseClassPropertyChanged;
}

private void OnMyBaseClassPropertyChanged(object sender, PropertyChangedEventArgs e)
{
    RaisePropertyChanged(e.PropertyName);
}

注意:我用的是C#-6.0的nameof()运算符,希望你能用,简直太棒了!

编辑:

这里有一个演示正确功能的简单测试方法:

    [TestMethod]
    public void ChildClassPropertyChanged()
    {
        var bc = new BaseClass();

        var c = new MyClassViewModel();

        bc.Title = "t1";

        c.myClassBase = bc;

        Assert.AreEqual("t1", c.Title);

        c.Title = "t2";
        Assert.AreEqual("t2", c.Title);

        c.myClassBase.Title = "t3";
        Assert.AreEqual("t3", c.Title);

        c.myClassBase = new BaseClass();

        bc.Title = "t4";
        Assert.AreEqual(null, c.Title);

        c.myClassBase.Title = "t5";
        Assert.AreEqual("t5", c.Title);
    }

请记住,如果您设置 null myClassBase,在您的属性的 getter 和 setter 中,代码会抛出 NullReferenceException。也许你应该这样修改它:

public string Title
{
    get
    {
        return myClassBase?.Title;
    }
    set
    {
        if (myClassBase != null)
                myClassBase.Title = value;
        RaisePropertyChanged();
    }
}