Open sandboxFocusImprove this doc

LAMA5150: The OnPropertyChanged method is missing or can't be overridden

Severity Error
Reported by Metalama.Patterns.Observability
Message The class '<Class>' implements the PropertyChanged event, but neither it nor its base classes define an overridable 'void OnPropertyChanged(string)' or 'void OnPropertyChanged(PropertyChangedEventArgs)' method. The method must be public or protected, virtual or override, and not sealed. It can also be named 'NotifyOfPropertyChange' or 'RaisePropertyChanged'.

Cause

When the [Observable] aspect is applied to a class that already has a PropertyChanged event, either declared in the class or inherited from a base class, the aspect doesn't introduce its own event. Instead, it reuses the existing implementation, which requires an OnPropertyChanged method that the aspect can override to react to the notifications raised by the base class.

Metalama reports LAMA5150 when neither the class nor its base classes define such a method. The aspect looks for a method that:

  • is named OnPropertyChanged, NotifyOfPropertyChange, or RaisePropertyChanged;
  • is an instance method that returns void and is public or protected;
  • is virtual or override, and not sealed;
  • takes a single parameter, of type string or PropertyChangedEventArgs.

Typical causes are a base class whose OnPropertyChanged method isn't virtual or is private, a base class whose method has no parameter or has more than one parameter, and a base class that raises the PropertyChanged event directly without any helper method.

In the following example, the ObservableBase class defines an OnPropertyChanged method, but the method isn't virtual.

1using Metalama.Patterns.Observability;
2using System.ComponentModel;
3
4namespace Doc.LAMA5150.Error;
5
6public abstract class ObservableBase : INotifyPropertyChanged
7{
8    public event PropertyChangedEventHandler? PropertyChanged;
9
10    // This method is not virtual, so the [Observable] aspect can't override it.
11    protected void OnPropertyChanged( string propertyName )
12        => this.PropertyChanged?.Invoke( this, new PropertyChangedEventArgs( propertyName ) );
13}
14
15// Error: the base class doesn't define an overridable OnPropertyChanged method.
16[Observable]
Error LAMA5150: The class 'Customer' implements the PropertyChanged event, but neither it nor its base classes define an overridable 'void OnPropertyChanged(string)' or 'void OnPropertyChanged(PropertyChangedEventArgs)' method. The method must be public or protected, virtual or override, and not sealed. It can also be named 'NotifyOfPropertyChange' or 'RaisePropertyChanged'.

17public partial class Customer : ObservableBase
18{
19    public string? Name { get; set; }
20}
21

Resolution

Add a suitable method to the base class, or change the existing one:

  • Make the existing OnPropertyChanged method virtual, and protected or public.
  • If the base class raises the PropertyChanged event directly, add a protected virtual OnPropertyChanged method with a string parameter that raises the event, and call it from the existing code.
  • If you can't change the base class, for instance because it belongs to a third-party library, remove the [Observable] aspect from the derived class and implement INotifyPropertyChanged manually.

The aspect also needs a method with a single string parameter to raise notifications. When the overridable method takes a PropertyChangedEventArgs, make sure that the class also has such a method, otherwise Metalama reports another error.

In the following example, the OnPropertyChanged method is made virtual.

Source Code
1using Metalama.Patterns.Observability;
2using System.ComponentModel;
3
4namespace Doc.LAMA5150.Fixed;
5
6public abstract class ObservableBase : INotifyPropertyChanged
7{
8    public event PropertyChangedEventHandler? PropertyChanged;
9
10    // Fixed: the method is virtual, so the [Observable] aspect can override it.
11    protected virtual void OnPropertyChanged( string propertyName )
12        => this.PropertyChanged?.Invoke( this, new PropertyChangedEventArgs( propertyName ) );
13}
14
15[Observable]
16public partial class Customer : ObservableBase
17{
18    public string? Name { get; set; }
19}


20
Transformed Code
1using Metalama.Patterns.Observability;
2using System.ComponentModel;
3
4namespace Doc.LAMA5150.Fixed;
5
6public abstract class ObservableBase : INotifyPropertyChanged
7{
8    public event PropertyChangedEventHandler? PropertyChanged;
9
10    // Fixed: the method is virtual, so the [Observable] aspect can override it.
11    protected virtual void OnPropertyChanged(string propertyName)
12        => this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
13}
14
15[Observable]
16public partial class Customer : ObservableBase
17{
18    private string? _name;
19
20    public string? Name
21    {
22        get
23        {
24            return _name;
25        }
26
27        set
28        {
29            if (!object.ReferenceEquals(value, _name))
30            {
31                _name = value;
32                OnPropertyChanged("Name");
33            }
34        }
35    }
36
37    protected override void OnPropertyChanged(string propertyName)
38    {
39        base.OnPropertyChanged(propertyName);
40    }
41}
42

See also