From 17fdde66d94155fc62a034fa6658995bef6fd6e5 Mon Sep 17 00:00:00 2001 From: Jason Smith Date: Tue, 22 Mar 2016 13:02:25 -0700 Subject: Initial import --- Xamarin.Forms.Core/MessagingCenter.cs | 131 ++++++++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 Xamarin.Forms.Core/MessagingCenter.cs (limited to 'Xamarin.Forms.Core/MessagingCenter.cs') diff --git a/Xamarin.Forms.Core/MessagingCenter.cs b/Xamarin.Forms.Core/MessagingCenter.cs new file mode 100644 index 00000000..973531ab --- /dev/null +++ b/Xamarin.Forms.Core/MessagingCenter.cs @@ -0,0 +1,131 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Xamarin.Forms +{ + public static class MessagingCenter + { + static readonly Dictionary, List>>> s_callbacks = + new Dictionary, List>>>(); + + public static void Send(TSender sender, string message, TArgs args) where TSender : class + { + if (sender == null) + throw new ArgumentNullException("sender"); + InnerSend(message, typeof(TSender), typeof(TArgs), sender, args); + } + + public static void Send(TSender sender, string message) where TSender : class + { + if (sender == null) + throw new ArgumentNullException("sender"); + InnerSend(message, typeof(TSender), null, sender, null); + } + + public static void Subscribe(object subscriber, string message, Action callback, TSender source = null) where TSender : class + { + if (subscriber == null) + throw new ArgumentNullException("subscriber"); + if (callback == null) + throw new ArgumentNullException("callback"); + + Action wrap = (sender, args) => + { + var send = (TSender)sender; + if (source == null || send == source) + callback((TSender)sender, (TArgs)args); + }; + + InnerSubscribe(subscriber, message, typeof(TSender), typeof(TArgs), wrap); + } + + public static void Subscribe(object subscriber, string message, Action callback, TSender source = null) where TSender : class + { + if (subscriber == null) + throw new ArgumentNullException("subscriber"); + if (callback == null) + throw new ArgumentNullException("callback"); + + Action wrap = (sender, args) => + { + var send = (TSender)sender; + if (source == null || send == source) + callback((TSender)sender); + }; + + InnerSubscribe(subscriber, message, typeof(TSender), null, wrap); + } + + public static void Unsubscribe(object subscriber, string message) where TSender : class + { + InnerUnsubscribe(message, typeof(TSender), typeof(TArgs), subscriber); + } + + public static void Unsubscribe(object subscriber, string message) where TSender : class + { + InnerUnsubscribe(message, typeof(TSender), null, subscriber); + } + + internal static void ClearSubscribers() + { + s_callbacks.Clear(); + } + + static void InnerSend(string message, Type senderType, Type argType, object sender, object args) + { + if (message == null) + throw new ArgumentNullException("message"); + var key = new Tuple(message, senderType, argType); + if (!s_callbacks.ContainsKey(key)) + return; + List>> actions = s_callbacks[key]; + if (actions == null || !actions.Any()) + return; // should not be reachable + + // ok so this code looks a bit funky but here is the gist of the problem. It is possible that in the course + // of executing the callbacks for this message someone will subscribe/unsubscribe from the same message in + // the callback. This would invalidate the enumerator. To work around this we make a copy. However if you unsubscribe + // from a message you can fairly reasonably expect that you will therefor not receive a call. To fix this we then + // check that the item we are about to send the message to actually exists in the live list. + List>> actionsCopy = actions.ToList(); + foreach (Tuple> action in actionsCopy) + { + if (action.Item1.IsAlive && actions.Contains(action)) + action.Item2(sender, args); + } + } + + static void InnerSubscribe(object subscriber, string message, Type senderType, Type argType, Action callback) + { + if (message == null) + throw new ArgumentNullException("message"); + var key = new Tuple(message, senderType, argType); + var value = new Tuple>(new WeakReference(subscriber), callback); + if (s_callbacks.ContainsKey(key)) + { + s_callbacks[key].Add(value); + } + else + { + var list = new List>> { value }; + s_callbacks[key] = list; + } + } + + static void InnerUnsubscribe(string message, Type senderType, Type argType, object subscriber) + { + if (subscriber == null) + throw new ArgumentNullException("subscriber"); + if (message == null) + throw new ArgumentNullException("message"); + + var key = new Tuple(message, senderType, argType); + if (!s_callbacks.ContainsKey(key)) + return; + s_callbacks[key].RemoveAll(tuple => !tuple.Item1.IsAlive || tuple.Item1.Target == subscriber); + if (!s_callbacks[key].Any()) + s_callbacks.Remove(key); + } + } +} \ No newline at end of file -- cgit v1.2.3