Last active
May 22, 2019 00:53
-
-
Save hwkr/b3adce43392baca84347 to your computer and use it in GitHub Desktop.
WPF MVVM Notification Object Base
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public abstract class NotificationObjectBase : INotifyPropertyChanged | |
{ | |
/// <summary> | |
/// Occurs when a property value changes. | |
/// </summary> | |
public virtual event PropertyChangedEventHandler PropertyChanged; | |
/// <summary> | |
/// This can be used to indicate that all property values have changed. | |
/// </summary> | |
public void RaiseAllPropertiesChanged() | |
{ | |
PropertyChanged.Raise(this, new PropertyChangedEventArgs(string.Empty)); | |
} | |
/// <summary> | |
/// This raises the INotifyPropertyChanged.PropertyChanged event to indicate | |
/// a specific property has changed value. This version provides a compile-time safe | |
/// way to indicate the property through the use of an expression tree / lambda. | |
/// Be aware that for high-volume changes this version might be much slower than | |
/// the above "magic-string" version due to the creation of an expression and runtime lookup. | |
/// </summary> | |
/// <example> | |
/// <![CDATA[ | |
/// // Raise the PropertyChanged event for the Name property, even from outside the setter | |
/// RaisePropertyChanged(() => this.Name); | |
/// ]]> | |
/// </example> | |
/// <typeparam name="T">Type where it is being raised.</typeparam> | |
/// <param name="propExpr">Property to raise PropertyChanged event on.</param> | |
protected virtual void RaisePropertyChanged<T>(Expression<Func<T>> propExpr) | |
{ | |
PropertyChangedEventHandler handler = PropertyChanged; | |
if (handler != null) | |
{ | |
var prop = (PropertyInfo)((MemberExpression)propExpr.Body).Member; | |
handler(this, new PropertyChangedEventArgs(prop.Name)); | |
} | |
} | |
/// <summary> | |
/// This raises the INotifyPropertyChanged.PropertyChanged event to indicate | |
/// a specific property has changed value. | |
/// </summary> | |
/// <example> | |
/// <![CDATA[ | |
/// public string Name | |
/// { | |
/// get { return _name; } | |
/// set | |
/// { | |
/// _name = value; | |
/// RaisePropertyChanged(); | |
/// } | |
/// } | |
/// ]]> | |
/// </example> | |
/// <param name="propertyName">Name of the property to raise PropertyChanged event on.</param> | |
protected virtual void RaisePropertyChanged([CallerMemberName] string propertyName = null) | |
{ | |
Debug.Assert(!string.IsNullOrEmpty(propertyName) && GetType().GetProperty(propertyName) != null); | |
PropertyChanged.Raise(this, new PropertyChangedEventArgs(propertyName)); | |
} | |
/// <summary> | |
/// This is used to set a specific value for a property and raise the | |
/// INotifyPropertyChanged.PropertyChanged event if that value changed. | |
/// </summary> | |
/// <example> | |
/// <![CDATA[ | |
/// public string Name | |
/// { | |
/// get { return _name; } | |
/// set | |
/// { | |
/// // Set _name to value and raise the PropertyChanged event on Name if _name changed | |
/// SetPropertyValueWithNotify<string>(ref _name, value); | |
/// // Set it with type inference | |
/// SetPropertyValueWithNotify(ref _name, value); | |
/// } | |
/// } | |
/// ]]> | |
/// </example> | |
/// <typeparam name="T">Type where it is being raised.</typeparam> | |
/// <param name="storageField">The backing field behind the property.</param> | |
/// <param name="newValue">The new value to set the backing field to.</param> | |
/// <param name="propertyName">Name of the property to raise PropertyChanged event on.</param> | |
/// <returns> | |
/// <value>true</value> if the backing field changed value and fired a PropertyChanged event, <value>false</value> otherwise. | |
/// </returns> | |
protected virtual bool SetPropertyValueWithNotify<T>(ref T storageField, T newValue, [CallerMemberName] string propertyName = null) | |
{ | |
Debug.Assert(!string.IsNullOrEmpty(propertyName) && GetType().GetProperty(propertyName) != null); | |
if (object.Equals(storageField, newValue)) | |
{ | |
return false; | |
} | |
storageField = newValue; | |
PropertyChanged.Raise(this, new PropertyChangedEventArgs(propertyName)); | |
return true; | |
} | |
/// <summary> | |
/// This is used to set a specific value for a property when that value is | |
/// itself a property of another object and raise the INotifyPropertyChanged.PropertyChanged | |
/// event if that value changed. | |
/// </summary> | |
/// <example> | |
/// <![CDATA[ | |
/// public string Name | |
/// { | |
/// get { return _name; } | |
/// set | |
/// { | |
/// // Set _name to value and raise the PropertyChanged event on Name if _name changed | |
/// SetPropertyValueWithNotify<TObject, TProperty>(_object, obj => obj.Property, value); | |
/// // Set it with type inference (way easier) | |
/// SetPropertyValueWithNotify(_object, obj => obj.Property, value); | |
/// } | |
/// } | |
/// ]]> | |
/// </example> | |
/// <typeparam name="TSource">Type where it is being raised.</typeparam> | |
/// <typeparam name="TProperty">Type of the object's property being changed.</typeparam> | |
/// <param name="source">The source object on which to change the property.</param> | |
/// <param name="propertyLambda">Lambda expression pointing to the property.</param> | |
/// <param name="newValue">The new value to set the backing field to.</param> | |
/// <param name="propertyName">Name of the property to raise PropertyChanged event on.</param> | |
/// <returns> | |
/// <value>true</value> if the backing field changed value and fired a PropertyChanged event, <value>false</value> otherwise. | |
/// </returns> | |
protected virtual bool SetPropertyValueWithNotify<TSource, TProperty>( | |
TSource source, | |
Expression<Func<TSource, TProperty>> propertyLambda, | |
TProperty newValue, | |
[CallerMemberName] string propertyName = null) | |
{ | |
Debug.Assert(!string.IsNullOrEmpty(propertyName) && GetType().GetProperty(propertyName) != null); | |
PropertyInfo property = GetPropertyInfo(source, propertyLambda); | |
if (object.Equals(property.GetValue(source), newValue)) | |
{ | |
return false; | |
} | |
property.SetValue(source, newValue); | |
PropertyChanged.Raise(this, new PropertyChangedEventArgs(propertyName)); | |
return true; | |
} | |
private PropertyInfo GetPropertyInfo<TSource, TProperty>( | |
TSource source, | |
Expression<Func<TSource, TProperty>> propertyLambda) | |
{ | |
Type type = typeof(TSource); | |
MemberExpression member = propertyLambda.Body as MemberExpression; | |
if (member == null) | |
throw new ArgumentException(string.Format( | |
"Expression '{0}' refers to a method, not a property.", | |
propertyLambda.ToString())); | |
PropertyInfo propInfo = member.Member as PropertyInfo; | |
if (propInfo == null) | |
throw new ArgumentException(string.Format( | |
"Expression '{0}' refers to a field, not a property.", | |
propertyLambda.ToString())); | |
if (type != propInfo.ReflectedType && | |
!type.IsSubclassOf(propInfo.ReflectedType)) | |
throw new ArgumentException(string.Format( | |
"Expression '{0}' refers to a property that is not from type {1}.", | |
propertyLambda.ToString(), | |
type)); | |
return propInfo; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment