/// <summary> /// Registers an object to become a notification observer. /// The object will have the provided method delegate invoked when a object posts the corresponding notification. /// </summary> /// <param name="observer">The object whom wants to be registered for the Manager to provide notifications to.</param> /// <param name="notification">The notification that the observer wants to be told of when they are fired.</param> /// <param name="action">The method delegate that will be invoked when the matching notification is posted.</param> /// <param name="canRunAsync">If false or null the action method will only be invoked on the Context thread or the calling thread if Context is null. If true the action method will be invoked asynchronously if needed.</param> public static void RegisterObserver(object observer, string notification, Action <object, Dictionary <string, object> > action, bool?canRunAsync = null) { // We only register valid objects. if (string.IsNullOrWhiteSpace(notification) || action == null || observer == null) { return; } // Create a new NotificationObserver object. // Currently you provide it a reference to the observer. This is not used anywhere; there are plans to use this. var registeredObserver = new NotificationObserver(observer, action, canRunAsync); // Make sure the notification has already been registered. // If not, we add the notification to the dictionary, then add the observer. if (observers.ContainsKey(notification)) { // Thread-Safe: Do not allow the _Observers property to be modified until we are done with it. lock (observers) { observers[notification].Add(registeredObserver); } } else { var observerList = new List <NotificationObserver> { registeredObserver }; lock (observers) { observers.Add(notification, observerList); } } }
/// <summary> /// Posts a notification to the manager for broadcasting to other objects. /// When a notification is posted, all objects that observe that notification will be notified via the method delegates they provided when registering. /// It is not guaranteed that the observers will have their methods invoked on the current thread. /// </summary> /// <param name="sender">The sender whom posted the notification.</param> /// <param name="notification">The notification being posted.</param> /// <param name="userData">Custom data provided by the sender that can be used by all of the observing objects.</param> public static void PostNotification(object sender, string notification, Dictionary <string, object> userData = null) { // Make sure the notification exists. if (observers.ContainsKey(notification)) { // Makes sure we never send a null userData dictionary. if (userData == null) { userData = new Dictionary <string, object>(); } try { lock (observers) { // Loop through each objects in the collection and invoke their methods. foreach (NotificationObserver observer in observers[notification].Where(obs => obs != null)) { // If Context is null, we fire on what ever thread the caller is on. // If the context is not null, yet the observer is async compatible, we fire on calling thread. if (Context == null || (observer.IsAsyncCompatible != null && observer.IsAsyncCompatible == true)) { observer.Action(sender, userData); } else { // If Context is not null and observer is not async compatible, we fire on Context thread. // Context thread should typically be the Main UI thread. NotificationObserver currentObserver = observer; Context.Send((state) => currentObserver.Action(sender, userData), null); } } } } catch (InvalidOperationException ex) { throw new InvalidOperationException("Attemped to Unregister an observer while a broadcast was taking place. You must wait until the broadcast is completed. If you are unregistering from within a method registered for observation, the unregistration must take place once control has left the registered method."); } // Clean ourself up. Task.Run(() => PurgeNullObservers()); } }