| Severity | Warning |
| Reported by | Metalama.Patterns.Wpf |
| Message | The can-execute property of the command method '<Method>' is not public, but INotifyPropertyChanged integration is enabled. Because INotifyPropertyChanged implementations usually raise the PropertyChanged event only for public properties, the CanExecuteChanged event of the command might not be raised. Make the property public or disable INotifyPropertyChanged integration. |
Cause
When the CanExecute logic of a [Command] method is a property, and the declaring type implements INotifyPropertyChanged, the aspect subscribes to the PropertyChanged event and raises the command's CanExecuteChanged event when the can-execute property changes. This integration is enabled by default.
Many INotifyPropertyChanged implementations raise PropertyChanged only for public properties. If the can-execute property is private, protected, or internal, the command might never learn that the property changed, and the bound button stays enabled or disabled.
Metalama reports LAMA5202 on the can-execute property when all the following conditions are true:
- the can-execute member found for the command, by naming convention or with CanExecuteProperty, is a property, not a method;
- INotifyPropertyChanged integration is enabled;
- the declaring type implements
INotifyPropertyChanged; - the property isn't
public.
In the message, <Method> is the method to which [Command] is applied. The command is still generated, and the integration is still in place.
In the following example, the command named Save uses the private property IsSaveEnabled as its can-execute property, found by the naming convention, in a type that implements INotifyPropertyChanged.
1using Metalama.Patterns.Wpf;
2using System.ComponentModel;
3
4namespace Doc.LAMA5202.Error;
5
6public partial class DocumentViewModel : INotifyPropertyChanged
7{
8 private bool _isDirty = true;
9
10 public event PropertyChangedEventHandler? PropertyChanged;
11
12 // Warning: the can-execute property is not public.
Warning LAMA5202: The can-execute property of the command method 'DocumentViewModel.Save()' is not public, but INotifyPropertyChanged integration is enabled. Because INotifyPropertyChanged implementations usually raise the PropertyChanged event only for public properties, the CanExecuteChanged event of the command might not be raised. Make the property public or disable INotifyPropertyChanged integration.
13 private bool IsSaveEnabled => this._isDirty;
14
15 [Command]
16 public void Save() => this._isDirty = false;
17}
18
1using Metalama.Patterns.Wpf;
2using System.ComponentModel;
3
4namespace Doc.LAMA5202.Error;
5
6public partial class DocumentViewModel : INotifyPropertyChanged
7{
8 private bool _isDirty = true;
9
10 public event PropertyChangedEventHandler? PropertyChanged;
11
12 // Warning: the can-execute property is not public.
13 private bool IsSaveEnabled => this._isDirty;
14
15 [Command]
16 public void Save() => this._isDirty = false;
17
18 public DocumentViewModel()
19 {
20 SaveCommand = DelegateCommandFactory.CreateDelegateCommand(Save, () => IsSaveEnabled, this, "IsSaveEnabled");
21 }
22
23 public DelegateCommand SaveCommand { get; }
24}
25
Resolution
- Make the can-execute property
public. This is the usual fix, and it makes the property available to data binding too. - If your INotifyPropertyChanged implementation does raise PropertyChanged for non-public properties, or if you don't need CanExecuteChanged, disable the integration by setting the EnableINotifyPropertyChangedIntegration property of the [Command] attribute to
false, or for many commands at once by setting EnableINotifyPropertyChangedIntegration tofalsein a fabric, through the ConfigureCommand extension method. - Use a can-execute method instead of a property. The integration doesn't apply to methods, so the command doesn't raise
CanExecuteChangedautomatically.
In the following example, the can-execute property is public.
1using Metalama.Patterns.Wpf;
2using System.ComponentModel;
3
4namespace Doc.LAMA5202.Fixed;
5
6public partial class DocumentViewModel : INotifyPropertyChanged
7{
8 private bool _isDirty = true;
9
10 public event PropertyChangedEventHandler? PropertyChanged;
11
12 // Fixed: the can-execute property is public.
13 public bool IsSaveEnabled => this._isDirty;
14
15 [Command]
16 public void Save() => this._isDirty = false;
17}
18
1using Metalama.Patterns.Wpf;
2using System.ComponentModel;
3
4namespace Doc.LAMA5202.Fixed;
5
6public partial class DocumentViewModel : INotifyPropertyChanged
7{
8 private bool _isDirty = true;
9
10 public event PropertyChangedEventHandler? PropertyChanged;
11
12 // Fixed: the can-execute property is public.
13 public bool IsSaveEnabled => this._isDirty;
14
15 [Command]
16 public void Save() => this._isDirty = false;
17
18 public DocumentViewModel()
19 {
20 SaveCommand = DelegateCommandFactory.CreateDelegateCommand(Save, () => IsSaveEnabled, this, "IsSaveEnabled");
21 }
22
23 public DelegateCommand SaveCommand { get; }
24}
25