Skip to content

Instantly share code, notes, and snippets.

@ajai8085
Last active May 8, 2017 23:52
Show Gist options
  • Save ajai8085/52bbe0c50a3d3a8e42d0923a548309db to your computer and use it in GitHub Desktop.
Save ajai8085/52bbe0c50a3d3a8e42d0923a548309db to your computer and use it in GitHub Desktop.
Poor Man's Single Page MVVM with Event Aggregator (Missing validation)
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Threading;
using System.Linq;
namespace AJ.Mvvm
{
public static class CachedFactory
{
public class CachedItem
{
public ICachedItem CachedObject { get; set; }
public Type CachedItemType { get; set; }
public DateTime CachedAt { get; private set; }
public TimeSpan? ExpiresAfter { get; set; }
public CachedItem()
{
CachedAt = DateTime.Now;
}
public bool IsExpired()
{
if (ExpiresAfter.HasValue)
{
var res = (DateTime.Now - CachedAt.Add(ExpiresAfter.Value));
if (res.TotalMilliseconds > 0)
return true;
}
return false;
}
}
private static readonly ConcurrentDictionary<string, CachedItem> CachedViewModels =
new ConcurrentDictionary<string, CachedItem>();
/// <summary>
/// Caches created object and returns it . If object does not exists in the cache, this method will contact you .
/// </summary>
/// <typeparam name="TClass"></typeparam>
/// <param name="onCreateInstance"></param>
/// <returns></returns>
public static TClass CrateOrGetCachedObject<TClass>(Func<TClass> onCreateInstance,
TimeSpan? expiresafter = null)
where TClass : class
{
TClass instance = default(TClass);
var typetoCache = typeof(TClass);
CachedItem cachedViewModel = null;
var key = typetoCache.FullName;
if (!CachedViewModels.TryGetValue(key, out cachedViewModel))
{
instance = onCreateInstance();
var cachedItem = instance as ICachedItem;
if (cachedItem != null)
{
cachedViewModel = new CachedItem
{
ExpiresAfter = expiresafter,
CachedItemType = typetoCache,
CachedObject = cachedItem
};
CachedViewModels.TryAdd(key, cachedViewModel
);
cachedItem.OnResolveFromCache(false);
}
else
{
System.Diagnostics.Debugger.Log(0,"Default",
string.Format("Object {0} failed to cache , in order to cache implement ICachedItem",
typetoCache));
}
}
else
{
if (cachedViewModel.IsExpired())
{
CachedItem cachedItemLocal = null;
if (CachedViewModels.TryRemove(key, out cachedItemLocal))
{
instance = CrateOrGetCachedObject<TClass>(onCreateInstance, expiresafter);
}
else
{
cachedViewModel.CachedObject.OnResolveFromCache(true);
instance = cachedViewModel.CachedObject as TClass;
}
}
else
{
cachedViewModel.CachedObject.OnResolveFromCache(true);
instance = cachedViewModel.CachedObject as TClass;
}
}
return instance;
}
/// <summary>
/// This method will not cache an item if it is not implementing ICachedItem
/// </summary>
/// <param name="objectType"></param>
/// <param name="onCreateInstance"></param>
/// <returns></returns>
public static object CrateOrGetCachedObject(Type objectType, Func<object> onCreateInstance,
TimeSpan? expiresafter = null)
{
var typetoCache = objectType;
CachedItem cachedViewModel = null;
object instance = null;
var key = typetoCache.FullName;
if (!CachedViewModels.TryGetValue(key, out cachedViewModel))
{
instance = onCreateInstance();
var cachedItem = instance as ICachedItem;
if (cachedItem != null)
{
CachedViewModels.TryAdd(typetoCache.FullName,
new CachedItem
{
ExpiresAfter = expiresafter,
CachedItemType = typetoCache,
CachedObject = cachedItem
});
cachedItem.OnResolveFromCache(false);
}
else
{
System.Diagnostics.Debugger.Log(1,"Default",
string.Format("Object {0} failed to cache , in order to cache implement ICachedItem",
typetoCache));
}
}
else
{
if (cachedViewModel.IsExpired())
{
CachedItem cachedItemLocal = null;
if (CachedViewModels.TryRemove(key, out cachedItemLocal))
{
instance = CrateOrGetCachedObject(onCreateInstance, expiresafter);
}
else
{
cachedViewModel.CachedObject.OnResolveFromCache(true);
instance = cachedViewModel.CachedObject;
}
}
else
{
cachedViewModel.CachedObject.OnResolveFromCache(true);
instance = cachedViewModel.CachedObject;
}
}
return instance;
}
}
public static class ViewModelExtensions
{
public static bool? ShowDialog<TViewModel>(this TViewModel viewModel, bool maximized = true,
ParentWinFormOrWpfWindow ownerForm = null)
where TViewModel : ScreenBase
{
bool? result = null;
var viewModelType = typeof(TViewModel);
var bfound = false;
var freshBakedView = false;
Type viewType = null;
if (viewModelType.TryInferViewType(out viewType))
{
object view = null;
var isCachedView = false;
if (viewModel.View == null)
{
var cachedview = CachedFactory.CrateOrGetCachedObject(viewType, () =>
{
freshBakedView = true;
return Activator.CreateInstance(viewType);
});
view = cachedview;
isCachedView = !freshBakedView;
}
else
{
view = viewModel.View;
var cachedView = view as ICachedItem;
if (cachedView != null)
{
cachedView.OnResolveFromCache(true);
}
isCachedView = true;
}
var windowView = view as Window;
if (windowView != null)
{
SetParent(ownerForm, windowView); //Set the parent window for the WindowView
if (freshBakedView)
{
if (maximized)
{
//Fix for showing window on the working screen -> http://stackoverflow.com/questions/3121900/how-can-i-make-a-wpf-window-maximized-on-the-screen-with-the-mouse-cursor
windowView.SourceInitialized +=
(s, a) => windowView.WindowState = WindowState.Maximized;
}
//Let the window know about the viewmodel
windowView.DataContext = viewModel;
//Let the viewmodel know about the view
viewModel.View = windowView;
viewModel.ViewParent = windowView;
windowView.Loaded += (s, a) => viewModel.RaiseViewLoaded(isCachedView);
}
else
{
viewModel.RaiseViewLoaded(isCachedView);
}
bfound = true;
result = windowView.ShowDialog();
}
}
if (!bfound)
{
var windowViewDlg = new Window
{
Title = "View not found !!!!",
Content = new Label { Content = "View Not found for view model -> " + viewModelType.FullName }
};
result = windowViewDlg.ShowDialog();
}
return result;
}
private static void SetParent(ParentWinFormOrWpfWindow parentWindow, Window childWindow)
{
if (parentWindow != null && parentWindow.IsValidWinForm())
{
var helper =
new System.Windows.Interop.WindowInteropHelper(childWindow)
{
Owner = parentWindow.WinformsAdapter.Handle
}; //Set the owner of the WPF form as winform
childWindow.WindowStartupLocation = WindowStartupLocation.CenterOwner;
}
else if (parentWindow != null && parentWindow.IsValidWpfWindow())
{
childWindow.Owner = parentWindow.WpfWindowAdapter;
childWindow.WindowStartupLocation = WindowStartupLocation.CenterOwner;
}
else
{
childWindow.WindowStartupLocation = WindowStartupLocation.CenterScreen;
}
}
private static bool TryInferViewType(this Type viewModelType, out Type viewType)
{
var bFound = false;
var viewTypeName = viewModelType.FullName;
if (viewModelType.IsGenericType)
{
//Get the non generic viewmodel
var index = viewTypeName.IndexOf('`');
viewTypeName = index == -1 ? viewTypeName : viewTypeName.Substring(0, index);
}
viewType = null;
if (viewTypeName.EndsWith("ViewModel", StringComparison.InvariantCultureIgnoreCase))
{
var index = viewTypeName.LastIndexOf("Model", StringComparison.InvariantCultureIgnoreCase) - 1;
var viewName = viewTypeName.Substring(0, index + 1);
viewType = Type.GetType(viewName);
if (viewType == null)
{
viewType = viewModelType.Assembly.GetType(viewName);
}
bFound = true;
}
return bFound;
}
public static void SetWindowDefaults(this Window windowView, bool maximized)
{
if (maximized)
{
//Fix for showing window on the working screen -> http://stackoverflow.com/questions/3121900/how-can-i-make-a-wpf-window-maximized-on-the-screen-with-the-mouse-cursor
windowView.WindowStartupLocation = WindowStartupLocation.CenterScreen;
windowView.SourceInitialized += (s, a) => windowView.WindowState = WindowState.Maximized;
}
}
public static void SetWindowWithoutHeader(this Window windowView)
{
windowView.ResizeMode = ResizeMode.NoResize;
windowView.WindowStyle = WindowStyle.None;
}
}
public class ParentWinFormOrWpfWindow : IDisposable
{
public System.Windows.Forms.Form WinformsAdapter { get; private set; }
public Window WpfWindowAdapter { get; private set; }
public bool IsValidWinForm()
{
return WinformsAdapter != null;
}
public bool IsValidWpfWindow()
{
return WpfWindowAdapter != null;
}
private ParentWinFormOrWpfWindow() //Do not instantiate this class .
{
}
public void Dispose()
{
WinformsAdapter = null;
WpfWindowAdapter = null;
}
public static implicit operator ParentWinFormOrWpfWindow(System.Windows.Forms.Form rhs)
{
return new ParentWinFormOrWpfWindow() { WinformsAdapter = (rhs) };
}
public static implicit operator ParentWinFormOrWpfWindow(Window rhs)
{
return new ParentWinFormOrWpfWindow() { WpfWindowAdapter = (rhs) };
}
}
static public class ViewExtensions
{
/// <summary>
/// Executes the action on the UI thread.
/// </summary>
/// <param name="action">The action to execute.</param>
public static void OnUIThread(this Action action)
{
if (Dispatcher.CurrentDispatcher.CheckAccess())
{
action();
}
else
{
Dispatcher.CurrentDispatcher.BeginInvoke(action);
}
}
public static MemberInfo GetMemberInfo(this System.Linq.Expressions.Expression expression)
{
LambdaExpression lambdaExpression = (LambdaExpression)expression;
MemberExpression memberExpression;
if (lambdaExpression.Body is UnaryExpression)
{
UnaryExpression unaryExpression = (UnaryExpression)lambdaExpression.Body;
memberExpression = (MemberExpression)unaryExpression.Operand;
}
else
{
memberExpression = (MemberExpression)lambdaExpression.Body;
}
return memberExpression.Member;
}
}
public interface ICachedItem
{
void OnResolveFromCache(bool isResolvedFromCache);
}
public interface IHaveTitle
{
string Title { set; }
}
public interface IHaveView<TView> where TView : DependencyObject
{
TView View { get; set; }
}
public abstract class PropertyChagnedBase : INotifyPropertyChanged
{
protected bool _isDirty = false;
protected bool _isNotifying = true;
protected virtual bool SetValueNotify<T>(ref T oldValue, T newValue, Expression<Func<T>> propertyExpression)
{
if (EqualityComparer<T>.Default.Equals(oldValue, newValue))
{
return false;
}
oldValue = newValue;
NotifyOfPropertyChange(propertyExpression);
_isDirty = true;
return true;
}
protected virtual bool SetValueNotify<T>(ref T oldValue, T newValue,
[CallerMemberName] string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(oldValue, newValue))
{
return false;
}
oldValue = newValue;
NotifyOfPropertyChange(propertyName);
_isDirty = true;
return true;
}
protected virtual ICommand GetCommand(ref ICommand command, Action executeMethod,
Func<bool> canExecuteMethod = null)
{
if (command == null)
{
command = new DelegateCommand(executeMethod, canExecuteMethod);
}
return command;
}
/// <summary>
/// Occurs when a property value changes.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged =
delegate (object param0, PropertyChangedEventArgs param1)
{
};
/// <summary>
/// Notifies subscribers of the property change.
/// </summary>
/// <param name="propertyName">Name of the property.</param>
public virtual void NotifyOfPropertyChange([CallerMemberName] string propertyName = null)
{
if (_isNotifying)
{
new Action(() =>
{
this.OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}).OnUIThread();
}
}
/// <summary>
/// Notifies subscribers of the property change.
/// </summary>
/// <typeparam name="TProperty">The type of the property.</typeparam>
/// <param name="property">The property expression.</param>
public virtual void NotifyOfPropertyChange<TProperty>(Expression<Func<TProperty>> property)
{
this.NotifyOfPropertyChange(property.GetMemberInfo().Name);
}
/// <summary>
/// Raises the <see cref="E:Caliburn.Micro.PropertyChangedBase.PropertyChanged" /> event directly.
/// </summary>
/// <param name="e">The <see cref="T:System.ComponentModel.PropertyChangedEventArgs" /> instance containing the event data.</param>
[EditorBrowsable(EditorBrowsableState.Never)]
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
{
PropertyChangedEventHandler propertyChanged = this.PropertyChanged;
if (propertyChanged != null)
{
propertyChanged(this, e);
}
}
}
/// <summary>
/// A base class that implements the infrastructure for property change notification and automatically performs UI thread marshalling.
/// </summary>
public abstract class ScreenBase : ViewAwareScreenBase<DependencyObject>
{
protected virtual TViewType GetView<TViewType>() where TViewType : class
{
return View as TViewType;
}
protected bool GetView<TViewType>(Action<TViewType> cbfnAction) where TViewType : class
{
var view = GetView<TViewType>();
if (view != null)
{
cbfnAction(view);
return true;
}
else
{
return false;
}
}
}
/// <summary>
/// A base class that implements the infrastructure for property change notification and automatically performs UI thread marshalling.
/// </summary>
public abstract class ViewAwareScreenBase<TView> : INotifyPropertyChanged, IHaveTitle, IHaveView<TView>, IDisposable
where TView : DependencyObject
{
protected abstract void ResetValues();
TView _view;
public TView View
{
get { return _view; }
set { _view = value; }
}
/// <summary>
/// Trigger ResetValues function followed by the callback function onSetParameters if defined . Callback can be used for setting default values to view model
/// </summary>
/// <typeparam name="TViewModel"></typeparam>
/// <param name="onSetParameters"></param>
public virtual void ResetValues(Action onSetParameters)
{
ResetValues();
if (onSetParameters != null)
{
onSetParameters();
}
}
public DependencyObject ViewParent { get; set; }
private bool _isDirty;
public bool IsDirty
{
get { return _isDirty; }
set
{
if (_isDirty != value)
{
_isDirty = value;
this.NotifyOfPropertyChange();
}
}
}
public int CacheResolveCount { get; private set; }
public void RaiseViewLoaded(bool isCached)
{
if (isCached)
{
CacheResolveCount++;
}
OnViewLoaded(View);
}
/// <summary>
/// Called when View Window get loaded
/// </summary>
/// <param name="view"></param>
protected virtual void OnViewLoaded(DependencyObject view)
{
}
protected Window GetViewWindow()
{
var window = View as Window;
if (window != null)
return window;
else
window = ViewParent as Window;
if (window != null)
return window;
return Window.GetWindow(View);
}
public string Title
{
set
{
var window = GetViewWindow();
if (window != null)
{
window.Title = value;
}
}
}
protected bool SetValueNotify<T>(ref T oldValue, T newValue, Expression<Func<T>> propertyExpression)
{
if (EqualityComparer<T>.Default.Equals(oldValue, newValue))
{
return false;
}
oldValue = newValue;
NotifyOfPropertyChange(propertyExpression);
IsDirty = true;
return true;
}
protected bool SetValueNotify<T>(ref T oldValue, T newValue, [CallerMemberName] string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(oldValue, newValue))
{
return false;
}
oldValue = newValue;
NotifyOfPropertyChange(propertyName);
IsDirty = true;
return true;
}
protected ICommand GetCommand(ref ICommand command, Action executeMethod, Func<bool> canExecuteMethod = null)
{
if (command == null)
{
command = new DelegateCommand(executeMethod, canExecuteMethod);
}
return command;
}
protected ICommand GetCommandWithParam<T>(ref ICommand command, Action<T> executeMethod,
Func<T, bool> canExecuteMethod = null)
{
if (command == null)
{
command = new DelegateCommand<T>(executeMethod, canExecuteMethod);
}
return command;
}
protected ICommand GetCommand<TCommandParam>(ref ICommand command, Action<TCommandParam> executeMethod,
Func<TCommandParam, bool> canExecuteMethod = null)
{
if (command == null)
{
command = new DelegateCommand<TCommandParam>(executeMethod, canExecuteMethod);
}
return command;
}
/// <summary>
/// Gets a value indicating whether the control is in design mode
/// (running under Blend or Visual Studio).
/// </summary>
public bool IsInDesignMode
{
get { return IsInDesignModeStatic; }
}
private static bool? _isInDesignMode;
/// <summary>
/// Gets a value indicating whether the control is in design mode
/// (running in Blend or Visual Studio).
/// </summary>
public static bool IsInDesignModeStatic
{
get
{
if (!_isInDesignMode.HasValue)
{
DependencyProperty prop = DesignerProperties.IsInDesignModeProperty;
_isInDesignMode =
(bool)
DependencyPropertyDescriptor.FromProperty(prop, typeof(FrameworkElement)).Metadata.DefaultValue;
}
return _isInDesignMode.Value;
}
}
/// <summary>
/// Occurs when a property value changes.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged =
delegate (object param0, PropertyChangedEventArgs param1)
{
};
/// <summary>
/// Enables/Disables property change notification.
/// </summary>
public bool IsNotifying { get; set; }
/// <summary>
/// Creates an instance of <see cref="T:Caliburn.Micro.PropertyChangedBase" />.
/// </summary>
public ViewAwareScreenBase()
{
this.IsNotifying = true;
}
/// <summary>
/// Raises a change notification indicating that all bindings should be refresrefreshed.
/// </summary>
public void Refresh()
{
this.NotifyOfPropertyChange(string.Empty);
}
/// <summary>
/// Notifies subscribers of the property change.
/// </summary>
/// <param name="propertyName">Name of the property.</param>
public virtual void NotifyOfPropertyChange([CallerMemberName] string propertyName = null)
{
if (this.IsNotifying)
{
new Action(() =>
{
this.OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}).OnUIThread();
}
}
/// <summary>
/// Notifies subscribers of the property change.
/// </summary>
/// <typeparam name="TProperty">The type of the property.</typeparam>
/// <param name="property">The property expression.</param>
public void NotifyOfPropertyChange<TProperty>(Expression<Func<TProperty>> property)
{
this.NotifyOfPropertyChange(property.GetMemberInfo().Name);
}
/// <summary>
/// Raises the <see cref="E:Caliburn.Micro.PropertyChangedBase.PropertyChanged" /> event directly.
/// </summary>
/// <param name="e">The <see cref="T:System.ComponentModel.PropertyChangedEventArgs" /> instance containing the event data.</param>
[EditorBrowsable(EditorBrowsableState.Never)]
protected void OnPropertyChanged(PropertyChangedEventArgs e)
{
PropertyChangedEventHandler propertyChanged = this.PropertyChanged;
if (propertyChanged != null)
{
propertyChanged(this, e);
}
}
protected bool _isDisposed = false;
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
~ViewAwareScreenBase()
{
Dispose(false);
}
protected abstract void Dispose(bool isDisposing);
private WindowClosedDialogResults _windowDialogResult;
public virtual WindowClosedDialogResults WindowDialogResult
{
get { return _windowDialogResult; }
set { SetValueNotify(ref _windowDialogResult, value); }
}
protected virtual void CloseWindow(WindowClosedDialogResults dialogResult = WindowClosedDialogResults.Cancel)
{
WindowDialogResult = dialogResult;
var vw = GetViewWindow();
if (vw != null)
{
var cwv = vw as ICachedItem;
if (cwv == null)
{
vw.DialogResult = dialogResult == WindowClosedDialogResults.OK
? (bool?)true
: (dialogResult == WindowClosedDialogResults.Cancel ? (bool?)false : null);
vw.Close();
}
else
{
HideWindow(dialogResult);
}
}
}
/// <summary>
/// When you hide window , dialog result is available from Windowmanagerextensionmethod
/// </summary>
/// <param name="dialogResult"></param>
protected virtual void HideWindow(WindowClosedDialogResults dialogResult = WindowClosedDialogResults.Cancel)
{
WindowDialogResult = dialogResult;
var vw = GetViewWindow();
if (vw != null)
{
vw.Hide();
}
}
private bool _isBusy;
public virtual bool IsBusy
{
get { return _isBusy; }
set { SetValueNotify(ref _isBusy, value); }
}
public IDisposable ActivateBusyBlock(Action beforeAction = null, Action afterAction = null)
{
IsBusy = true;
if (beforeAction != null)
{
beforeAction();
}
return new DisposableObject(() =>
{
IsBusy = false;
if (afterAction != null)
{
afterAction();
}
});
}
//public DisposableObject<TProgress> ActivateBusyBlock<TProgress>()
//{
// IsBusy = true;
// return new DisposableObject<TProgress>(() => IsBusy = false, OnProgress);
//}
//protected virtual void OnProgress<TProgress>(TProgress progressMessage)
//{
//}
}
public class DisposableObject : IDisposable
{
private readonly Action _onDispose;
public DisposableObject(Action onDispose)
{
_onDispose = onDispose;
}
public void Dispose()
{
if (_onDispose != null)
{
_onDispose();
}
}
}
public class DisposableObject<TProgress> : DisposableObject
{
private readonly Action<TProgress> _onProgress;
public DisposableObject(Action onDispose, Action<TProgress> onProgress)
: base(onDispose)
{
_onProgress = onProgress;
}
public bool ReportProgress(TProgress progress)
{
if (_onProgress != null)
{
_onProgress(progress);
return true;
}
return false;
}
}
public enum WindowClosedDialogResults
{
None,
OK,
Cancel,
Abort,
Retry,
Ignore,
Yes,
No,
}
////http://blog.excastle.com/2010/07/25/mvvm-and-dialogresult-with-no-code-behind/comment-page-1/
//public static class DialogCloser
//{
// public static readonly DependencyProperty DialogResultProperty =
// DependencyProperty.RegisterAttached(
// "DialogResult",
// typeof(bool?),
// typeof(DialogCloser),
// new PropertyMetadata(DialogResultChanged));
// private static void DialogResultChanged(
// DependencyObject d,
// DependencyPropertyChangedEventArgs e)
// {
// var window = d as Window;
// if (window != null)
// window.DialogResult = e.NewValue as bool?;
// }
// public static void SetDialogResult(Window target, bool? value)
// {
// target.SetValue(DialogResultProperty, value);
// }
//}
}
namespace AJ.Mvvm
{
/// <summary>
/// This class allows delegating the commanding logic to methods passed as parameters,
/// and enables a View to bind commands to objects that are not part of the element tree.
/// </summary>
public class DelegateCommand : ICommand
{
#region Constructors
/// <summary>
/// Constructor
/// </summary>
public DelegateCommand(Action executeMethod)
: this(executeMethod, null, false)
{
}
/// <summary>
/// Constructor
/// </summary>
public DelegateCommand(Action executeMethod, Func<bool> canExecuteMethod)
: this(executeMethod, canExecuteMethod, false)
{
}
/// <summary>
/// Constructor
/// </summary>
public DelegateCommand(Action executeMethod, Func<bool> canExecuteMethod, bool isAutomaticRequeryDisabled)
{
if (executeMethod == null)
{
throw new ArgumentNullException("executeMethod");
}
_executeMethod = executeMethod;
_canExecuteMethod = canExecuteMethod;
_isAutomaticRequeryDisabled = isAutomaticRequeryDisabled;
}
#endregion
#region Public Methods
/// <summary>
/// Method to determine if the command can be executed
/// </summary>
public bool CanExecute()
{
if (_canExecuteMethod != null)
{
return _canExecuteMethod();
}
return true;
}
/// <summary>
/// Execution of the command
/// </summary>
public void Execute()
{
if (_executeMethod != null)
{
_executeMethod();
}
}
/// <summary>
/// Property to enable or disable CommandManager's automatic requery on this command
/// </summary>
public bool IsAutomaticRequeryDisabled
{
get
{
return _isAutomaticRequeryDisabled;
}
set
{
if (_isAutomaticRequeryDisabled != value)
{
if (value)
{
CommandManagerHelper.RemoveHandlersFromRequerySuggested(_canExecuteChangedHandlers);
}
else
{
CommandManagerHelper.AddHandlersToRequerySuggested(_canExecuteChangedHandlers);
}
_isAutomaticRequeryDisabled = value;
}
}
}
/// <summary>
/// Raises the CanExecuteChaged event
/// </summary>
public void RaiseCanExecuteChanged()
{
OnCanExecuteChanged();
}
/// <summary>
/// Protected virtual method to raise CanExecuteChanged event
/// </summary>
protected virtual void OnCanExecuteChanged()
{
CommandManagerHelper.CallWeakReferenceHandlers(_canExecuteChangedHandlers);
}
#endregion
#region ICommand Members
/// <summary>
/// ICommand.CanExecuteChanged implementation
/// </summary>
public event EventHandler CanExecuteChanged
{
add
{
if (!_isAutomaticRequeryDisabled)
{
CommandManager.RequerySuggested += value;
}
CommandManagerHelper.AddWeakReferenceHandler(ref _canExecuteChangedHandlers, value, 2);
}
remove
{
if (!_isAutomaticRequeryDisabled)
{
CommandManager.RequerySuggested -= value;
}
CommandManagerHelper.RemoveWeakReferenceHandler(_canExecuteChangedHandlers, value);
}
}
bool ICommand.CanExecute(object parameter)
{
return CanExecute();
}
void ICommand.Execute(object parameter)
{
Execute();
}
#endregion
#region Data
private readonly Action _executeMethod = null;
private readonly Func<bool> _canExecuteMethod = null;
private bool _isAutomaticRequeryDisabled = false;
private List<WeakReference> _canExecuteChangedHandlers;
#endregion
}
/// <summary>
/// This class allows delegating the commanding logic to methods passed as parameters,
/// and enables a View to bind commands to objects that are not part of the element tree.
/// </summary>
/// <typeparam name="T">Type of the parameter passed to the delegates</typeparam>
public class DelegateCommand<T> : ICommand
{
#region Constructors
/// <summary>
/// Constructor
/// </summary>
public DelegateCommand(Action<T> executeMethod)
: this(executeMethod, null, false)
{
}
/// <summary>
/// Constructor
/// </summary>
public DelegateCommand(Action<T> executeMethod, Func<T, bool> canExecuteMethod)
: this(executeMethod, canExecuteMethod, false)
{
}
/// <summary>
/// Constructor
/// </summary>
public DelegateCommand(Action<T> executeMethod, Func<T, bool> canExecuteMethod, bool isAutomaticRequeryDisabled)
{
if (executeMethod == null)
{
throw new ArgumentNullException("executeMethod");
}
_executeMethod = executeMethod;
_canExecuteMethod = canExecuteMethod;
_isAutomaticRequeryDisabled = isAutomaticRequeryDisabled;
}
#endregion
#region Public Methods
/// <summary>
/// Method to determine if the command can be executed
/// </summary>
public bool CanExecute(T parameter)
{
if (_canExecuteMethod != null)
{
return _canExecuteMethod(parameter);
}
return true;
}
/// <summary>
/// Execution of the command
/// </summary>
public void Execute(T parameter)
{
if (_executeMethod != null)
{
_executeMethod(parameter);
}
}
/// <summary>
/// Raises the CanExecuteChaged event
/// </summary>
public void RaiseCanExecuteChanged()
{
OnCanExecuteChanged();
}
/// <summary>
/// Protected virtual method to raise CanExecuteChanged event
/// </summary>
protected virtual void OnCanExecuteChanged()
{
CommandManagerHelper.CallWeakReferenceHandlers(_canExecuteChangedHandlers);
}
/// <summary>
/// Property to enable or disable CommandManager's automatic requery on this command
/// </summary>
public bool IsAutomaticRequeryDisabled
{
get
{
return _isAutomaticRequeryDisabled;
}
set
{
if (_isAutomaticRequeryDisabled != value)
{
if (value)
{
CommandManagerHelper.RemoveHandlersFromRequerySuggested(_canExecuteChangedHandlers);
}
else
{
CommandManagerHelper.AddHandlersToRequerySuggested(_canExecuteChangedHandlers);
}
_isAutomaticRequeryDisabled = value;
}
}
}
#endregion
#region ICommand Members
/// <summary>
/// ICommand.CanExecuteChanged implementation
/// </summary>
public event EventHandler CanExecuteChanged
{
add
{
if (!_isAutomaticRequeryDisabled)
{
CommandManager.RequerySuggested += value;
}
CommandManagerHelper.AddWeakReferenceHandler(ref _canExecuteChangedHandlers, value, 2);
}
remove
{
if (!_isAutomaticRequeryDisabled)
{
CommandManager.RequerySuggested -= value;
}
CommandManagerHelper.RemoveWeakReferenceHandler(_canExecuteChangedHandlers, value);
}
}
bool ICommand.CanExecute(object parameter)
{
// if T is of value type and the parameter is not
// set yet, then return false if CanExecute delegate
// exists, else return true
if (parameter == null &&
typeof(T).IsValueType)
{
return (_canExecuteMethod == null);
}
return CanExecute((T)parameter);
}
void ICommand.Execute(object parameter)
{
Execute((T)parameter);
}
#endregion
#region Data
private readonly Action<T> _executeMethod = null;
private readonly Func<T, bool> _canExecuteMethod = null;
private bool _isAutomaticRequeryDisabled = false;
private List<WeakReference> _canExecuteChangedHandlers;
#endregion
}
/// <summary>
/// This class contains methods for the CommandManager that help avoid memory leaks by
/// using weak references.
/// </summary>
internal static class CommandManagerHelper
{
internal static void CallWeakReferenceHandlers(List<WeakReference> handlers)
{
if (handlers != null)
{
// Take a snapshot of the handlers before we call out to them since the handlers
// could cause the array to me modified while we are reading it.
EventHandler[] callees = new EventHandler[handlers.Count];
int count = 0;
for (int i = handlers.Count - 1; i >= 0; i--)
{
WeakReference reference = handlers[i];
var handler = reference.Target as EventHandler;
if (handler == null)
{
// Clean up old handlers that have been collected
handlers.RemoveAt(i);
}
else
{
callees[count] = handler;
count++;
}
}
// Call the handlers that we snapshotted
for (int i = 0; i < count; i++)
{
EventHandler handler = callees[i];
handler(null, EventArgs.Empty);
}
}
}
internal static void AddHandlersToRequerySuggested(List<WeakReference> handlers)
{
if (handlers != null)
{
foreach (WeakReference handlerRef in handlers)
{
var handler = handlerRef.Target as EventHandler;
if (handler != null)
{
CommandManager.RequerySuggested += handler;
}
}
}
}
internal static void RemoveHandlersFromRequerySuggested(List<WeakReference> handlers)
{
if (handlers != null)
{
foreach (WeakReference handlerRef in handlers)
{
var handler = handlerRef.Target as EventHandler;
if (handler != null)
{
CommandManager.RequerySuggested -= handler;
}
}
}
}
internal static void AddWeakReferenceHandler(ref List<WeakReference> handlers, EventHandler handler)
{
AddWeakReferenceHandler(ref handlers, handler, -1);
}
internal static void AddWeakReferenceHandler(ref List<WeakReference> handlers, EventHandler handler, int defaultListSize)
{
if (handlers == null)
{
handlers = (defaultListSize > 0 ? new List<WeakReference>(defaultListSize) : new List<WeakReference>());
}
handlers.Add(new WeakReference(handler));
}
internal static void RemoveWeakReferenceHandler(List<WeakReference> handlers, EventHandler handler)
{
if (handlers != null)
{
for (int i = handlers.Count - 1; i >= 0; i--)
{
WeakReference reference = handlers[i];
var existingHandler = reference.Target as EventHandler;
if ((existingHandler == null) || (existingHandler == handler))
{
// Clean up old handlers that have been collected
// in addition to the handler that is to be removed.
handlers.RemoveAt(i);
}
}
}
}
}
}
namespace AJ.Mvvm.Messaging
{
static public class EnumerableExtensions
{
/// <summary>
/// Applies the action to each element in the list.
/// </summary>
/// <typeparam name="T">The enumerable item's type.</typeparam>
/// <param name="enumerable">The elements to enumerate.</param>
/// <param name="action">The action to apply to each item in the list.</param>
public static void Apply<T>(this IEnumerable<T> enumerable, Action<T> action)
{
foreach (var item in enumerable)
{
action(item);
}
}
}
public interface IMessage
{
}
public interface IHandleMessage
{
}
public interface IHandleMessage<in TMessage, out TResult> : IHandleMessage where TMessage : IMessage
{
TResult Handle(TMessage message);
}
public interface IHandleMessage<in TMessage> : IHandleMessage<TMessage, bool> where TMessage : IMessage
{
}
public interface IMessageAggregator
{
void Subscribe(object subscriber);
void Unsubscribe(object subscriber);
void Publish(object message);
}
public class MessageAggregator : IMessageAggregator
{
static MessageAggregator _messageAggregator;
public static IMessageAggregator GetInstance()
{
lock (typeof(MessageAggregator))
{
if (_messageAggregator == null)
_messageAggregator = new MessageAggregator();
}
return _messageAggregator;
}
private MessageAggregator()
{
}
public static Action<object, object> HandlerResultProcessing = (target, result) => { };
readonly List<Handler> handlers = new List<Handler>();
public bool HandlerExistsFor(Type messageType)
{
return handlers.Any(handler => handler.Handles(messageType) & !handler.IsDead);
}
public virtual void Subscribe(object subscriber)
{
if (subscriber == null)
{
throw new ArgumentNullException("subscriber");
}
lock (handlers)
{
if (handlers.Any(x => x.Matches(subscriber)))
{
return;
}
handlers.Add(new Handler(subscriber));
}
}
public virtual void Unsubscribe(object subscriber)
{
if (subscriber == null)
{
throw new ArgumentNullException("subscriber");
}
lock (handlers)
{
var found = handlers.FirstOrDefault(x => x.Matches(subscriber));
if (found != null)
{
handlers.Remove(found);
}
}
}
public virtual void Publish(object message)
{
Publish(message, x => x.OnUIThread());
}
public virtual void Publish(object message, Action<System.Action> marshal)
{
if (message == null)
{
throw new ArgumentNullException("message");
}
if (marshal == null)
{
throw new ArgumentNullException("marshal");
}
Handler[] toNotify;
lock (handlers)
{
toNotify = handlers.ToArray();
}
marshal(() =>
{
var messageType = message.GetType();
var dead = toNotify
.Where(handler => !handler.Handle(messageType, message))
.ToList();
if (dead.Any())
{
lock (handlers)
{
dead.Apply(x => handlers.Remove(x));
}
}
});
}
private class Handler
{
readonly WeakReference reference;
readonly Dictionary<Type, MethodInfo> supportedHandlers = new Dictionary<Type, MethodInfo>();
public bool IsDead
{
get { return reference.Target == null; }
}
public Handler(object handler)
{
reference = new WeakReference(handler);
var interfaces = handler.GetType().GetInterfaces()
.Where(x => typeof(IHandleMessage).IsAssignableFrom(x) && x.GetTypeInfo().IsGenericType);
foreach (var @interface in interfaces)
{
var type = @interface.GetGenericArguments()[0];
var method = @interface.GetMethod("Handle", new Type[] { type });
if (method != null)
{
supportedHandlers[type] = method;
}
}
}
public bool Matches(object instance)
{
return reference.Target == instance;
}
public bool Handle(Type messageType, object message)
{
var target = reference.Target;
if (target == null)
{
return false;
}
foreach (var pair in supportedHandlers)
{
if (pair.Key.IsAssignableFrom(messageType))
{
var result = pair.Value.Invoke(target, new[] { message });
if (result != null)
{
HandlerResultProcessing(target, result);
}
}
}
return true;
}
public bool Handles(Type messageType)
{
return supportedHandlers.Any(pair => pair.Key.IsAssignableFrom(messageType));
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment