If you are tired of writing boilerplate code for property change notifications, you can try either PropertyChanged.Fody or ReactiveUI.Fody. These libraries are both based on Fody - an extensible tool for weaving .NET assemblies, and they'll inject INotifyPropertyChanged
code into properties at compile time for you. We recommend using ReactiveUI.Fody package that also handles ObservableAsProperyHelper
properties.
Read-write properties
Typically properties are declared like this:
private string _name;
public string Name
{
get => _name;
set => this.RaiseAndSetIfChanged(ref _name, value);
}
With ReactiveUI.Fody, you don't have to write boilerplate code for getters and setters of read-write properties — the package will do it automagically for you at compile time. All you have to do is annotate the property with the [Reactive]
attribute, as shown below.
[Reactive]
public string Name { get; set; }
Note
ReactiveUI.Fody
currently doesn't support inline auto property initializers in generic types. It works fine with non-generic types. But if you are working on a generic type, don't attempt to write code likepublic string Name { get; set; } = "Name";
, this won't work as you might expect and will likely throw a very weird exception. To workaround this limitation, move your property initialization code to the constructor of your view model class. We know about this limitation and have a tracking issue for this.
ObservableAsPropertyHelper properties
Similarly, to declare output properties, the code looks like this:
ObservableAsPropertyHelper<string> _firstName;
public string FirstName => _firstName.Value;
Then the helper is initialized with a call to ToProperty
:
// firstNameObservable is IObservable<string>
_firstName = firstNameObservable
.ToProperty(this, x => x.FirstName);
With ReactiveUI.Fody, you can simply declare a read-only property using the [ObservableAsProperty]
attribute, using either option of the two options shown below. One option is to annotate the getter of the property:
public string FirstName { [ObservableAsProperty] get; }
Another option is to annotate the property as a whole:
[ObservableAsProperty]
public string FirstName { get; }
The field will be generated and the property implemented at compile time. Because there is no field for you to pass to .ToProperty
, you should use the .ToPropertyEx
extension method provided by this library:
// firstNameObservable is IObservable<string>
firstNameObservable.ToPropertyEx(this, x => x.FirstName);
This extension will assign the auto-generated field for you rather than relying on the out
parameter.
Note The generated getter for property of type
T
annotated with the[ObservableAsProperty]
attribute will returndefault(T)
in case if the property isn't yet initialized via a call toToPropertyEx
. To be more specific, the generated getter code looks somewhat likeT PropertyName => oaph?.Value ?? default(T);
, whereoaph
is a field of typeObservableAsProperty<T>
which is generated by the compiler.