Skip to content

Instantly share code, notes, and snippets.

@LGM-AdrianHum
Created November 6, 2015 04:24
Show Gist options
  • Save LGM-AdrianHum/08f1616de24da7eef0ba to your computer and use it in GitHub Desktop.
Save LGM-AdrianHum/08f1616de24da7eef0ba to your computer and use it in GitHub Desktop.
Yet another ViewModelBase
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Reflection;
using System.Collections.Specialized;
using System.Linq.Expressions;
namespace MVVMExamples
{
/// <summary>
/// Base class for all ViewModel classes in the application.
/// It provides support for property change notifications
/// and has a DisplayName property. This class is abstract.
/// </summary>
public abstract class ViewModelBase : INotifyPropertyChanged, IDisposable
{
#region Constructor
protected ViewModelBase()
{
}
#endregion // Constructor
#region DisplayName
/// <summary>
/// Returns the user-friendly name of this object.
/// Child classes can set this property to a new value,
/// or override it to determine the value on-demand.
/// </summary>
public virtual string DisplayName { get; protected set; }
#endregion // DisplayName
#region Debugging Aides
/// <summary>
/// Warns the developer if this object does not have
/// a public property with the specified name. This
/// method does not exist in a Release build.
/// </summary>
[Conditional("DEBUG")]
[DebuggerStepThrough]
public void VerifyPropertyName(string propertyName)
{
// Verify that the property name matches a real,
// public, instance property on this object.
if (TypeDescriptor.GetProperties(this)[propertyName] == null)
{
string msg = "Invalid property name: " + propertyName;
if (this.ThrowOnInvalidPropertyName)
throw new Exception(msg);
else
Debug.Fail(msg);
}
}
/// <summary>
/// Returns whether an exception is thrown, or if a Debug.Fail() is used
/// when an invalid property name is passed to the VerifyPropertyName method.
/// The default value is false, but subclasses used by unit tests might
/// override this property's getter to return true.
/// </summary>
protected virtual bool ThrowOnInvalidPropertyName { get; private set; }
#endregion // Debugging Aides
#region INotifyPropertyChanged Members
/// <summary>
/// Raised when a property on this object has a new value.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Raises this object's PropertyChanged event.
/// </summary>
/// <param name="propertyName">The property that has a new value.</param>
protected virtual void NotifyPropertyChanged(string propertyName)
{
this.VerifyPropertyName(propertyName);
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
protected virtual void NotifyPropertyChangedAll(object inOjbect)
{
foreach (PropertyInfo pi in inOjbect.GetType().GetProperties())
{
NotifyPropertyChanged(pi.Name);
}
}
public virtual void Refresh()
{
NotifyPropertyChangedAll(this);
}
#endregion // INotifyPropertyChanged Members
//http://weblogs.asp.net/fmarguerie/archive/2010/08/30/PropertyOf-INotifyPropertyChanged-PropertyChanged-strings-infoof.aspx
#region UpdatedNotifyPropertyChanged
public static PropertyInfo PropertyOf<T>(Expression<Func<T>> expression)
{
var memberExpr = expression.Body as MemberExpression;
// If the method gets a lambda expression that is not a member access,
// for example, () => x + y, an exception is thrown
if (memberExpr == null)
throw new ArgumentException("Expression \"" + expression +
"\" is not a valid member expression.");
var property = memberExpr.Member as PropertyInfo;
if (property == null)
throw new ArgumentException("Expression \"" + expression +
"\" does not reference a property.");
return property;
}
protected void NotifyPropertyChanged<T>(Expression<Func<T>> expression)
{
var handler = PropertyChanged;
if (handler == null)
return;
var propertyName = PropertyOf(expression).Name;
handler(this, new PropertyChangedEventArgs(propertyName));
}
protected void NotifyPropertyChanged()
{
var methodName = new StackFrame(1).GetMethod().Name;
if (!methodName.StartsWith("set_"))
throw new Exception("This overload of the NotifyPropertyChanged" +
"method must be called from a property setter.");
NotifyPropertyChanged(methodName.Substring("set_".Length));
}
#endregion
#region IDisposable Members
/// <summary>
/// Invoked when this object is being removed from the application
/// and will be subject to garbage collection.
/// </summary>
public void Dispose()
{
this.OnDispose();
}
/// <summary>
/// Child classes can override this method to perform
/// clean-up logic, such as removing event handlers.
/// </summary>
protected virtual void OnDispose()
{
}
#if DEBUG
/// <summary>
/// Useful for ensuring that ViewModel objects are properly garbage collected.
/// </summary>
~ViewModelBase()
{
string msg = string.Format("{0} ({1}) ({2}) Finalized", this.GetType().Name, this.DisplayName, this.GetHashCode());
System.Diagnostics.Debug.WriteLine(msg);
}
#endif
#endregion // IDisposable Members
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment