C# WPF Caliburn.Micro框架下利用Mef加载其它项目界面

01—前言

MEF是微软自家的托管可扩展框架,在这里我把它用成了ioc容器。在Caliburn.Micro框架下,view和viewmodel被注入到CompositionContainer容器中,然后通过名称可以实现view和viewmodel的匹配。利用这一特点,在多人合作项目开发中,一个解决方法就可以拆分成很多个项目,只用在主项目中搭建框架,每个分支项目开发好以后加载到容器中,就可以实现界面和逻辑的调用,可能这样解释有点生涩,具体我们看下面实例再去理解。.

02—新建项目MefTest

第一步 :在我们的解决方法下添加新的项目MefTest(类库)

C# WPF Caliburn.Micro框架下利用Mef加载其它项目界面

第二步:MefTest下添加MefTestView.xaml和MefTestViewModel.cs

MefTestViewModel:

[Export(typeof(MefTestViewModel))] 一般是继承公共的接口并导出,当然像我这样直接导出也是可以的.

 //[Export("PluginTestViewModel", typeof(IPluggablePart))]    //[PartCreationPolicy(CreationPolicy.Shared)]    ///也可以这样    [Export(typeof(MefTestViewModel))]//表示此类需要导出,导出的类型为object    public class MefTestViewModel    {        public void MefTestBtn()        {           MessageBox.Show("这是一个mef的测试类");        }        public int Sum(int a ,int b)        {            return a + b;        }    }

MefTestView.xaml:​​​

<UserControl x:Class="MefTest.MefTestView"             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"              xmlns:d="http://schemas.microsoft.com/expression/blend/2008"              xmlns:local="clr-namespace:MefTest"             mc:Ignorable="d"              d:DesignHeight="450" d:DesignWidth="800">    <Grid ShowGridLines="True">        <Grid.RowDefinitions>            <RowDefinition Height="*"/>            <RowDefinition Height="*"/>        </Grid.RowDefinitions>        <Grid.ColumnDefinitions>            <ColumnDefinition Width="*"/>            <ColumnDefinition Width="*"/>        </Grid.ColumnDefinitions>        <Button Name="MefTestBtn" Content="MefTestBtn" Background="LightCoral" FontSize="45"/>    </Grid></UserControl>

03—通过Mef注入dll

详细代码如下:

DisplayRootViewFor<StartViewModel>();//显示界面

这里也可以让主界面的viewmodel继承一个公共的接口,比如IShell,这样这里接可以改写为:

DisplayRootViewFor<IShell>();//显示界面

 

using Caliburn.Micro;using MefTest;using System;using System.Collections.Generic;using System.ComponentModel.Composition;using System.ComponentModel.Composition.Hosting;using System.ComponentModel.Composition.Primitives;using System.IO;using System.Linq;using System.Reflection;using System.Text;using System.Windows;
namespace CaliburnTest{    class MyBootstrapper : BootstrapperBase    {
        public MyBootstrapper()        {            Initialize();//初始化框架        }
        //容器,装东西用的。具体装什么先不管。        CompositionContainer container;
        //[Import]        //public MefTestParts mefTestParts { get; set; }        protected override void OnStartup(object sender, StartupEventArgs e)        {            DisplayRootViewFor<StartViewModel>();//显示界面        }        //private IDialogManager dialogManager = PlatformIoC.Get<IDialogManager>();        //[Import(typeof(ContainerTest))]        //public ContainerTest ts { get; set; }        /// <summary>        /// 方法1        /// </summary>        protected void Configure0()        {            var envirmentPath = System.IO.Directory.GetCurrentDirectory();            //AssemblyCatalog 目录的一种,表示在程序集中搜索            var assemblyCatalog = new AssemblyCatalog(typeof(StartViewModel).Assembly);//此处这一句实际上没啥用,因为此程序集下没有任何我们需要的实例(各种handler)                                                                                       //在某个目录下的dll中搜索。                                                                                       //var directoryCatalog = new DirectoryCatalog(@"C:\Program Files (x86)\YWTK\TOOLS\PLUGIN-LIBS\MISC\I12\", "*.dll");            var directoryCatalog = new DirectoryCatalog(envirmentPath, @"ContainerDLL.dll");
            var aggregateCatalog = new AggregateCatalog(assemblyCatalog, directoryCatalog);
            //创建搜索到的部件,放到容器中。            container = new CompositionContainer(aggregateCatalog);
            var batch = new CompositionBatch();      //如果还有自己的部件都加在这个地方            batch.AddExportedValue<IWindowManager>(new WindowManager());            batch.AddExportedValue<IEventAggregator>(new EventAggregator());            batch.AddExportedValue(container);
            this.container.Compose(batch);
        }        /// <summary>        /// 方法2        /// </summary>        protected override void Configure()        {            var envirmentPath = System.IO.Directory.GetCurrentDirectory();
            AssemblySource.Instance.Add(Assembly.LoadFile(Path.Combine(envirmentPath, @"MefTest.dll")));            //AssemblySource.Instance.Add(Assembly.LoadFile(Path.Combine(envirmentPath, @"PluginTest.dll")));            var catalog = new AggregateCatalog(                AssemblySource.Instance.Select(x => new AssemblyCatalog(x)).OfType<ComposablePartCatalog>());            this.container = new CompositionContainer(catalog);
            var batch = new CompositionBatch();      //如果还有自己的部件都加在这个地方            batch.AddExportedValue<IWindowManager>(new WindowManager());            batch.AddExportedValue<IEventAggregator>(new EventAggregator());            batch.AddExportedValue(this.container);
            this.container.Compose(batch);
        }
        protected override object GetInstance(Type service, string key)        {            if (service == null)            {                return null;            }            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("Could not locate any instances of contract {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 void OnExit(object sender, EventArgs e)        {            base.OnExit(sender, e);            this.Application.Shutdown();        }
    }

}

04—主程序加载和调用

StartView.xaml中添加一个tab页:

 <TabItem x:Name="Up5" Header="MefTest" >                <ContentControl cal:View.Model="{Binding MefTestView}"/>            </TabItem>

viewmodel中:

定义 MefTestViewModel

public MefTestViewModel MefTestView  { get;set;}

然后在主程序的构造函数中通过ioc获取viewmodel实例:

  MefTestView = IoC.Get<MefTestViewModel>();

这样其它项目的界面就成功的被加载到了我们的主项目中,然而我们并没有实例化,这样如果我们定义了公共的接口,直接导出接口类型,就很好地实现了主项目和子项目的解耦。

05—运行结果

C# WPF Caliburn.Micro框架下利用Mef加载其它项目界面