Automatically INotifyPropertyChanged
EDIT: The author of NotifyPropertyWeaver has deprecated the tool in favor of the more general Fody. (A migration guide for people moving from weaver to fody is available.)
A very convenient tool I've used for my projects is Notify Property Weaver Fody.
It installs itself as a build step in your projects and during compilation injects code that raises the PropertyChanged
event.
Making properties raise PropertyChanged is done by putting special attributes on them:
[ImplementPropertyChanged]
public string MyProperty { get; set; }
As a bonus, you can also specify relationships for properties that depend on other properties
[ImplementPropertyChanged]
public double Radius { get; set; }
[DependsOn("Radius")]
public double Area
{
get { return Radius * Radius * Math.PI; }
}
The nameof operator was implemented in C# 6.0 with .NET 4.6 and VS2015 in July 2015. The following is still valid for C# < 6.0
We use the code below (From http://www.ingebrigtsen.info/post/2008/12/11/INotifyPropertyChanged-revisited.aspx). Works great :)
public static class NotificationExtensions
{
#region Delegates
/// <summary>
/// A property changed handler without the property name.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="sender">The object that raised the event.</param>
public delegate void PropertyChangedHandler<TSender>(TSender sender);
#endregion
/// <summary>
/// Notifies listeners about a change.
/// </summary>
/// <param name="EventHandler">The event to raise.</param>
/// <param name="Property">The property that changed.</param>
public static void Notify(this PropertyChangedEventHandler EventHandler, Expression<Func<object>> Property)
{
// Check for null
if (EventHandler == null)
return;
// Get property name
var lambda = Property as LambdaExpression;
MemberExpression memberExpression;
if (lambda.Body is UnaryExpression)
{
var unaryExpression = lambda.Body as UnaryExpression;
memberExpression = unaryExpression.Operand as MemberExpression;
}
else
{
memberExpression = lambda.Body as MemberExpression;
}
ConstantExpression constantExpression;
if (memberExpression.Expression is UnaryExpression)
{
var unaryExpression = memberExpression.Expression as UnaryExpression;
constantExpression = unaryExpression.Operand as ConstantExpression;
}
else
{
constantExpression = memberExpression.Expression as ConstantExpression;
}
var propertyInfo = memberExpression.Member as PropertyInfo;
// Invoke event
foreach (Delegate del in EventHandler.GetInvocationList())
{
del.DynamicInvoke(new[]
{
constantExpression.Value, new PropertyChangedEventArgs(propertyInfo.Name)
});
}
}
/// <summary>
/// Subscribe to changes in an object implementing INotifiyPropertyChanged.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="ObjectThatNotifies">The object you are interested in.</param>
/// <param name="Property">The property you are interested in.</param>
/// <param name="Handler">The delegate that will handle the event.</param>
public static void SubscribeToChange<T>(this T ObjectThatNotifies, Expression<Func<object>> Property, PropertyChangedHandler<T> Handler) where T : INotifyPropertyChanged
{
// Add a new PropertyChangedEventHandler
ObjectThatNotifies.PropertyChanged += (s, e) =>
{
// Get name of Property
var lambda = Property as LambdaExpression;
MemberExpression memberExpression;
if (lambda.Body is UnaryExpression)
{
var unaryExpression = lambda.Body as UnaryExpression;
memberExpression = unaryExpression.Operand as MemberExpression;
}
else
{
memberExpression = lambda.Body as MemberExpression;
}
var propertyInfo = memberExpression.Member as PropertyInfo;
// Notify handler if PropertyName is the one we were interested in
if (e.PropertyName.Equals(propertyInfo.Name))
{
Handler(ObjectThatNotifies);
}
};
}
}
Used for example this way:
public class Employee : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _firstName;
public string FirstName
{
get { return this._firstName; }
set
{
this._firstName = value;
this.PropertyChanged.Notify(()=>this.FirstName);
}
}
}
private void firstName_PropertyChanged(Employee sender)
{
Console.WriteLine(sender.FirstName);
}
employee = new Employee();
employee.SubscribeToChange(() => employee.FirstName, firstName_PropertyChanged);
Some syntax errors in the example may exist. Didn't test it. But you should have the concept there at least :)
EDIT: I see now that you may have wanted even less work, but yeah... the stuff above at least makes it a lot easier. And you prevent all the scary problems with refering to properties using strings.
The Framework 4.5 provides us with the CallerMemberNameAttribute
, which makes passing the property name as a string unnecessary:
private string m_myProperty;
public string MyProperty
{
get { return m_myProperty; }
set
{
m_myProperty = value;
OnPropertyChanged();
}
}
private void OnPropertyChanged([CallerMemberName] string propertyName = "none passed")
{
// ... do stuff here ...
}
Similar to Svish's solution, just replacing lambda awesomeness with boring framework functionality ;-)
If you're working on Framework 4.0 with KB2468871 installed, you can install the Microsoft BCL Compatibility Pack via nuget, which also provides this attribute.