• Stars
    star
    1,885
  • Rank 24,595 (Top 0.5 %)
  • Language
    C#
  • License
    MIT License
  • Created over 12 years ago
  • Updated about 1 month ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Injects INotifyPropertyChanged code into properties at compile time

PropertyChanged.Fody

Chat on Gitter NuGet Status

Injects code which raises the PropertyChanged event, into property setters of classes which implement INotifyPropertyChanged.

This is an add-in for Fody

It is expected that all developers using Fody become a Patron on OpenCollective. See Licensing/Patron FAQ for more information.

Usage

See also Fody usage.

NuGet installation

Install the PropertyChanged.Fody NuGet package and update the Fody NuGet package:

PM> Install-Package Fody
PM> Install-Package PropertyChanged.Fody

The Install-Package Fody is required since NuGet always defaults to the oldest, and most buggy, version of any dependency.

Add to FodyWeavers.xml

Add <PropertyChanged/> to FodyWeavers.xml

<Weavers>
  <PropertyChanged/>
</Weavers>

Overview

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 PropertyChangedEventArgs FamilyName = new PropertyChangedEventArgs("FamilyName");
    internal static PropertyChangedEventArgs FullName = new PropertyChangedEventArgs("FullName");
    internal static PropertyChangedEventArgs GivenNames = new PropertyChangedEventArgs("GivenNames");
}

(the actual injected type and method names are different)

Code Generator

Starting with version 4 PropertyChanged.Fody ships with a C# code generator that can even more simplify your code by generating the boilerplate of the basic INotifyPropertyChanged implementation for you directly as source code.

Simply mark a class implementing INotifyPropertyChanged or having the [AddINotifyPropertyChangedInterface] attribute as partial and the generator will add the necessary event and event-invokers:

e.g. a class like this:

public partial class Class1 : INotifyPropertyChanged
{
    public int Property1 { get; set; }
    public int Property2 { get; set; }
}

will be complemented by the generator with this:

public partial class Class1
{
    public event PropertyChangedEventHandler? PropertyChanged;
    protected void OnPropertyChanged([CallerMemberName] string? propertyName = null)
    {
        OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
    }
    protected virtual void OnPropertyChanged(PropertyChangedEventArgs eventArgs)
    {
        PropertyChanged?.Invoke(this, eventArgs);
    }
}
  • Only classes are supported, no records.
  • For nested classes, all containing classes must be partial, too.
  • Code generators only work correctly in SDK-style projects

Code Generator Configuration

You can configure the code generator via properties in your project file:

<PropertyGroup>
  <PropertyChangedAnalyzerConfiguration>
    <IsCodeGeneratorDisabled>false</IsCodeGeneratorDisabled>
    <EventInvokerName>OnPropertyChanged</EventInvokerName>
  </PropertyChangedAnalyzerConfiguration>
</PropertyGroup>
  • IsCodeGeneratorDisabled: Set to true to switch off the code generator.
  • EventInvokerName: Change the name of the event invoker method from OnPropertyChanged to your favorite name.

Workaround for WPF projects targeting multiple frameworks:

WPF projects targeting multiple frameworks may fail during the compilation of the *_wpftmp.csproj with

... error CS0111: Type 'SomeType' already defines a member called 'OnPropertyChanged' with the same parameter types

This can be fixed by adding this build target to your project:

<Target Name="RemoveDuplicateAnalyzers" BeforeTargets="CoreCompile">
  <!-- see https://github.com/dotnet/wpf/pull/6680 -->
  <RemoveDuplicates Inputs="@(Analyzer)">
    <Output
      TaskParameter="Filtered"
      ItemName="FilteredAnalyzer"/>
  </RemoveDuplicates>
  <ItemGroup>
    <Analyzer Remove="@(Analyzer)" />
    <Analyzer Include="@(FilteredAnalyzer)" />
  </ItemGroup>
</Target>

Notes

  • Dependent properties — In the above sample, the getter for FullName depends on the getters for GivenName and FamilyName. Therefore, when either GivenName or FamilyName is set, PropertyChanged is raised for FullName as well. This behavior can be configured manually using the AlsoNotifyFor attribute on the source property, or the DependsOn 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 the AddINotifyPropertyChangedInterfaceAttribute attribute. Raising an issue about "this attribute does not behave as expected" will result in a RTFM and the issue being closed.

    • for partial methods this will be done via the code generator, so the implementation is available at compile time.
  • Behavior is configured via attributes, or via options in the Weavers.xml file.

For more information, see the wiki pages.

Icon

Icon courtesy of The Noun Project

More Repositories

1

Fody

Extensible tool for weaving .net assemblies
C#
4,325
star
2

Costura

Embed references as resources
C#
2,383
star
3

NullGuard

Adds null argument checks to an assembly
C#
686
star
4

MethodTimer

Injects some very basic method timing code.
C#
686
star
5

Home

The landing page for Fody repositories
C#
671
star
6

ConfigureAwait

Configure async code's ConfigureAwait at a global level
C#
444
star
7

MethodDecorator

Compile time decorator pattern via IL rewriting
C#
380
star
8

Anotar

Simplifies logging through a static class and some IL manipulation.
C#
262
star
9

Equals

Generate Equals, GetHashCode and operators methods from properties.
C#
111
star
10

AsyncErrorHandler

An extension for Fody to integrate error handling into async and TPL code
C#
104
star
11

FodyAddinSamples

A working sample for each Fody Addin
C#
95
star
12

ToString

Generate ToString method from public properties.
C#
82
star
13

Janitor

Simplifies the implementation of IDisposable
C#
76
star
14

Ionad

Replaces static method calls.
C#
68
star
15

InfoOf

Provides methodof, propertyof and fieldof equivalents of typeof .
C#
65
star
16

Virtuosity

Change all members to virtual as part of your build.
C#
61
star
17

PropertyChanging

Injects INotifyPropertyChanging code into properties at compile time.
C#
54
star
18

ExtraConstraints

Facilitates adding constraints for Enum and Delegate to types and methods.
C#
47
star
19

Visualize

Adds debugger attributes to help visualize objects.
C#
43
star
20

Caseless

Change string comparisons to be case insensitive.
C#
40
star
21

Resourcer

Simplifies reading embedded resources from an Assembly.
C#
37
star
22

LoadAssembliesOnStartup

Loads all the references on startup by actually using the types in the module initializer.
C#
36
star
23

Obsolete

Helps keep usages of ObsoleteAttribute consistent.
C#
32
star
24

EmptyConstructor

Adds an empty constructor to classes even if you have a non-empty one defined.
C#
29
star
25

Scalpel

Strips all testing code from an assembly
C#
27
star
26

AssertMessage

Add 'message' parameter to Assertions. Nunit, Mstest, Xunit is supported.
C#
22
star
27

Publicize

Converts non-public members to public hidden members
C#
12
star
28

UniversalAppSample

C#
7
star
29

.github

1
star