Injects code which raises the PropertyChanged
event, into property setters of classes which implement INotifyPropertyChanged.
This is an add-in for Fody.
See also Fody usage.
Install the PropertyChanged.Fody NuGet package and update the Fody NuGet package:
PM> Install-Package PropertyChanged.Fody
PM> Update-Package Fody
The Update-Package Fody
is required since NuGet always defaults to the oldest, and most buggy, version of any dependency.
Add <PropertyChanged/>
to FodyWeavers.xml
<?xml version="1.0" encoding="utf-8" ?>
<Weavers>
<PropertyChanged/>
</Weavers>
NOTE: All classes that implement INotifyPropertyChanged
will have notification code injected into property setters.
Before code:
public class Person : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public string GivenNames { get; set; }
public string FamilyName { get; set; }
public string FullName => $"{GivenNames} {FamilyName}";
}
What gets compiled:
public class Person : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
string givenNames;
public string GivenNames
{
get => givenNames;
set
{
if (value != givenNames)
{
givenNames = value;
OnPropertyChanged(InternalEventArgsCache.GivenNames);
OnPropertyChanged(InternalEventArgsCache.FullName);
}
}
}
string familyName;
public string FamilyName
{
get => familyName;
set
{
if (value != familyName)
{
familyName = value;
OnPropertyChanged(InternalEventArgsCache.FamilyName);
OnPropertyChanged(InternalEventArgsCache.FullName);
}
}
}
public string FullName => $"{GivenNames} {FamilyName}";
protected void OnPropertyChanged(PropertyChangedEventArgs eventArgs)
{
PropertyChanged?.Invoke(this, eventArgs);
}
}
internal static class InternalEventArgsCache
{
internal static readonly PropertyChangedEventArgs FamilyName = new PropertyChangedEventArgs("FamilyName");
internal static readonly PropertyChangedEventArgs FullName = new PropertyChangedEventArgs("FullName");
internal static readonly PropertyChangedEventArgs GivenNames = new PropertyChangedEventArgs("GivenNames");
}
(the actual injected type and method names are different)
-
Dependent properties — In the above sample, the getter for
FullName
depends on the getters forGivenName
andFamilyName
. Therefore, when eitherGivenName
orFamilyName
is set,PropertyChanged
is raised forFullName
as well. This behavior can be configured manually using theAlsoNotifyFor
attribute on the source property, or theDependsOn
attribute on the target property). -
Intercepting the notification call
- Global interception
- Class-level interception — The
OnPropertyChanged
method will only be injected if there is no such existing method on the class; if there is such a method, then calls to that method will be injected into the setters — see here. - Property-level interception — For a given property, if there is a method of the form
On<PropertyName>Changed
, then that method will be called — see here.
-
To get the before / after values, use the following signature for
OnPropertyChanged
/On<PropertyName>Changed
:public void OnPropertyChanged(string propertyName, object before, object after)
-
To prevent a specific class from having the notification call injection, use the
DoNotNotify
attribute. -
To scope the rewriting only to specific classes, and not the whole Assembly, you can use the
FilterType
attribute. This changes the general behavior from from opt-out to opt-in. Example:[assembly: PropertyChanged.FilterType("My.Specific.OptIn.Namespace.")]
. The string is interpreted as a Regex, and you can use multiple filters. A class will be weaved, if any filter matches. -
The
INotifyPropertyChanged
interface can be automatically implemented for a specific class using theAddINotifyPropertyChangedInterfaceAttribute
attribute. Raising an issue about "this attribute does not behave as expected" will result in a RTFM and the issue being closed. -
Behavior is configured via attributes, or via options in the
Weavers.xml
file.
For more information, see the wiki pages.
Icon courtesy of The Noun Project