A source generated approach, to turn your backing fields into properties that can fire events when their value changes - Automagically!
If this library helped you, consider buying me a coffee
Install via Nuget
Install-Package TomLonghurst.Events.NotifyValueChanged
- Auto-generated properties for backing fields that fire events
- Events that fire for computed properties
- Events that fire for specific types
- Events that fire for any type
- Custom property names
- Custom accessors for property getters and setters
This small class will generate this class!
- Make your class
partial
- Declare a
private
field - This is the backing field for which the property will be generated for. - Add the
[NotifyValueChange]
attribute to the field - That's it!
public partial class Person
{
[NotifyValueChange]
private string _name;
}
Your class now has a property called Name
- And you can subscribe to an event called OnNameValueChange
that will fire whenever the value of Name
changes.
You can do this for multiple fields, and each one should generate you a separate event that you can subscribe to called On{PropertyName}ValueChange
var person = new Person();
person.OnNameValueChange += (sender, eventArgs) =>
{
Console.WriteLine($"Name was: '{eventArgs.PreviousValue}' and is now '{eventArgs.NewValue}'\n");
};
person.Name = "Tom"; // Event will fire and log to the console "Name was: '' and is now 'Tom'"
person.Name = "Tom"; // No event will fire because the value hasn't changed
person.Name = ""; // Event will fire and log to the console "Name was: 'Tom' and is now ''"
PropertyName - Define a custom property name to be generated for your backing field
[NotifyValueChange(PropertyName = "FamilyName")]
private string _lastName;
Outputs
public String FamilyName { get { ... }; set { ... }; }
GetterAccessLevel and SetterAccessLevel - Define custom accessors for your generated properties
[NotifyValueChange(GetterAccessLevel = PropertyAccessLevel.PrivateProtected, SetterAccessLevel = PropertyAccessLevel.Internal)]
private string _middleName;
Outputs
internal String MiddleName { private protected get { ... }; set { ... }; }
If you have a property that has its value computed based on the value of a backing field with a [NotifyValueChange]
attribute, then this should automatically produce an event to subscribe to also.
This event will fire when any of the backing fields' values change.
public partial class Person
{
[NotifyValueChange]
private string _firstName;
[NotifyValueChange]
private string _lastName;
public string FullName => $"{_firstName} {_lastName}";
}
var person = new Person
{
FirstName = "Tom",
LastName = "Jones"
};
person.OnFullNameValueChange += (sender, eventArgs) =>
{
Console.WriteLine($"The Person's Full Name was: '{eventArgs.PreviousValue}' and is now '{eventArgs.NewValue}'\n");
};
person.LastName = "Longhurst"; // Will output The Person's Full Name was: 'Tom Jones' and is now 'Tom Longhurst'
If your class implements an interface and you want this event to be exposed on the interface, then:
- Make your interface partial
- Declare the property on the interface as normal
- Add the attribute
[GenerateInterfaceValueChangeEvent
]
public partial interface IPerson
{
[GenerateInterfaceValueChangeEvent]
public string Name { get; }
}
Any field with the [NotifyValueChange]
attribute will also fire an any value changed event
[NotifyAnyValueChange]
public partial class Person
{
[NotifyValueChange]
private string _name; // Will fire the AnyValueChange event because it has the NotifyValueChange attribute
[NotifyValueChange]
private int _age; // Will fire the AnyValueChange event because it has the NotifyValueChange attribute
}
var person = new Person();
person.OnAnyValueChange += (sender, eventArgs) =>
{
Console.WriteLine($"Property Name: {eventArgs.PropertyName} | Previous Value: {eventArgs.PreviousValue} | New Value: {eventArgs.NewValue}\n");
};
Any field with the [NotifyValueChange]
attribute will also fire an type specific value changed event if that type was passed into the [NotifyTypeValueChange(type)]
attribute
[NotifyTypeValueChange(typeof(string))]
public partial class Person
{
[NotifyValueChange]
private string _name; // Will fire the OnTypeStringValueChange event because it has the NotifyValueChange attribute combined with the NotifyTypeValueChange(string) attribute
[NotifyValueChange]
private int _age; // Will not fire the OnTypeStringValueChange event because it is not a string
}
var person = new Person();
person.OnTypeStringValueChange += (sender, eventArgs) =>
{
Console.WriteLine($"Property Name: {eventArgs.PropertyName} | Previous Value: {eventArgs.PreviousValue} | New Value: {eventArgs.NewValue}\n");
};