1

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)))
        }
    }
}
Jogge
  • 1,654
  • 12
  • 36

1 Answers1

7

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.

Community
  • 1
  • 1
Jogge
  • 1,654
  • 12
  • 36
  • 3
    I would prefer Solution 2 any day. Especially when the base class (A) is already a given. – H H Feb 20 '19 at 15:09
  • I agree with solution 2. Espacially it's actually eliminating the need of passing the property name at all. This would be necessary in case of bein able to call the event directly. – Malior Feb 20 '19 at 15:14
  • The base viewmodel I use is almost exactly the same as that base class. Except I make RaisePropertyChanged public. It's occasionally useful to be able to call it from one vm on another. – Andy Feb 20 '19 at 16:23
  • 1
    Maybe I am missing the point, but if you have a question and a quite detailed answer in the same minute, this should rather go to https://codereview.stackexchange.com/ – mami Feb 20 '19 at 17:49
  • @mami https://stackoverflow.blog/2011/07/01/its-ok-to-ask-and-answer-your-own-questions/ – Jogge Feb 26 '19 at 07:35
  • @Andy If you don't have a problem with being able to raise property changed from other classes, you really should consider **Solution 1**, instead of implementing the method on every single class that implements `INotifyPropertyChanged`. – Jogge Jun 09 '20 at 10:45