就我个人的理解,Caliburn.Micro的Action其实为我们实现了事件的响应以及事件参数的传递。废话不多说,直接看代码:
1.新建一个工程,取名为:CaliburnMicroAction。
2.新建一个ViewModel,取名为MainViewModel,其代码如下:
- class MainViewModel:PropertyChangedBase,IShell {
-
- private string mytxt;
- public string Mytxt {
- get {
- return mytxt;
- }
- set {
- mytxt = value;
- NotifyOfPropertyChange(() => Mytxt);
- NotifyOfPropertyChange(() => CanMyClick);
- }
- }
-
- public bool CanMyClick {
- get {
- return !string.IsNullOrEmpty(mytxt);
- }
- }
-
- public void MyClick(object ob, string str2) {
- MessageBox.Show(ob.ToString() +" " + str2);
- }
- }
说明:IShell接口需要定义,此处的IShell接口的实现时候为了配合MEF的使用
3.根据ViewModel,新建View,代码如下:
- <StackPanel>
- <TextBox x:Name="Mytxt" />
-
- <Button Height="30" Margin="0,3" Content="Click Me">
- <i:Interaction.Triggers>
- <i:EventTrigger EventName="Click">
- <cal:ActionMessage MethodName="MyClick">
- <cal:Parameter Value="{Binding ElementName=Mytxt,Path=Text}"/>
- <cal:Parameter Value="aaabbbccc"/>
- </cal:ActionMessage>
- </i:EventTrigger>
- </i:Interaction.Triggers>
- </Button>
- <Button Height="30" Margin="0,3" Content="Click Me2" cal:Message.Attach="[Event Click]=[Action MyClick($source,'xiao')]"></Button>
- </StackPanel>
其中
- <i:Interaction.Triggers>
- <i:EventTrigger EventName="Click">
- <cal:ActionMessage MethodName="MyClick">
- <cal:Parameter Value="{Binding ElementName=Mytxt,Path=Text}"/>
- <cal:Parameter Value="aaabbbccc"/>
- </cal:ActionMessage>
- </i:EventTrigger>
- </i:Interaction.Triggers>
是控件的Click事件绑定的语法(长语法),其中EventTrigger可以指定哪个事件需要监听监听,通过Caliburn Micro的ActionMessage我们可以指定哪些方法应该调用。 使用这种方法可以使用相同的控制包含任意数量的事件触发监听其他事件。<cal:Parameter />表明此函数传递的参数是String类型的名称为Mytxt的元素的Text属性和字符串aaabbbccc。
这里ActionMessage处理的事情有二:
通过名字匹配自动把ShellViewModel设置为ShellView的DataContext。
通过反射用名字取到对应的方法,Invoke该方法。
这个思路是很清晰的,第一步的匹配是CM中的一个特色Convention,自动匹配XXViewModel和XXView。第二步会在前面查到的XXViewModel中查询名字为Show的MethodInfo,然后反射调用。
- cal:Message.Attach="[Event Click]=[Action MyClick($source,'xiao')]"
是控件的Click事件绑定的另外一种语法(短语法),事件的参数写在方法名称的方括号后。如果有多个事件需要响应,按如下方式书写即可:<Button cm:Message.Attach="[Event MouseEnter] = [Action Show('Enter')]; [Event MouseLeave] = [Action Show('Leave')]" />
这个Action,简单来说就是在事件A发生时执行对象B的C方法,
ActionMessage是继承自TriggerAction<FrameworkElement>的,TriggerAction来自于Blend的Interactivity,在事件A被执行时会调用到ActionMessage的Invoke方法。关于Blend的TriggerBase不是本文讨论的重点,只是说明事件A这一步由Blend保证完成。
在创建ActionMessage的时候设置了属性MethodName,这个MethodName指明了C方法,那么整个ActionMessage的关键就是如何找到对应的B对象。
4.定义IShell接口,代码如下:
- public interface IShell {
- }
此接口没有实质性用途,只是为MEF的使用提供基础。
5.实现BootStrapper,代码如下:
- public class AppBootStrapper : BootstrapperBase {
-
- public AppBootStrapper() {
- Initialize();
- }
-
- private CompositionContainer _container;
- protected override void Configure() {
- AggregateCatalog _catalog = new AggregateCatalog(
- AssemblySource.Instance.Select(x => new AssemblyCatalog(x)).OfType<ComposablePartCatalog>());
- _container = new CompositionContainer(_catalog);
-
- CompositionBatch _batch = new CompositionBatch();
- _batch.AddExportedValue<IWindowManager>(new WindowManager());
- _batch.AddExportedValue<IEventAggregator>(new EventAggregator());
-
- _batch.AddExportedValue(_container);
- _container.Compose(_batch);
- Coroutine.Completed += (s, e) => {
- if (e.Error != null) {
- MessageBox.Show(e.Error.Message);
- }
- };
-
- MessageBinder.SpecialValues.Add("$mysender", ctx => {
- var _ui = ctx.Source as UIElement;
- return _ui;
- });
-
- }
-
- protected override object GetInstance(Type service, string key) {
- string _contract = string.IsNullOrEmpty(key) ? AttributedModelServices.GetContractName(service) : key;
- var _exports = _container.GetExportedValues<object>(_contract);
- if (_exports.Any()) {
- return _exports.First();
- }
- throw new Exception(string.Format("找不到{0}实例!", _contract));
- }
-
- protected override IEnumerable<object> GetAllInstances(Type service) {
- return _container.GetExportedValues<object>(AttributedModelServices.GetContractName(service));
- }
-
- protected override void BuildUp(object instance) {
- _container.SatisfyImportsOnce(instance);
- }
- protected override IEnumerable<Assembly> SelectAssemblies() {
- return new[] {
- Assembly.GetExecutingAssembly()
- };
- }
- protected override void OnStartup(object sender, StartupEventArgs e) {
- DisplayRootViewFor<IShell>();
- }
- }
上面的代码简单实现了MEF的框架和原理,详细的MEF的知识,各位可以自己去理解!
6.将ViewModel输出成IShell类型,遵守MEF的契约,实现方式如下:
7.在App.xmal中添加BootStrapper,代码如下:
- <Application.Resources>
- <ResourceDictionary>
- <ResourceDictionary.MergedDictionaries>
- <ResourceDictionary>
- <local:AppBootStrapper x:Key="appboot"/>
- </ResourceDictionary>
- </ResourceDictionary.MergedDictionaries>
- </ResourceDictionary>
- </Application.Resources>
8.Enjoy it!