How to Raise Property Changed in Derived Classes?
Asked Answered
D

1

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)))
        }
    }
}
Discoloration answered 20/2, 2019 at 14:56 Comment(1)
This is a common problem solved in every MVVM framework or demo.Immotile
D
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.

Discoloration answered 20/2, 2019 at 14:56 Comment(6)
I would prefer Solution 2 any day. Especially when the base class (A) is already a given.Immotile
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.Manassas
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.Oubre
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 codereview.stackexchange.comComras
@Comras stackoverflow.blog/2011/07/01/…Discoloration
@Oubre 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.Discoloration

© 2022 - 2024 — McMap. All rights reserved.