Coproject - a RIA Caliburn.Micro demo, part 11
2015-09-03 06:41
501 查看
In this part, we will synchronize list after saving data and add some icons. You may continue on your work from older parts, or download updated code from Coproject site.
Usually, if you want a component of your application to notify other components about an event that occurred, you use standard .NET events. The problem with events is that you always need to access the publisher of an event from the subscriber. What’s more,
you also need to unsubscribe from the event. That might be tricky in a complicated application or when the same type of event may come from more sources (for example if you would implement user events logging via events and logger subscribed to these events).
Here comes (again) Caliburn.Micro and its implementation of Event Aggregator. Consider EventAggregator as a component that you subscribe to all kinds of events or that you use to publish an event.
In Coproject, we will use EventAggregator to notify ToDoItemsViewModel that ToDoItem has been changed and thus it should reload data.
First of all, we must create an object that will represent the event. In Coproject client project, create new folder Events and put a new class called ToDoItemUpdatedEvent into it:
Open ToDoItemViewModel and let MEF import an EventAggregator instance:
Then update Save as follows:
So, after (remember coroutines and yield return) a ToDoItem is saved, we publish event notifying about it. We do not care who subscribes to this event, this is not our business here.
Now, let’s write the other end – an event subscriber. Open ToDoListsViewModel and edit the constructor:
Now, event aggregator will know that it should notify ToDoListsViewModel about events. But what events? It depends on what IHandle<T> interfaces the subscriber implements. Let the view model implement IHandle<ToDoItemUpdatedEvent> and add this
implementation:
Remember that LoadData returns iEnumerable<IResult> so we have to enumerate it to ‘run’ it. Therefore, we wrap it into a SequentialResult and then execute this single result.
The only problem is that LoadData requires a parameter – filter. And this value is unknown to the handler. Fortunately, we can change the source Filter textbox binding from function parameter to property. Add this property to ToDoListsViewMode:
And the remove parameter ‘filter’ from LoadData (take the filter value from Filter).
Build the application – it should run as before, but after saving of a ToDoItem, the list should refresh automatically.
Let’s give Coproject a little more style – change text buttons to icon buttons.
Open the zip file attached to this post and extract the five images to Assets/Icons. These icons are from Axialis.
Then, open ToDoListsView and edit the LoadData button:
Well, this works nicely but let’s make it a little more shorter since we will probably use these image buttons quite frequently in our application. To do this, we will create a custom control called ImageButton. Create a new folder Controls and add this control
to it:
As you can see, we inherited original Button and added a property to it. The reason that ImageName is implemented as a dependency property is to enable it for binding, styling, etc. When the property is changed a new Image is inserted to the button. It would
be great to add this image into ContentTemplate by style and just bind its Source property to ImageName, but since in template binding, you cannot use any converters, we would have to set ImageName in the raw form (/Coproject;component/Assets/Icons/Search.png)
and that is not what we want. So that is why I’ve chosen this way. Now open Assets/Cosmopolitan/Custom.xaml and add this namespace to it:
And then add this to the end of the file, just above </ResourceDictionary>:
We are done – get back to ToDoListsView, register namespace for ImageButton as in Custom.xaml and update LoadData button:
Update buttons in Toolbar view in the same manner:
That is all. I hope you like the new look!
Icons.zip
Event aggregator
Usually, if you want a component of your application to notify other components about an event that occurred, you use standard .NET events. The problem with events is that you always need to access the publisher of an event from the subscriber. What’s more,you also need to unsubscribe from the event. That might be tricky in a complicated application or when the same type of event may come from more sources (for example if you would implement user events logging via events and logger subscribed to these events).
Here comes (again) Caliburn.Micro and its implementation of Event Aggregator. Consider EventAggregator as a component that you subscribe to all kinds of events or that you use to publish an event.
In Coproject, we will use EventAggregator to notify ToDoItemsViewModel that ToDoItem has been changed and thus it should reload data.
First of all, we must create an object that will represent the event. In Coproject client project, create new folder Events and put a new class called ToDoItemUpdatedEvent into it:
public class ToDoItemUpdatedEvent { public int ToDoItemID { get; set; } public ToDoItemUpdatedEvent(int toDoItemID) { ToDoItemID = toDoItemID; } }
Open ToDoItemViewModel and let MEF import an EventAggregator instance:
[Import] public IEventAggregator EventAggregator { get; set; }
Then update Save as follows:
public IEnumerable<IResult> Save() { (Item as IEditableObject).EndEdit(); IsReadOnly = true; #region Fix DataForm bug IsReadOnly = false; IsReadOnly = true; #endregion yield return new SaveDataResult(_context); EventAggregator.Publish(new ToDoItemUpdatedEvent(Item.ToDoItemID)); }
So, after (remember coroutines and yield return) a ToDoItem is saved, we publish event notifying about it. We do not care who subscribes to this event, this is not our business here.
Now, let’s write the other end – an event subscriber. Open ToDoListsViewModel and edit the constructor:
[ImportingConstructor] public ToDoListsViewModel(IEventAggregator eventAggregator) { DisplayName = "To Do"; Description = "To-do lists"; eventAggregator.Subscribe(this); }
Now, event aggregator will know that it should notify ToDoListsViewModel about events. But what events? It depends on what IHandle<T> interfaces the subscriber implements. Let the view model implement IHandle<ToDoItemUpdatedEvent> and add this
implementation:
public void Handle(ToDoItemUpdatedEvent message) { LoadData().ToSequential().Execute(null); }
Remember that LoadData returns iEnumerable<IResult> so we have to enumerate it to ‘run’ it. Therefore, we wrap it into a SequentialResult and then execute this single result.
The only problem is that LoadData requires a parameter – filter. And this value is unknown to the handler. Fortunately, we can change the source Filter textbox binding from function parameter to property. Add this property to ToDoListsViewMode:
public string Filter { get; set; }
And the remove parameter ‘filter’ from LoadData (take the filter value from Filter).
Build the application – it should run as before, but after saving of a ToDoItem, the list should refresh automatically.
Icons
Let’s give Coproject a little more style – change text buttons to icon buttons.Open the zip file attached to this post and extract the five images to Assets/Icons. These icons are from Axialis.
Then, open ToDoListsView and edit the LoadData button:
<Button x:Name="LoadData" Grid.Column="1" ToolTipService.ToolTip="Search"> <Button.Content> <Image Source="/Coproject;component/Assets/Icons/Search.png" Height="20" /> </Button.Content> </Button>
Well, this works nicely but let’s make it a little more shorter since we will probably use these image buttons quite frequently in our application. To do this, we will create a custom control called ImageButton. Create a new folder Controls and add this control
to it:
public class ImageButton : Button { public string ImageName { get { return (string)GetValue(ImageNameProperty); } set { SetValue(ImageNameProperty, value); } } public static readonly DependencyProperty ImageNameProperty = DependencyProperty.Register("ImageName", typeof(string), typeof(ImageButton), new PropertyMetadata(OnImageNamePropertyChanged)); public static void OnImageNamePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { ImageButton button = d as ImageButton; string newValue = e.NewValue as string; if (newValue.IsNullOrWhiteSpace()) { button.Content = null; return; } newValue = "/Coproject;component/Assets/Icons/{0}.png".FormatWith(newValue); ImageSource image = new BitmapImage(new Uri(newValue, UriKind.Relative)); button.Content = new Image { Source = image }; } }
As you can see, we inherited original Button and added a property to it. The reason that ImageName is implemented as a dependency property is to enable it for binding, styling, etc. When the property is changed a new Image is inserted to the button. It would
be great to add this image into ContentTemplate by style and just bind its Source property to ImageName, but since in template binding, you cannot use any converters, we would have to set ImageName in the raw form (/Coproject;component/Assets/Icons/Search.png)
and that is not what we want. So that is why I’ve chosen this way. Now open Assets/Cosmopolitan/Custom.xaml and add this namespace to it:
xmlns:local="clr-namespace:Coproject.Controls"
And then add this to the end of the file, just above </ResourceDictionary>:
<Style TargetType="local:ImageButton" BasedOn="{StaticResource DefaultButtonStyle}"> <Setter Property="Height" Value="32" /> </Style>
We are done – get back to ToDoListsView, register namespace for ImageButton as in Custom.xaml and update LoadData button:
<local:ImageButton x:Name="LoadData" Grid.Column="1" ImageName="Search" ToolTipService.ToolTip="Search" />
Update buttons in Toolbar view in the same manner:
<local:ImageButton x:Name="Edit" ImageName="Pen" ToolTipService.ToolTip="Edit" Margin="0,0,5,0" /> <local:ImageButton x:Name="Cancel" ImageName="Undo" ToolTipService.ToolTip="Cancel" Margin="0,0,5,0" /> <local:ImageButton x:Name="Save" ImageName="Save" ToolTipService.ToolTip="Save" Margin="0,0,5,0" /> <local:ImageButton x:Name="TryClose" ImageName="Close" ToolTipService.ToolTip="Close" />
That is all. I hope you like the new look!
Icons.zip
相关文章推荐
- 我是如何利用1条百度知道给网站日引流破万的?
- Coproject - a RIA Caliburn.Micro demo, part 10.5
- Coproject - a RIA Caliburn.Micro demo, part 12
- Coproject - a RIA Caliburn.Micro demo, part 14
- Coproject - a RIA Caliburn.Micro demo, part 13
- Nginx 配置常见误区
- Linux学习之grep篇
- Hadoop学习计划
- Clustered Shading架构实现步骤
- wcf利用IDispatchMessageInspector实现接口监控日志记录和并发限流
- Nginx的作用
- centos7关闭防火墙
- hadoop学习(一)-- centos 64位编译安装hadoop
- nginx完美支持thinkphp3.2.2
- linux 常用命令记录
- Ubuntu下 5步安装nginx记录
- nginx启动、停止、重启、配置文件校验
- 在linux中添加ftp用户,并设置相应的权限
- Linux下解压rar格式的压缩文件
- tomcat异常之org.apache.catalina.LifecycleException: