namespace Caliburn.Micro.Core { using System; using System.Collections.Generic; using System.Linq; using System.Reflection; /// /// Enables loosely-coupled publication of and subscription to events. /// public class EventAggregator : IEventAggregator { static readonly ILog Log = LogManager.GetLog(typeof(EventAggregator)); readonly List handlers = new List(); /// /// Processing of handler results on publication thread. /// public static Action HandlerResultProcessing = (target, result) => { }; /// /// Searches the subscribed handlers to check if we have a handler for /// the message type supplied. /// /// The message type to check with /// True if any handler is found, false if not. public bool HandlerExistsFor(Type messageType) { return handlers.Any(handler => handler.Handles(messageType) & !handler.IsDead); } /// /// Subscribes an instance to all events declared through implementations of /// /// The instance to subscribe for event publication. 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)); } } /// /// Unsubscribes the instance from all events. /// /// The instance to unsubscribe. 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 bool IsHandlerDead(object subscriber) { if (subscriber == null) { throw new ArgumentNullException("subscriber"); } lock (handlers) { foreach (var @handler in handlers) if (@handler.IsDead) handlers.Remove(@handler); if (handlers.Any(x => x.Matches(subscriber))) return false; return true; } } /// /// Publishes a message. /// /// The message instance. /// Allows the publisher to provide a custom thread marshaller for the message publication. public virtual void Publish(object message, 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)); } } }); } class Handler { readonly object reference; readonly Dictionary supportedHandlers = new Dictionary(); public bool IsDead { //get { return reference.Target == null; } get { return reference == null; } } public Handler(object handler) { //reference = new WeakReference(handler); reference = handler; var interfaces = handler.GetType().GetInterfaces() .Where(x => typeof(IHandle).IsAssignableFrom(x) && x.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 == instance; } public bool Handle(Type messageType, object message) { //var target = reference.Target; var target = reference; if (target == null) { Log.Info("Subscriber missing."); 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)); } } } }