Microsoft should have implemented something snappy for INotifyPropertyChanged
, like in the automatic properties, just specify {get; set; notify;}
I
.Net 4.5 introduces a new Caller Information Attributes.
private void OnPropertyChanged<T>([CallerMemberName]string caller = null) {
// make sure only to call this if the value actually changes
var handler = PropertyChanged;
if (handler != null) {
handler(this, new PropertyChangedEventArgs(caller));
}
}
It's probably a good idea to add a comparer to the function as well.
EqualityComparer<T>.Default.Equals
More examples here and here
Also see Caller Information (C# and Visual Basic)
I suggest to use ReactiveProperty. This is the shortest method except Fody.
public class Data : INotifyPropertyChanged
{
// boiler-plate
...
// props
private string name;
public string Name
{
get { return name; }
set { SetField(ref name, value, "Name"); }
}
}
instead
public class Data
{
// Don't need boiler-plate and INotifyPropertyChanged
// props
public ReactiveProperty<string> Name { get; } = new ReactiveProperty<string>();
}
(DOCS)
Whilst there are obviously lots of ways to do this, with the exception of the AOP magic answers, none of the answers seem to look at setting a Model's property directly from the view model without having a local field to reference.
The issue is you can't reference a property. However, you can use an Action to set that property.
protected bool TrySetProperty<T>(Action<T> property, T newValue, T oldValue, [CallerMemberName] string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(oldValue, newValue))
{
return false;
}
property(newValue);
RaisePropertyChanged(propertyName);
return true;
}
This can be used like the following code extract.
public int Prop {
get => model.Prop;
set => TrySetProperty(x => model.Prop = x, value, model.Prop);
}
Check out this BitBucket repo for a full implementation of the method and a few different ways of achieving the same result, including a method that uses LINQ and a method that uses reflection. Do note that these methods are slower performance wise.
Other things you may want to consider when implementing these sorts of properties is the fact that the INotifyPropertyChang *ed *ing both use event argument classes.
If you have a large number of properties that are being set then the number of event argument class instances can be huge, you should consider caching them as they are one of the areas that a string explosion can occur.
Take a look at this implementation and explanation of why it was conceived.
Josh Smiths Blog
=> here my solution with the following features
public ResourceStatus Status
{
get { return _status; }
set
{
_status = value;
Notify(Npcea.Status,Npcea.Comments);
}
}
Based on the answer by Thomas which was adapted from an answer by Marc I've turned the reflecting property changed code into a base class:
public abstract class PropertyChangedBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
protected void OnPropertyChanged<T>(Expression<Func<T>> selectorExpression)
{
if (selectorExpression == null)
throw new ArgumentNullException("selectorExpression");
var me = selectorExpression.Body as MemberExpression;
// Nullable properties can be nested inside of a convert function
if (me == null)
{
var ue = selectorExpression.Body as UnaryExpression;
if (ue != null)
me = ue.Operand as MemberExpression;
}
if (me == null)
throw new ArgumentException("The body must be a member expression");
OnPropertyChanged(me.Member.Name);
}
protected void SetField<T>(ref T field, T value, Expression<Func<T>> selectorExpression, params Expression<Func<object>>[] additonal)
{
if (EqualityComparer<T>.Default.Equals(field, value)) return;
field = value;
OnPropertyChanged(selectorExpression);
foreach (var item in additonal)
OnPropertyChanged(item);
}
}
Usage is the same as Thomas' answer except that you can pass additional properties to notify for. This was necessary to handle calculated columns which need to be refreshed in a grid.
private int _quantity;
private int _price;
public int Quantity
{
get { return _quantity; }
set { SetField(ref _quantity, value, () => Quantity, () => Total); }
}
public int Price
{
get { return _price; }
set { SetField(ref _price, value, () => Price, () => Total); }
}
public int Total { get { return _price * _quantity; } }
I have this driving a collection of items stored in a BindingList exposed via a DataGridView. It has eliminated the need for me to do manual Refresh() calls to the grid.