The EventAggregator - canton7/Stylet GitHub Wiki
The EventAggregator is a decentralised, weakly-binding, publish/subscribe-based event manager.
Subscribers interesting in a particular event can tell the IEventAggregator of their interest, and will be notified whenever a publisher publishes that particular event to the IEventAggregator.
Events are classes - do whatever you want with them. For example:
C# | VB.NET |
class MyEvent {
// Do something
} |
Class MyEvent
' Do Something
End Class |
Subscribers must implement IHandle<T>
, where T
is the event type they are interested in receiving (they can of course implement multiple IHandle<T>
's for multiple T
's). They must then get hold of an instance of the IEventAggregator, and subscribe themselves, for example:
C# | VB.NET |
class Subscriber : IHandle<MyEvent>, IHandle<MyOtherEvent>
{
public Subscriber(IEventAggregator eventAggregator)
{
eventAggregator.Subscribe(this);
}
public void Handle(MyEvent message)
{
// ...
}
public void Handle(MyOtherEvent message)
{
// ...
}
} |
Class Subscriber : Implements IHandle(Of MyEvent)
Public Sub New(ByRef eventAggregator as IEventAggregator)
eventAggregator.Subscribe(Me)
End Sub
Public Sub Handle(message as MyEvent) Implements IHandle(Of MyEvent).Handle
' ...
End Sub
Public Sub Handle(message as MyOtherEvent) Implements IHandle(Of MyOtherEvent).Handle
' ...
End Sub
End Class |
For VB.NET users, the Sub New()
passing the eventAggregator by reference will probably fail across namespaces, and can be irritating to have to define with each new subscriber. Thus, it may be easier to define your eventAggregator in a global module, then subscribe directly to it instead of passing its reference along to each new ViewModel you call.
Module Global
Public eventAggregator as IEventAggregator
End Module
Class Subscriber : Implements IHandle(Of MyEvent)
Public Sub New()
Global.eventAggregator.Subscribe(Me)
End Sub
'Public Sub Handle...
End Class
Make sure to keep the namespace for the module blank, so that it can be used throughout the program.
Publishers must also get an instance of the IEventAggregator, but they don't need to subscribe themselves - they only need to call IEventAggregator.Publish every time they want to publish an event, for example:
C# | VB.NET |
class Publisher
{
private IEventAggregator eventAggregator;
public Publisher(IEventAggregator eventAggregator)
{
this.eventAggregator = eventAggregator;
}
public void PublishEvent()
{
this.eventAggregator.Publish(new MyEvent());
}
} |
Class Publisher
Dim eventAggregator as IEventAggregator
Public Sub New(ByRef eventAggregator as IEventAggregator)
Me.eventAggregator = eventAggregator
End Sub
Public Sub PublishEvent()
Me.eventAggregator.Publish(New MyEvent())
End Sub
End Class |
Again, for VB.NET users, if you've set up the global module then you don't need to pass the eventAggregator to the Publisher. You can just publish directly to the global eventAggregator;
Class Publisher
Public Sub PublishEvent()
Global.eventAggregator.Publish(New MyEvent())
End Sub
End Class
Because the IEventAggregator is weakly binding, subscribers don't need to unsubscribe themselves - the IEventAggregator won't retain them. It is however possible for a subscriber to unsubscribe itself if it wants - call
C# | VB.NET |
IEventAggregator.Unsubscribe(this); |
IEventAggregator.UnSubscribe(Me) |
The default IEventAggregator.Publish
method publishes the event synchronously. You can also call PublishOnUIThread
to dispatch asynchronously to the UI thread, or PublishWithDispatcher
and pass any action you want to act as the dispatcher (this can be useful if writing your own methods on IEventAggregator).
Subscribers can listen to particular channels, and publishers can publish events to particular channels. If an event is published to a particular channel, only subscribers who have subscribed to that channel will receive that event. This can be useful if the same message type is used in several different contexts.
Channels are strings, and so allow loose coupling between subscribers to a channel, and publishers to that channel.
By default, Subscribe()
will subscribe the subscriber to a single channel, EventAggregator.DefaultChannel
. Similarly, Publish()
(and all variants thereof) will publish events to that same default channel. However, you can specify your own channel(s) if you want....
To subscribe to a particular channel, pass it as a parameter to Subscribe
: eventAggregator.Subscribe(this, "ChannelA")
. You can also subscribe to multiple channels: eventAggregator.Subscribe(this, "ChannelA", "ChannelB")
.
In both of these cases, you will not be subscribed to EventAggregator.DefaultChannel
- only to the channels listed. You will only receive events that were pushed to "ChannelA" or "ChannelB".
To publish to a particular channel, pass it as a parameter to Publish
: eventAggregator.Publish(message, "ChannelA")
, or eventAggregator.PublishOnUIThread(message, "ChannelA", "ChannelB")
, etc. As with subscribing above, the event will be published to all of the channels named, and not to the default channel.
To unsubscribe from a channel, pass it to Unsubscribe
: eventAggregator.Unsubscribe(this, "ChannelA")
. You will remain subscribed to any other channels you were previously subscribed to, and have not unsubscribed from.
Calling eventAggregator.Unsubscribe(this)
will unsubscribe you from all channels.
If you're using StyletIoC with the default Bootstrapper<TRootViewModel>
, you don't need to worry about this - the EventAggregator is set up correctly by default.
If you're using another IoC container, however, you need to make sure that the EventAggregator is registered as a singleton service to the interface IEventAggregator
- there must be only one instance of the EventAggregator created, ever, and this once instance must be returned every time it is requested.