Skip to content

Instantly share code, notes, and snippets.

@hwkr
Last active May 22, 2019 00:53
Show Gist options
  • Save hwkr/b3adce43392baca84347 to your computer and use it in GitHub Desktop.
Save hwkr/b3adce43392baca84347 to your computer and use it in GitHub Desktop.
WPF MVVM Notification Object Base
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