Skip to content

How to implement OnPropertyChanged

Simon Hughes edited this page Mar 20, 2026 · 5 revisions

How to Implement OnPropertyChanged

This guide shows how to add INotifyPropertyChanged support to generated POCO classes. The approach uses file-based templates for a clean, maintainable implementation.

Recommended Approach: File-Based Templates

The cleanest way is to use Custom file-based templates and modify the POCO template directly. This avoids editing the .ttinclude file (which would be overwritten on upgrade).

Alternative: Partial Classes (Simpler)

If you only need OnPropertyChanged for specific scenarios, use the partial modifier and implement the logic in a separate file without touching the generator.

Full Implementation

Step 1: Set the table base class in Database.tt

Settings.UpdateTable = delegate(Table table)
{
    table.BaseClasses = " : INotifyPropertyChanged";
};

Step 2: Add required namespaces in Database.tt

Settings.AdditionalNamespaces = new List<string>
{
    "System.ComponentModel",
    "System.Runtime.CompilerServices"
};

Step 3: Write the event/method boilerplate inside each class

In Database.tt, use WriteInsideClassBody:

Settings.WriteInsideClassBody = delegate(Table t)
{
    return
        "    public event PropertyChangedEventHandler PropertyChanged;" + Environment.NewLine +
        "    protected virtual void OnPropertyChanged([CallerMemberName] string name = null)" + Environment.NewLine +
        "    {" + Environment.NewLine +
        "        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));" + Environment.NewLine +
        "    }" + Environment.NewLine;
};

Step 4: Modify the property template

This step requires editing the Mustache property template in your file-based templates folder, or editing EF.Reverse.POCO.v3.ttinclude.

Warning: Editing EF.Reverse.POCO.v3.ttinclude directly means your changes will be lost when you upgrade the generator. Use file-based templates instead.

In the property template, find the line that generates properties:

public {{#if OverrideModifier}}override {{/if}}{{WrapIfNullable}} {{NameHumanCase}}

Replace it with a backing field + property with change notification:

private {{WrapIfNullable}} _{{NameHumanCase}};{{#newline}}
public {{#if OverrideModifier}}override {{/if}}{{WrapIfNullable}} {{NameHumanCase}} { get { return _{{NameHumanCase}}; } {{PrivateSetterForComputedColumns}}set { if(value != _{{NameHumanCase}}) { _{{NameHumanCase}} = value; OnPropertyChanged(); } } }{{PropertyInitialisers}}{{InlineComments}}{{#newline}}

Result

Generated classes will look like:

public partial class Order : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged([CallerMemberName] string name = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
    }

    private int _orderId;
    public int OrderId
    {
        get { return _orderId; }
        set { if (value != _orderId) { _orderId = value; OnPropertyChanged(); } }
    }

    private string _customerName;
    public string CustomerName
    {
        get { return _customerName; }
        set { if (value != _customerName) { _customerName = value; OnPropertyChanged(); } }
    }
}

Clone this wiki locally