Skip to content

Instantly share code, notes, and snippets.

@makomweb
Last active March 28, 2020 22:22
Show Gist options
  • Save makomweb/a761768a5b16e4ca2740 to your computer and use it in GitHub Desktop.
Save makomweb/a761768a5b16e4ca2740 to your computer and use it in GitHub Desktop.
This is a view model base implementation which can be used in well known MVVM scenarios. It stores all the values in a backing dictionary and accesses it through the property name specified by the lambda expression.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq.Expressions;
namespace Playground {
// Notice the 3 occurrencies of the Title identifier and the abscence of additional backing fields!
public class ExampleViewModel : ViewModelBase {
public string Title {
get { return GetValue(() => Title); }
set { SetValue(() => Title, value); }
}
}
public abstract class ViewModelBase : INotifyPropertyChanged {
private readonly Dictionary<string, object> _propertyValueStorage = new Dictionary<string, object>();
public event PropertyChangedEventHandler PropertyChanged;
protected T GetValue<T>(Expression<Func<T>> property) {
var expression = property as LambdaExpression;
if (expression == null) {
throw new ArgumentException("The property is not a valid lambda expression!", "property");
}
var propertyName = ExtractPropertyName(expression);
return GetValue<T>(propertyName);
}
private T GetValue<T>(string propertyName) {
object value;
if (_propertyValueStorage.TryGetValue(propertyName, out value)) {
return (T) value;
}
return default(T);
}
protected void SetValue<T>(Expression<Func<T>> property, T value) {
var expression = property as LambdaExpression;
if (expression == null) {
throw new ArgumentException("The property is not a valid lambda expression!", "property");
}
var propertyName = ExtractPropertyName(expression);
var currentValue = GetValue<T>(propertyName);
if (Equals(currentValue, value))
return;
_propertyValueStorage[propertyName] = value;
if (PropertyChanged != null) {
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
protected void RaisePropertyChanged() {
if (PropertyChanged == null)
return;
var stackTrace = new StackTrace();
var callingMethodName = stackTrace.GetFrame(1).GetMethod().Name;
if (!callingMethodName.Contains("_"))
return;
var propertyName = callingMethodName.Split('_')[1];
if (!string.IsNullOrEmpty(propertyName)) {
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
protected void RaisePropertyChanged(Expression<Func<object>> expression) {
if (PropertyChanged == null)
return;
var body = expression.Body as MemberExpression;
if (body == null && expression.Body is UnaryExpression) {
var unaryExpression = expression.Body as UnaryExpression;
body = unaryExpression.Operand as MemberExpression;
}
PropertyChanged(this, new PropertyChangedEventArgs(body.Member.Name));
}
[Obsolete("Use the overload which takes a lambda instead!")]
protected void RaisePropertyChanged(string propertyName) {
AssertPropertyExistence(propertyName);
if (PropertyChanged != null) {
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
[Conditional("DEBUG")]
private void AssertPropertyExistence(string propertyName) {
var propertyDescriptor = TypeDescriptor.GetProperties(this)[propertyName];
if (propertyDescriptor == null) {
#if true
Debug.Fail(string.Format("The property with the propertyName '{0}' doesn't exist.", propertyName));
#else
throw new InvalidOperationException(string.Format("The property with the propertyName '{0}' doesn't exist.", propertyName));
#endif
}
}
private static string ExtractPropertyName(LambdaExpression expression) {
MemberExpression memberExpression;
if (expression.Body is UnaryExpression) {
var unaryExpression = expression.Body as UnaryExpression;
memberExpression = unaryExpression.Operand as MemberExpression;
}
else {
memberExpression = expression.Body as MemberExpression;
}
return memberExpression.Member.Name;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment