Skip to content
GeestWagen edited this page Jan 29, 2020 · 6 revisions

TinyMessenger

TinyMessenger is an independent part of the TinyIoC project and provides an event aggregator/messenger for loosely coupled communication. Some scenarios where this may prove useful :

  • Communication between Controllers/ViewModels.
  • Service classes raising "events".
  • Removing responsibilities from classes that receive external events (such as the AppDelegate in an iPhone application).
  • Decoupling communication between classes.

TinyMessenger uses a Publish/Subscribe model and is orderless, so you can subscribe to a message even if there's nobody publishing, and you can publish one even if nobody is subscribed. It is also loosely coupled in that the publisher and subscriber know nothing of each other, all they both care about is the message itself.

Key Feature Examples

"Hub" Creation

// Simple hub creation
var messageHub = new TinyMessengerHub();

// Or we can use TinyIoC to resolve it
// Uncommenting line 18 (#define TINYMESSENGER) in TinyIoC.cs will make
// TinyIoC automatically register TinyMessenger in your root container
// (but not child containers)
var resolvedHub = container.Resolve<ITinyMessengerHub>();

Publishing

// Publishing a message delivers it to anyone registered to receive it
// and who hasn't "filtered" the message (see Subscribing)
//
// Publishing a message is as simple as calling the "Publish" method.
messageHub.Publish(new MyMessage());

// We can also publish asyncronously if necessary
messageHub.PublishAsync(new MyMessage());

// And we can get a callback when publishing is completed
messageHub.PublishAsync(new MyMessage(), MyCallback); // MyCallback is executed on completion

Subscribing

// Basic subscription, we just provide the message type 
// we are interested in and an Action<TMessageType> to call on receipt.
//
// We can subscribe regardless of how many publishers there are
// (even if there are none)
messageHub.Subscribe<MyMessage>((m) => { MessageBox.Show("Message Received!"); });

// If we are only interested in a message if it has certain
// properties we can provide a filter (Func<TMessageType, bool)
// which dictates whether we recieve the message or not.
//
// In this example we will only recieve the message if 
// the content container "Testing"
messageHub.Subscribe<MyMessageAgain>((m) => { MessageBox.Show("Message Received!"); }, (m) => m.Content == "Testing");

// By default the message hub keeps string references to everything
// If you need it to keep a weak reference to everything apart from the proxy
// (see Proxies for more information). you can specify that too:
messageHub.Subscribe<MyMessage>((m) => { MessageBox.Show("Message Received"); }, true);

// We can also specify something called a proxy.
// A singleton "pass-thru" proxy is provided for defaults, but
// proxies can be used to intercept messages, marshall onto
// threads, logging etc.
//
// This simple example uses the default proxy, for more information
// about proxies see the Proxy section.
messageHub.Subscribe<MyMessage>((m) => { MessageBox.Show("Received via proxy"); }, DefaultTinyMessageProxy.Instance);

// Of course we can combine these options in any way we want..
// ..this example uses a filter, strong references and supplies
// a proxy.
messageHub.Subscribe<MyMessageAgain>((m) => { MessageBox.Show("Message Received!"); }, (m) => m.Content == "Testing", true, DefaultTinyMessageProxy.Instance);

Unsubscribing

// You can use the token that the Subscribe message returns to later
// unsubscribe, either explicitly..
var token = messageHub.Subscribe<MyMessage>((m) => { MessageBox.Show("Message Received!"); });
messenger.Unsubscribe<TestMessage>(token );

// ..or implicitly by disposing the token. Disposing it will automatically
// unsubscribe for you, so there's no need to keep a reference
// to the message hub:
var token = messageHub.Subscribe<MyMessage>((m) => { MessageBox.Show("Message Received!"); });
token.Dispose();  //Automatically unsubscribed

Messages

// The basic requirement for a message is that it implements
// ITinyMessage
public class MyMessage : ITinyMessage
{
    /// <summary>
    /// The sender of the message, or null if not supported by the message implementation.
    /// </summary>
    public object Sender { get; private set; }
}

// But we also provide a TinyMessageBase to handle
// holding a weak reference to the sender
public class MyOtherMessage : TinyMessageBase
{
    /// <summary>
    /// Initializes a new instance of the MyOtherMessage class.
    /// </summary>
    /// <param name="sender">Message sender (usually "this")</param>
    public MyOtherMessage(object sender)
        : base(sender)
    {
        
    }
}

// And if your message needs some "content" we provide a simple
// generic message (GenericTinyMessage<TContent>) out of the box.
//
// We can use that message directly or derive from it to gain a 
// strongly typed "content" property
public class MyMessageAgain : GenericTinyMessage<String>
{
    /// <summary>
    /// Create a new instance of the MyMessageAgain class.
    /// </summary>
    /// <param name="sender">Message sender (usually "this")</param>
    /// <param name="content">Contents of the message</param>
    public MyMessageAgain(object sender, String content)
        : base(sender, content)
    {
        // We now have a public string property called Content
    }
}

// Messages can also contain "callbacks" - one such example
// is provided out of the box as CancellableGenericTinyMessage<TContent>
// where a "cancel" action is included as part of the message.
// This could be useful for "application close" events where another part
// of the application can cancel the closedown, perhaps due to files
// not being saved.
//
// If any subscribers call "Cancel" on our
// cancellable message then isCancelled will then
// equal true.
bool isCancelled = false;
messageHub.Publish(new CancellableGenericTinyMessage<string>(this, "Testing", () => { isCancelled = true; }));

Proxies

// A proxy is created by implementing ITinyMessageProxy
// with it's single method:
//
// void Deliver(ITinyMessage message, ITinyMessageSubscription subscription)
//
// The proxy is called each time the message is to be delivered
// to a subscriber, and the proxy to use is dictated by the subscription,
// not the publication - although passing an ITinyMessageProxy in as a dependency
// via TinyIoC could enable you to switch on/off "message logging" throughout
// the system quite easily.
//
// A proxy could be used to intercept and log messages, marshall them
// to a UI thread, or even chain to another proxy.

// This example proxy (for WPF/Silverlight) uses the despatcher to 
// marshall messages back to the UI thread:
public class DispatcherTinyMessageProxy : ITinyMessageProxy
{
    private Dispatcher _Dispatcher;

    public DispatcherTinyMessageProxy(Dispatcher dispatcher)
    {
        _Dispatcher = dispatcher;
    }

    public void Deliver(ITinyMessage message, ITinyMessageSubscription subscription)
    {
        _Dispatcher.Invoke((Action)delegate { subscription.Deliver(message); });
    }
}

// This proxy does a similar job, but for WinForms. It holds a weak
// reference to a control (probably the form) and uses it to marshall
// messages to the UI thread:
public class ControlInvokeTinyMessageProxy : ITinyMessageProxy
{
    private WeakReference _Control;

    /// <summary>
    /// Initializes a new instance of the ControlInvokeTinyMessageProxy class.
    /// </summary>
    /// <param name="control"></param>
    public ControlInvokeTinyMessageProxy(Control control)
    {
        _Control = new WeakReference(control);
    }

    public void Deliver(ITinyMessage message, ITinyMessageSubscription subscription)
    {
        var control = _Control.Target as Control;

        if (control != null)
            control.Invoke((Action)delegate { subscription.Deliver(message); });
    }
}
Clone this wiki locally