Skip to content

Instantly share code, notes, and snippets.

@mlehmk
Last active August 29, 2015 14:02
Show Gist options
  • Save mlehmk/f83d1abd303b8e315ec7 to your computer and use it in GitHub Desktop.
Save mlehmk/f83d1abd303b8e315ec7 to your computer and use it in GitHub Desktop.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Threading;
public abstract class ModelBase : IDisposable, INotifyPropertyChanged
{
private Dictionary<string, object> _properties = new Dictionary<string, object>();
private static string MemberName<T>(LambdaExpression lambda)
{
if (lambda == null) throw new ArgumentNullException("lambda");
MemberExpression member_access = lambda.Body as MemberExpression;
if (member_access == null) throw new ArgumentException("Lambda body not a member access");
PropertyInfo property = member_access.Member as PropertyInfo;
if (property == null) throw new ArgumentException("Lambda body not a property access");
System.Diagnostics.Debug.Assert(property.PropertyType == typeof(T), "Property type check failed");
return property.Name;
}
protected void SetProperty<T>(Expression<Func<object,T>> member, T value)
{
SetProperty<T>(MemberName<T>(member), value);
}
protected void SetProperty<T>(string name, T value)
{
T oldvalue = GetProperty<T>(name);
if (!oldvalue.Equals(value))
{
_properties[name] = value;
Dispatcher dispatcher = Dispatcher;
if (dispatcher != null)
{
dispatcher.Invoke(() => OnPropertyChanged(name, oldvalue, value));
}
else
{
OnPropertyChanged(name, oldvalue, value);
}
}
}
protected Task SetPropertyAsync<T>(Expression<Func<object, T>> member, T value)
{
return SetPropertyAsync<T>(MemberName<T>(member), value);
}
protected async Task SetPropertyAsync<T>(string name, T value)
{
T oldvalue = GetProperty<T>(name);
if (!oldvalue.Equals(value))
{
_properties[name] = value;
Dispatcher dispatcher = Dispatcher;
if (dispatcher != null)
{
await dispatcher.InvokeAsync(() => OnPropertyChanged(name, oldvalue, value));
}
else
{
OnPropertyChanged(name, oldvalue, value);
}
}
}
protected T GetProperty<T>(Expression<Func<object, T>> member)
{
return GetProperty<T>(MemberName<T>(member));
}
protected T GetProperty<T>(string name)
{
object obj;
if (_properties.TryGetValue(name, out obj))
{
return (T)obj;
}
return default(T);
}
protected object GetProperty(string name)
{
object obj;
if (_properties.TryGetValue(name, out obj))
{
return obj;
}
return null;
}
protected virtual void OnPropertyChanged(string name, object oldvalue, object newvalue)
{
PropertyChangedEventHandler _event = _propertychanged;
if (_event != null)
{
PropertyChangedEventArgs _args = new PropertyChangedEventArgs(name);
_event(this, _args);
}
}
public Dispatcher Dispatcher { get; set; }
private PropertyChangedEventHandler _propertychanged;
public event PropertyChangedEventHandler PropertyChanged
{
add
{
if (disposed) return;
lock (this)
{
_propertychanged += value;
}
}
remove
{
if (disposed) return;
lock (this)
{
_propertychanged -= value;
}
}
}
#region IDisposable Members
/// <summary>
/// Internal variable which checks if Dispose has already been called
/// </summary>
private Boolean disposed;
/// <summary>
/// Releases unmanaged and - optionally - managed resources
/// </summary>
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
protected virtual void Dispose(Boolean disposing)
{
if (disposed)
{
return;
}
if (disposing)
{
_propertychanged = null;
}
//TODO: Unmanaged cleanup code here
disposed = true;
}
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
// Call the private Dispose(bool) helper and indicate
// that we are explicitly disposing
this.Dispose(true);
// Tell the garbage collector that the object doesn't require any
// cleanup when collected since Dispose was called explicitly.
GC.SuppressFinalize(this);
}
/// <summary>
/// The destructor for the class.
/// </summary>
~ModelBase()
{
this.Dispose(false);
}
#endregion
}
<?xml version="1.0" encoding="utf-8"?>
<CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2008/CodeSnippet">
<CodeSnippet Format="1.0.0">
<Header>
<Title>
ModelBase tracked property
</Title>
<Shortcut>trackedproperty</Shortcut>
</Header>
<Snippet>
<Code Language="CSharp">
<![CDATA[#region Tracked Property: $PropertyType$ $PropertyName$
public virtual $PropertyType$ $PropertyName$
{
get
{
return GetProperty<$PropertyType$>(_ => this.$PropertyName$);
}
set
{
SetProperty<$PropertyType$>(_ => this.$PropertyName$, value);
}
}
#endregion]]>
</Code>
<Declarations>
<Object>
<ID>PropertyType</ID>
<Default>object</Default>
</Object>
<Literal>
<ID>PropertyName</ID>
<Default>Name</Default>
</Literal>
</Declarations>
</Snippet>
</CodeSnippet>
</CodeSnippets>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment