using System; using System.Linq; using System.Collections.Generic; using System.Reflection; using System.Diagnostics; namespace AIStudio.Wpf.DiagramDesigner { #region WeakAction Inner Class /// /// This class creates a weak delegate of form Action(Of Object) /// public class WeakAction { #region Data private readonly WeakReference _target; private readonly Type _ownerType; private readonly Type _actionType; private readonly string _methodName; #endregion #region Public Properties/Methods public WeakAction(object target, Type actionType, MethodBase mi) { if (target == null) { Debug.Assert(mi.IsStatic); _ownerType = mi.DeclaringType; } else _target = new WeakReference(target); _methodName = mi.Name; _actionType = actionType; } public Type ActionType { get { return _actionType; } } public bool HasBeenCollected { get { return (_ownerType == null && (_target == null || !_target.IsAlive)); } } public Delegate GetMethod() { if (_ownerType != null) { return Delegate.CreateDelegate(_actionType, _ownerType, _methodName); } if (_target != null && _target.IsAlive) { object target = _target.Target; if (target != null) return Delegate.CreateDelegate(_actionType, target, _methodName); } return null; } #endregion } #endregion /// /// This class creates a simple Mediator which loosely connects different objects together. /// The message handlers are organized using string-based message keys and are held in a WeakReference /// collection. /// public class Mediator { #region Data static readonly Mediator instance = new Mediator(); static readonly object syncLock = new object(); private readonly Dictionary> _registeredHandlers = new Dictionary>(); #endregion #region Ctor static Mediator() { } private Mediator() { } #endregion #region Private Methods /// /// Performs the actual registration of a target /// /// Key to store in dictionary /// Delegate type /// Method private void RegisterHandler(object key, Type actionType, Delegate handler) { var action = new WeakAction(handler.Target, actionType, handler.Method); lock (_registeredHandlers) { List wr; if (_registeredHandlers.TryGetValue(key, out wr)) { if (wr.Count > 0) { WeakAction wa = wr[0]; if (wa.ActionType != actionType && !wa.ActionType.IsAssignableFrom(actionType)) throw new ArgumentException("Invalid key passed to RegisterHandler - existing handler has incompatible parameter type"); } wr.Add(action); } else { wr = new List { action }; _registeredHandlers.Add(key, wr); } } } /// /// Performs the unregistration from a target /// /// Key to store in dictionary /// Delegate type /// Method private void UnregisterHandler(object key, Type actionType, Delegate handler) { lock (_registeredHandlers) { List wr; if (_registeredHandlers.TryGetValue(key, out wr)) { wr.RemoveAll(wa => handler == wa.GetMethod() && actionType == wa.ActionType); if (wr.Count == 0) _registeredHandlers.Remove(key); } } } /// /// This method broadcasts a message to all message targets for a given /// message key and passes a parameter. /// /// Message key /// Message parameter /// True/False if any handlers were invoked. private bool NotifyColleagues(object key, object message) { List wr; List wrCopy = new List(); lock (_registeredHandlers) { if (!_registeredHandlers.TryGetValue(key, out wr)) return false; else { foreach (var weakRe in wr) { wrCopy.Add(weakRe); } } } foreach (var cb in wrCopy) { Delegate action = cb.GetMethod(); if (action != null) action.DynamicInvoke(message); } lock (_registeredHandlers) { wr.RemoveAll(wa => wa.HasBeenCollected); } return true; } #endregion #region Public Properties/Methods /// /// Singleton Instance /// public static Mediator Instance { get { return instance; } } /// /// This registers a Type with the mediator. Any methods decorated with will be /// registered as target method handlers for the given message key. /// /// Object to register public void Register(object view) { // Look at all instance/static methods on this object type. foreach (var mi in view.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public)) { // See if we have a target attribute - if so, register the method as a handler. foreach (var att in mi.GetCustomAttributes(typeof(MediatorMessageSinkAttribute), true)) { var mha = (MediatorMessageSinkAttribute)att; var pi = mi.GetParameters(); if (pi.Length != 1) throw new InvalidCastException("Cannot cast " + mi.Name + " to Action delegate type."); Type actionType = typeof(Action<>).MakeGenericType(pi[0].ParameterType); object key = (mha.MessageKey) ?? actionType; if (mi.IsStatic) RegisterHandler(key, actionType, Delegate.CreateDelegate(actionType, mi)); else RegisterHandler(key, actionType, Delegate.CreateDelegate(actionType, view, mi.Name)); } } } /// /// This method unregisters a type from the message mediator. /// /// Object to unregister public void Unregister(object view) { foreach (var mi in view.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public)) { foreach (var att in mi.GetCustomAttributes(typeof(MediatorMessageSinkAttribute), true)) { var mha = (MediatorMessageSinkAttribute)att; var pi = mi.GetParameters(); if (pi.Length != 1) throw new InvalidCastException("Cannot cast " + mi.Name + " to Action delegate type."); Type actionType = typeof(Action<>).MakeGenericType(pi[0].ParameterType); object key = (mha.MessageKey) ?? actionType; if (mi.IsStatic) UnregisterHandler(key, actionType, Delegate.CreateDelegate(actionType, mi)); else UnregisterHandler(key, actionType, Delegate.CreateDelegate(actionType, view, mi.Name)); } } } /// /// This registers a specific method as a message handler for a specific type. /// /// Message key /// Handler method public void RegisterHandler(string key, Action handler) { RegisterHandler(key, handler.GetType(), handler); } /// /// This registers a specific method as a message handler for a specific type. /// /// Handler method public void RegisterHandler(Action handler) { RegisterHandler(typeof(Action), handler.GetType(), handler); } /// /// This unregisters a method as a handler. /// /// Message key /// Handler public void UnregisterHandler(string key, Action handler) { UnregisterHandler(key, handler.GetType(), handler); } /// /// This unregisters a method as a handler for a specific type /// /// Handler public void UnregisterHandler(Action handler) { UnregisterHandler(typeof(Action), handler.GetType(), handler); } /// /// This method broadcasts a message to all message targets for a given /// message key and passes a parameter. /// /// Message key /// Message parameter /// True/False if any handlers were invoked. public bool NotifyColleagues(string key, T message) { return NotifyColleagues((object)key, message); } /// /// This method broadcasts a message to all message targets for a given parameter type. /// If a derived type is passed, any handlers for interfaces or base types will also be /// invoked. /// /// Message parameter /// True/False if any handlers were invoked. public bool NotifyColleagues(T message) { Type actionType = typeof(Action<>).MakeGenericType(typeof(T)); var keyList = from key in _registeredHandlers.Keys where key is Type && ((Type)key).IsAssignableFrom(actionType) select key; bool rc = false; foreach (var key in keyList) rc |= NotifyColleagues(key, message); return rc; } /// /// This method broadcasts a message to all message targets for a given /// message key and passes a parameter. The message targets are all called /// asynchronously and any resulting exceptions are ignored. /// /// Message key /// Message parameter public void NotifyColleaguesAsync(string key, T message) { Func smaFunc = NotifyColleagues; smaFunc.BeginInvoke(key, message, ia => { try { smaFunc.EndInvoke(ia); } catch { } }, null); } /// /// This method broadcasts a message to all message targets for a given parameter type. /// If a derived type is passed, any handlers for interfaces or base types will also be /// invoked. The message targets are all called asynchronously and any resulting exceptions /// are ignored. /// /// Message parameter public void NotifyColleaguesAsync(T message) { Func smaFunc = NotifyColleagues; smaFunc.BeginInvoke(message, ia => { try { smaFunc.EndInvoke(ia); } catch { } }, null); } #endregion } }