How to Raise Property Changed in Derived Classes?

2020-03-31 20:28发布

问题:

How do I raise PropertyChanged for SomeProperty in class B?

This example does not compile since PropertyChanged is not accessible this way...

public class A : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
}

public class B : A
{
    private object _someProperty;

    public object SomeProperty
    {
        get => _someProperty;
        set
        {
            _someProperty = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SomeProperty)))
        }
    }
}

回答1:

Solution 1:

You can use this RaisePropertyChangedExtension:

public static class RaisePropertyChangedExtension
{
    public static void RaisePropertyChanged(this INotifyPropertyChanged @this, [CallerMemberName] string propertyName = null)
    {
        var declaringType = @this.GetType().GetEvent(nameof(INotifyPropertyChanged.PropertyChanged)).DeclaringType;
        var propertyChangedFieldInfo = declaringType.GetField(nameof(INotifyPropertyChanged.PropertyChanged), BindingFlags.Instance | BindingFlags.NonPublic);
        var propertyChangedEventHandler = propertyChangedFieldInfo.GetValue(@this) as PropertyChangedEventHandler;
        propertyChangedEventHandler?.Invoke(@this, new PropertyChangedEventArgs(propertyName));
    }
}

Like this:

public class B : A
{
    private object _someProperty;

    public object SomeProperty
    {
        get => _someProperty;
        set
        {
            _someProperty = value;
            this.RaisePropertyChanged();
        }
    }
}

In my opinion this is the best solution I know so far.

Disadvantage is that you're able to raise PropertyChanged from another class like this:

public class C
{
    public C(B b)
    {
        b.RaisePropertyChanged(nameof(b.SomeProperty));
    }
}

It's not good practise to raise PropertyChanged from other classes this way, so i'm not concerned by this disadvantage.

This solution is inspired by Thomas Levesque's answer here: Simple small INotifyPropertyChanged implementation

Solution 2:

You can create a protected RaisePropertyChanged in the base class A:

public class A : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

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

And call the method in the derived class B:

public class B : A
{
    private object _someProperty;

    public object SomeProperty
    {
        get => _someProperty;
        set
        {
            _someProperty = value;
            RaisePropertyChanged();
        }
    }
}

Disadvantage is that you have to implement the RaisePropertyChanged method for each new base class you're creating on the opposite you avoid the disadvantage that Solution 1 had.