zoukankan      html  css  js  c++  java
  • Caliburn实现MVVM模式的编程

    引言:什么是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 SDKsSilverlightv4.0LibrariesClientSystem.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上下了几天功夫。学习来源于网络,也发表一篇与大家共勉。

    帮忙右下角“赞”一下,“赞”的高尿的远!

     

  • 相关阅读:
    关于效应量
    sc 与 net 命令
    隐函数求导
    关于向量空间的基本性质,与子空间的最最基本性质
    生成随机数(C++)
    关于 setw() 函数(C++)
    关于 加减乘除 基本运算的性质
    为什么文件无法用wps打开,甚至wps.exe本身都无法打开?
    with open()函数中,如何在文件名设置中引用变量(python)
    pygame 运行心理学问卷
  • 原文地址:https://www.cnblogs.com/xcj26/p/4054222.html
Copyright © 2011-2022 走看看