2

我有这个代码,我有我的 ViewModel,而 ViewModel 有一个属性,它可以获取它的所有属性。

这是粗略的伪代码:

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 永远不会被绑定。

有没有办法触发父母的 RaisePropertyChanged?

谢谢

4

2 回答 2

2

首先,您可以简化RaisePropertyChanged这种方式:

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

所以你不需要写RaisePropertyChanged("Description"),而只需要:RaisePropertyChanged(),并且propertyName自动注入。如果您经常重构,那就太棒了:您不必面对记住整个解决方案中所有“标题”和“描述”字符串的噩梦 :)

其次,如果有的话BaseClassPropertyChangedEvent可以在里面收听MyClassViewModel

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

但是,如果你不myClassBase立即在 的构造函数中注入MyClassViewModel,或者如果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);
}

NB:我用的是C#-6.0nameof()操作符,希望你能用,简直太棒了!

编辑:

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

    [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 中设置 a ,代码会抛出 a NullReferenceException。也许你应该这样修改它:

public string Title
{
    get
    {
        return myClassBase?.Title;
    }
    set
    {
        if (myClassBase != null)
                myClassBase.Title = value;
        RaisePropertyChanged();
    }
}
于 2016-11-11T09:44:05.827 回答
1

我建议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
}
于 2016-11-11T09:16:08.813 回答