引言:什么是Caliburn ?
一個夜晚,一處教堂,人們懺悔結束后抬頭看到一把寶劍插在一塊石頭上。石上字述“英格蘭人,凡能從石頭上拔出劍者,為王者!”,Caliburn就是英格蘭人心中的石中劍,這把劍的主人是亞瑟王,但是在一次格斗中,這把劍被伯林諾王斬斷。
Caliburn用于一個MVVM產品的名稱,其用意明顯,作者是想借助于Caliburn的“鋒利”來描述這個產品。
Caliburn是一個功能全面的MVVM產品,全面的同時帶來了代碼量的龐大,作者在這個基礎上做了一個精簡版,名為 Caliburn.Micro,簡寫為CM。
一:Caliburn環境搭建
1,Caliburn.Micro的下載地址:https://caliburnmicro.codeplex.com/releases/view/108277,下載完成后可以看一個名為Caliburn.Micro v1.5.2 Snapshot.zip的壓縮包。
隨時間推移,版本有可能更新,導致名字的變化,此版本下載于2014-10-28 08:07。
2,解壓過后可以看到如下目錄,每個目錄我做了一個簡單的備注,如果做應用型開發,我們只需關注samples就夠了。
點開bin目錄,我們可以看到Caliburn.Micro可用于silverlight,wpf,wp的開發應用。這篇博客重點以WPF的應用來表述MVVM的用法。
二:WPF下的Caliburn.Micro理論
1:雙向綁定
在做WPF下的MVVM編碼時,我們先普及一個WPF的常識,在WPF中一般有雙向綁定的機制,我們看到很多WPF程序的model,viewmodel都繼承自INotifyPropertyChanged接口,其實這是在為雙向綁定作鋪墊。
PropertyChangedBase 繼承自 INotifyPropertyChanged ,當我們向UI傳遞屬性變化并且更新客戶端UI時會用到INotifyPropertyChanged。
當一個集合項改變時我們則需要使用ObservableCollection<T>。
一般情況下,MVVM的ViewModel都會繼承PropertyChangedBase類,以便實現雙向綁定機制。
2:Action的處理
ActionMessage,利用TriggerAction的EventTrigger,可以把UI控件中的事件對應到后臺方法,類似于CallMethodAction。Caliburn.Micro對ActionMessage進行了擴展,可以傳入多個參數,參數支持綁定等功能。
3:Conventions的約定
Conventions,約定,只要View與ViewModel都遵守約定,就會有意想不到的效果,比如神奇的智能匹配。CM制定了一系列匹配的規則,View和ViewModel之間的匹配,控件名與屬性,方法的匹配。
4:Screen
在Caliburn中,Screen用來表示UI部件,并且定義UI部件的生命周期(Activated,DeActivated等)。Conductor用來管理Screen,一個Conductor可以管理一組Screen。
三:Caliburn.Micro的引導模式
1:標準WPF程序的引導
標準的WPF的啟動程序都是從設置Application結點的StartupUri屬性開始的。如下代碼:
<Application x:Class="WpfApplication1.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" StartupUri="MainWindow.xaml">
2:Caliburn.Micro程序的引導
Caliburn.Micro有自己的引導類,引導類主要通過Configure方法,采用MEF技術組合應用程序部件。
引導類的GetInstance,GetAllInstances,BuildUp,OnStartup方法寫法比較固定。除非有特殊的需求,基本不用修改。不過要注意silverlight,wpf,wp寫法上略有差異。
比如在實例化CompositionContainer容器時,silverlight用CompositionHost.Initialize方法,WPF用CompositionContainer構造函數。
在用SimpleContainer容器代替CompositionContainer容器時,應在OnStartup方法中加上DisplayRootViewFor<IShell>();這句代碼。還有若干的細節問題我們可以在samples例子中慢慢品味。
public class AppBootstrapper : Bootstrapper<IShell> { private CompositionContainer _container; //用MEF組合部件 protected override void Configure() { _container = new CompositionContainer( new AggregateCatalog( AssemblySource.Instance.Select(x => new AssemblyCatalog(x)).OfType<ComposablePartCatalog>() ) ); //Silverlight版本 //C:\Program Files (x86)\Microsoft SDKs\Silverlight\v4.0\Libraries\Client\System.ComponentModel.Composition.Initialization.dll //container = CompositionHost.Initialize( // 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 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)); } //將實例傳遞給 Ioc 容器,使依賴關系注入 protected override void BuildUp(object instance) { _container.SatisfyImportsOnce(instance); } protected override void OnStartup(object sender, StartupEventArgs e) { base.OnStartup(sender, e); //Silverlight //Application.Current.RootVisual = new ShellView(); //SimpleContainer //DisplayRootViewFor<IShell>(); } }
3:設置引導類的啟動
在silverlight中,啟動一個引導類
<Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:Caliburn.Micro.Hello" x:Class="Caliburn.Micro.Hello.App"> <Application.Resources> <local:HelloBootstrapper x:Key="bootstrapper" /> </Application.Resources> </Application>
在wpf中啟動一個引導類為
<Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local ="clr-namespace:Calib.DWpfApp1" x:Class="Calib.DWpfApp1.App"> <Application.Resources> <ResourceDictionary> <ResourceDictionary.MergedDictionaries> <ResourceDictionary> <local:AppBootstrapper x:Key="bootstrapper" /> </ResourceDictionary> </ResourceDictionary.MergedDictionaries> </ResourceDictionary> </Application.Resources> </Application>
四:代碼實踐
在xaml編程中,一般都會借助于Blend的兩個類庫 System.Windows.Interactivity.dll和Microsoft.Expression.Interactions.dll來進行編程。具體引用如下:
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
<!--或者-->
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
此處只引用了System.Windows.Interactivity.dll類庫。
<Window x:Class="Calib.DWpfApp1.MainView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" xmlns:cm="http://www.caliburnproject.org" Title="MainView" Height="500" Width="500">
a,Caliburn.Micro根據UI元素名稱匹配ViewModel的方法(無參數)
<Button x:Name="OpenOneChild1" Content="打開窗口(無參)" Width="240" Height="30"/>
b,Caliburn.Micro使用Message.Attach匹配ViewModel方法(無參數)
<Button Content="打開窗口(無參)" Width="240" Height="30" cm:Message.Attach="OpenOneChild1" />
c,Caliburn.Micro借助于TriggerAction實現ViewModel方法的調用(有參數)
<Button Content="打開窗口(有參)" Width="240" Height="30"> <i:Interaction.Triggers> <i:EventTrigger EventName="Click"> <cm:ActionMessage MethodName="OpenOneChild2"> <cm:Parameter Value="hello..."></cm:Parameter> </cm:ActionMessage> </i:EventTrigger> </i:Interaction.Triggers> </Button>
d,Caliburn.Micro使用Message.Attach匹配ViewModel方法(有參數)
<Button Content="打開窗口(有參,簡寫)" Width="240" Height="30" cm:Message.Attach="[Event Click] = [Action OpenOneChild2('woo~')]" />
e,Caliburn.Micro使用Message.Attach匹配多個ViewModel方法(有參數)
<Button Content="打開窗口(有參,簡寫,兩個事件)" Width="240" Height="30" cm:Message.Attach="[Event MouseEnter] = [Action Show('Enter')];[Event MouseLeave] = [Action Show('Leave')]" />
f,cm:Action.Target 用法
<ListBox Height="100" Name="listBox1" SelectionMode="Multiple" > <ListBoxItem>這是第一項</ListBoxItem> <ListBoxItem>這是第二項</ListBoxItem> <ListBoxItem>這是第三項</ListBoxItem> </ListBox> <Button Content="全選" HorizontalAlignment="Left" Focusable="False" Name="button1" cm:Action.Target="{Binding ElementName=listBox1}" cm:Message.Attach="[Event Click] = [Action SelectAll]"/>
ViewModel的源碼參考
[Export(typeof(IShell))] public class MainViewModel : PropertyChangedBase { readonly IWindowManager windowManager; public string MainTitle { get; private set; } [ImportingConstructor] public MainViewModel(IWindowManager wmanager) { MainTitle = "主窗體-MainView"; windowManager = wmanager; } public void OpenOneChild1() { ChildWindowViewModel childViewModel = new ChildWindowViewModel(); windowManager.ShowDialog(childViewModel); } public void OpenOneChild2(String para1) { ChildWindowViewModel childViewModel = new ChildWindowViewModel(); windowManager.ShowDialog(childViewModel); } public void Show(String para1) { System.Windows.MessageBox.Show(para1); } }
五:總結
近段時間接手惠普給我們公司開發的一個項目,我負責WPF程序部份,以前也斷斷續續的做過wpf的項目,但是用的是MVVMLight,這個項目用的是Caliburn.Micro。
所以在Caliburn.Micro上下了幾天功夫。學習來源于網絡,也發表一篇與大家共勉。
幫忙右下角“贊”一下,“贊”的高尿的遠!
文章列表