就我個人的了解,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);
}
};
//注意這個$mysender 都是小寫的在view層寫的時候可以不區分大小寫
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的契約,實作方式如下:
[Export(typeof(IShell))]
7.在App.xmal中添加BootStrapper,代碼如下:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary>
<local:AppBootStrapper x:Key="appboot"/>
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
8.Enjoy it!