Caliburn Micro Part 4: The Event Aggregator
2015-08-24 15:36
405 查看
For those of you who are new to this tutorial series, we have been learning about how to use Caliburn Micro to build
WPF application with a robust MVVM architecture. Caliburn Micro is a framework used to help build .NET applications (WPF, Silverlight and Windows Phone 7) using several popular UI patterns including MVVM, MVP and MVC. It provides lots of neat ways to reduce
the amount of work you need to do for common tasks such as setting up bindings and hooking up events. The various features of Caliburn Micro make it easy to have a clean line between the model objects and the UI. That means your application is easy to test
and maintain. Here are links for the previous blog posts:
Part 1: Getting Started
Part 2: Data Binding and Events
Part 3: More About Events and Parameters
In this week’s tutorial we will be learning about how to use the event aggregator included with Caliburn Micro. The event aggregator is a service that makes it easy for multiple parts of your application to send messages to each other. This is useful when your
application is made up of several view-models that need to communicate. To do this, you subscribe objects (such as view models) to the event aggregator and specify what type of message they should listen for. You also define what the object does when it receives
such messages. Then, when another part of the application publishes a message, the event aggregator makes sure the appropriate subscribed objects receive it and perform the appropriate action. Throughout this tutorial we will be extending the application we
made in the getting started tutorial. (You can download the application
here.)
Although the event aggregator is more useful for larger applications that have multiple view-models, we will be keeping our tutorial application rather small. Also be warned that this tutorial has a lot more to digest compared to the previous ones! By the end
of this tutorial, we will have an application that displays two views, each with their own view-model. One of the views will be displaying some radio buttons, each representing a different color. When a radio button is clicked, we’ll publish a message that
includes the appropriate color. The second view will listen for these messages and change the color of a Rectangle. We will do this in 4 steps: Adding another view to the application, implementing the IHandle<TMessage> interface, subscribe one of the view
models to an event aggregator and finally publish events from the other view model.
Step 1: Adding Another View and View-Model
In order to demonstrate the event aggregator, we will need at least 2 view models in our application. We already have one (AppViewModel), so lets start by adding another one. Remember the naming convention described in the getting started tutorial? Add a new
class called ColorViewModel, and a UserControl called ColorView. Lets also change the background of the ColorView so that we at least have something to see when we first add it to the application. In terms of the visual structure, we are going to get the existing
AppView to contain the new ColorView. (Views do not need to be nested in order to use the event aggregator; a view-model can listen to messages being published from anywhere in the application.) To do this, AppViewModel will need a property of type ColorViewModel
which we will set in the constructor like this:
In AppView.xaml, we will split the grid into 2 columns and display the ColorModelView in the first column. AppView.xaml will now look like this:
See what’s going to happen here? We have set the name of the ContentControl to be the same name as the property we just added to AppViewModel. From this, Caliburn Micro will kindly bind the Content property of the ContentControl to the ColorModel property for
us. When we run this up later, Caliburn Micro will make sure that an instance of ColorView is displayed for the ColorViewModel.
If we were to run up the application now, we’d run into an exception saying that the default constructor of AppViewModel can’t be found. Hmm, that’s a good point: we have included a constructor on AppViewModel that requires a parameter – a ColorViewModel object.
To resolve this, we’ll need to update our AppBootstrapper as seen below. (Make sure to include a reference to System.ComponentModel.Composition.dll to the application.)
This is similar to the bootstrappers used in the samples that come with the Caliburn Micro download. So as not to make this blog post too long, I won’t be diving into the
11b0f
details of what this code is doing (search for MEF or Managed Extensibility Framework
if you need more details).
Next, we need to include an Export attribute on both our view-model classes. This is for the GetInstance method in the AppBootstrapper to work.
And finally, include the ImportingConstructor attribute on the AppViewModel constructor. This is to indicate that this constructor should be used since there is no default constructor. When Caliburn Micro creates the AppViewModel, it will also create an instance
of the ColorViewModel to pass into the constructor for us.
Now we can run the application and see that the ColorView is successfully being displayed within the AppView:
Let’s add a Rectangle into the second column. This will be the Rectangle that changes color when messages are handled, so its color will be controlled by a property on the AppViewModel like this:
Step 2: Implementing the IHandle<TMessage> Interface
We are going to be publishing messages from the ColorViewModel to be picked up by the AppViewModel. To do this we are going to need to implement a class that holds the message information. such a class is usually very small and simple. It mainly needs to have
some properties that hold any information that we want to send. Here is the message class that we are going to use:
In order for the AppViewModel to handle the appropriate events, it will need to implement the IHandle<TMessage> interface. In our case, we will be using ColorEvent as the generic type. The IHandle interface has a single method that we need to implement which
is called Handle. In the Handle method of our AppViewModel, we will look at the SolidColorBrush sent in the ColorEvent and use it to set the Color property. This will in turn change the color of the Rectangle in the view.
Step 3: Subscribe
Now we need to subscribe the AppViewModel to an event aggregator so that it can actually listen to published messages. We do this by adding another parameter to the constructor which will be an IEventAggregator. When the time comes to create the AppViewModel,
Caliburn Micro will pass in the event aggregator that we set up in the bootstrapper. Now within the constructor, we simply call the Subscribe method like this:
Step 4: Publish
The ColorViewModel is also going to need the event aggregator so that it can publish messages. Add a constructor to the ColorViewModel which takes an IEventAggregator and stores it in a field. Remember to include the ImportingConstructor attribute:
Now we just need add the radio buttons to the ColorView, listen to their click event and then publish a message. You may remember from the second tutorial in this series about the quick way to listen to the click event. Simply set the name of the RadioButton
to be the same as the method we want to be called. We could of course use event parameters rather than having an action method for every RadioButton, but I have done it like this so you can clearly see what’s going on:
And that’s it. Run up the application now and click the radio buttons. See that the AppViewModel is successfully getting the messgaes from the ColorViewModel to change the color of the Rectangle.
One thing to note is that I have been passing around SolidColorBrushes in the messages to set the color of the Rectangle. In general you would pass around more primitive values and then use a converter in the UI to interpret the value as a SolidColorBrush or
the like.
The full Visual Studio 2010 project for this tutorial can
be downloaded from here. I hope you find the event aggregator useful for orchestrating communication across the various view-models in your application. Check
out the documention for more information including how to do polymorphic event subscriptions, custom publication marshaling and unsubscribe from the event aggregator.
See you next time.
WPF application with a robust MVVM architecture. Caliburn Micro is a framework used to help build .NET applications (WPF, Silverlight and Windows Phone 7) using several popular UI patterns including MVVM, MVP and MVC. It provides lots of neat ways to reduce
the amount of work you need to do for common tasks such as setting up bindings and hooking up events. The various features of Caliburn Micro make it easy to have a clean line between the model objects and the UI. That means your application is easy to test
and maintain. Here are links for the previous blog posts:
Part 1: Getting Started
Part 2: Data Binding and Events
Part 3: More About Events and Parameters
In this week’s tutorial we will be learning about how to use the event aggregator included with Caliburn Micro. The event aggregator is a service that makes it easy for multiple parts of your application to send messages to each other. This is useful when your
application is made up of several view-models that need to communicate. To do this, you subscribe objects (such as view models) to the event aggregator and specify what type of message they should listen for. You also define what the object does when it receives
such messages. Then, when another part of the application publishes a message, the event aggregator makes sure the appropriate subscribed objects receive it and perform the appropriate action. Throughout this tutorial we will be extending the application we
made in the getting started tutorial. (You can download the application
here.)
Although the event aggregator is more useful for larger applications that have multiple view-models, we will be keeping our tutorial application rather small. Also be warned that this tutorial has a lot more to digest compared to the previous ones! By the end
of this tutorial, we will have an application that displays two views, each with their own view-model. One of the views will be displaying some radio buttons, each representing a different color. When a radio button is clicked, we’ll publish a message that
includes the appropriate color. The second view will listen for these messages and change the color of a Rectangle. We will do this in 4 steps: Adding another view to the application, implementing the IHandle<TMessage> interface, subscribe one of the view
models to an event aggregator and finally publish events from the other view model.
Step 1: Adding Another View and View-Model
In order to demonstrate the event aggregator, we will need at least 2 view models in our application. We already have one (AppViewModel), so lets start by adding another one. Remember the naming convention described in the getting started tutorial? Add a new
class called ColorViewModel, and a UserControl called ColorView. Lets also change the background of the ColorView so that we at least have something to see when we first add it to the application. In terms of the visual structure, we are going to get the existing
AppView to contain the new ColorView. (Views do not need to be nested in order to use the event aggregator; a view-model can listen to messages being published from anywhere in the application.) To do this, AppViewModel will need a property of type ColorViewModel
which we will set in the constructor like this:
public class AppViewModel : PropertyChangedBase { public AppViewModel(ColorViewModel colorModel) { ColorModel = colorModel; } public ColorViewModel ColorModel { get; private set; } } |
<Grid Width="300" Height="300" Background="LightBlue"> <Grid.ColumnDefinitions> <ColumnDefinition Width="*" /> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> <ContentControl Name="ColorModel" Margin="10" /> </Grid> |
us. When we run this up later, Caliburn Micro will make sure that an instance of ColorView is displayed for the ColorViewModel.
If we were to run up the application now, we’d run into an exception saying that the default constructor of AppViewModel can’t be found. Hmm, that’s a good point: we have included a constructor on AppViewModel that requires a parameter – a ColorViewModel object.
To resolve this, we’ll need to update our AppBootstrapper as seen below. (Make sure to include a reference to System.ComponentModel.Composition.dll to the application.)
using Caliburn.Micro; using System.ComponentModel.Composition; using System.ComponentModel.Composition.Hosting; using System.ComponentModel.Composition.Primitives; public class AppBootstrapper : Bootstrapper<AppViewModel> { private CompositionContainer container; protected override void Configure() { container = new CompositionContainer(new AggregateCatalog(AssemblySource.Instance.Select(x => new AssemblyCatalog(x)).OfType<ComposablePartCatalog>())); CompositionBatch batch = new CompositionBatch(); batch.AddExportedValue<IWindowManager>(new WindowManager()); batch.AddExportedValue<IEventAggregator>(new EventAggregator()); batch.AddExportedValue(container); container.Compose(batch); } protected override object GetInstance(Type serviceType, string key) { string contract = string.IsNullOrEmpty(key) ? AttributedModelServices.GetContractName(serviceType) : key; var exports = container.GetExportedValues<object>(contract); if (exports.Count() > 0) { return exports.First(); } throw new Exception(string.Format("Could not locate any instances of contract {0}.", contract)); } } |
11b0f
details of what this code is doing (search for MEF or Managed Extensibility Framework
if you need more details).
Next, we need to include an Export attribute on both our view-model classes. This is for the GetInstance method in the AppBootstrapper to work.
[Export(typeof(AppViewModel))] public class AppViewModel : PropertyChangedBase { ... } [Export(typeof(ColorViewModel))] public class ColorViewModel { } |
of the ColorViewModel to pass into the constructor for us.
[ImportingConstructor] public AppViewModel(ColorViewModel colorModel) { ColorModel = colorModel; } |
Let’s add a Rectangle into the second column. This will be the Rectangle that changes color when messages are handled, so its color will be controlled by a property on the AppViewModel like this:
private SolidColorBrush _Color; public SolidColorBrush Color { get { return _Color; } set { _Color = value; NotifyOfPropertyChange(() => Color); } } |
<Rectangle Grid.Column="1" Width="100" Height="100" Fill="{Binding Color}" /> |
We are going to be publishing messages from the ColorViewModel to be picked up by the AppViewModel. To do this we are going to need to implement a class that holds the message information. such a class is usually very small and simple. It mainly needs to have
some properties that hold any information that we want to send. Here is the message class that we are going to use:
public class ColorEvent { public ColorEvent(SolidColorBrush color) { Color = color; } public SolidColorBrush Color { get; private set; } } |
is called Handle. In the Handle method of our AppViewModel, we will look at the SolidColorBrush sent in the ColorEvent and use it to set the Color property. This will in turn change the color of the Rectangle in the view.
public void Handle(ColorEvent message) { Color = message.Color; } |
Now we need to subscribe the AppViewModel to an event aggregator so that it can actually listen to published messages. We do this by adding another parameter to the constructor which will be an IEventAggregator. When the time comes to create the AppViewModel,
Caliburn Micro will pass in the event aggregator that we set up in the bootstrapper. Now within the constructor, we simply call the Subscribe method like this:
[ImportingConstructor] public AppViewModel(ColorViewModel colorModel, IEventAggregator events) { ColorModel = colorModel; events.Subscribe(this); } |
The ColorViewModel is also going to need the event aggregator so that it can publish messages. Add a constructor to the ColorViewModel which takes an IEventAggregator and stores it in a field. Remember to include the ImportingConstructor attribute:
private readonly IEventAggregator _events; [ImportingConstructor] public ColorViewModel(IEventAggregator events) { _events = events; } |
to be the same as the method we want to be called. We could of course use event parameters rather than having an action method for every RadioButton, but I have done it like this so you can clearly see what’s going on:
<RadioButton Name="Red" Content="Red" Foreground="White" VerticalAlignment="Center" HorizontalAlignment="Center" /> <RadioButton Name="Green" Content="Green" Foreground="White" VerticalAlignment="Center" HorizontalAlignment="Center" Grid.Row="1" /> <RadioButton Name="Blue" Content="Blue" Foreground="White" VerticalAlignment="Center" HorizontalAlignment="Center" Grid.Row="2" /> |
public void Red() { _events.Publish(new ColorEvent(new SolidColorBrush(Colors.Red))); } public void Green() { _events.Publish(new ColorEvent(new SolidColorBrush(Colors.Green))); } public void Blue() { _events.Publish(new ColorEvent(new SolidColorBrush(Colors.Blue))); } |
One thing to note is that I have been passing around SolidColorBrushes in the messages to set the color of the Rectangle. In general you would pass around more primitive values and then use a converter in the UI to interpret the value as a SolidColorBrush or
the like.
The full Visual Studio 2010 project for this tutorial can
be downloaded from here. I hope you find the event aggregator useful for orchestrating communication across the various view-models in your application. Check
out the documention for more information including how to do polymorphic event subscriptions, custom publication marshaling and unsubscribe from the event aggregator.
See you next time.
相关文章推荐
- js页面宽度获取
- 手机摇一摇 DeviceOrientationEvent
- user_tab_columns和user_col_comments区别
- IES Light Profiles
- JAVA MQ编码部分
- 判断一棵树是否是另一棵树的子树
- Android OkHttp完全解析 是时候来了解OkHttp了
- ssh框架概述
- Android OkHttp完全解析 是时候来了解OkHttp了
- Maven项目使用步骤
- 两列布局——左侧宽度固定,右侧宽度自适应的两种方法
- VS2010下error C2664: “atoi”: 不能将参数 1 从“CString”转换为“const char *”
- HTML5的链接
- Caliburn Micro Part 2: Data Binding and Events
- 手机录音和降噪那些事
- Understanding IES Lights
- 关于Android最佳性能实践——高性能编码优化学习笔记
- apktool反编译详细使用教程
- a、b交换的几种方法(不采用中间变量)
- Universal Image Loader