summaryrefslogtreecommitdiff
path: root/Xamarin.Forms.Core/MessagingCenter.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Xamarin.Forms.Core/MessagingCenter.cs')
-rw-r--r--Xamarin.Forms.Core/MessagingCenter.cs131
1 files changed, 131 insertions, 0 deletions
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<Tuple<string, Type, Type>, List<Tuple<WeakReference, Action<object, object>>>> s_callbacks =
+ new Dictionary<Tuple<string, Type, Type>, List<Tuple<WeakReference, Action<object, object>>>>();
+
+ public static void Send<TSender, TArgs>(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>(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<TSender, TArgs>(object subscriber, string message, Action<TSender, TArgs> callback, TSender source = null) where TSender : class
+ {
+ if (subscriber == null)
+ throw new ArgumentNullException("subscriber");
+ if (callback == null)
+ throw new ArgumentNullException("callback");
+
+ Action<object, object> 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<TSender>(object subscriber, string message, Action<TSender> callback, TSender source = null) where TSender : class
+ {
+ if (subscriber == null)
+ throw new ArgumentNullException("subscriber");
+ if (callback == null)
+ throw new ArgumentNullException("callback");
+
+ Action<object, object> 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<TSender, TArgs>(object subscriber, string message) where TSender : class
+ {
+ InnerUnsubscribe(message, typeof(TSender), typeof(TArgs), subscriber);
+ }
+
+ public static void Unsubscribe<TSender>(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<string, Type, Type>(message, senderType, argType);
+ if (!s_callbacks.ContainsKey(key))
+ return;
+ List<Tuple<WeakReference, Action<object, object>>> 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<Tuple<WeakReference, Action<object, object>>> actionsCopy = actions.ToList();
+ foreach (Tuple<WeakReference, Action<object, object>> 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<object, object> callback)
+ {
+ if (message == null)
+ throw new ArgumentNullException("message");
+ var key = new Tuple<string, Type, Type>(message, senderType, argType);
+ var value = new Tuple<WeakReference, Action<object, object>>(new WeakReference(subscriber), callback);
+ if (s_callbacks.ContainsKey(key))
+ {
+ s_callbacks[key].Add(value);
+ }
+ else
+ {
+ var list = new List<Tuple<WeakReference, Action<object, object>>> { 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<string, Type, Type>(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