一、MEF
简介
1、 Managed Extensibility Framework
简称MEF
,是WPF中的一种插件式开发的框架。其开发程序的主要优点如下:
- 易于将复杂程序进行拆分成不同的功能模块,然后进行多团队的协作。将各个功能编译成
dll
的形式提供给主程序,由主程序进行统一加载。 - 便于程序的后期扩展,在后期需要添加新功能时,只需要提供对应的
dll
即可,主程序无需进行大的改动。 - 不同模块间的功能相对独立,降低程序的耦合性,使程序易于维护。
2、MEF
的使用需要引用System.ComponentModel.Composition,该dll
提供了MEF
开发的核心功能。
3、MEF
程序的组成部分:
- 主程序:一般为一个WPF可执行应用程序,用来提供整个程序的框架,并加载各个功能模块所编译生成的
dll
程序集。 - 接口程序:一般为类库项目(即
Class Library
),编译生成dll
文件。主要用来定义主程序与各个Plugin
插件程序之间的接口,只有定义好这些接口才能保证主程序正确加载各个Plugin
- 插件程序:一般为
WPF User Control Library
,即各个独立的功能模块,编译生成dll
文件,被主程序加载使用。
二、MEF
程序创建流程
所有项目均位于解决方案MEF_Project
下
1、创建WPF
应用主程序
1.1 主程序为WPF Application
1.2 后续接口项目和各个Plugin
项目编译完成后,需要再完善主程序以加载各个插件
2、创建接口项目MyInterface
2.1 接口项目中,接口类型的关键字为interface
,在接口中只能声明而不能有具体的实现,具体的实现需要在其派生类中来进行。
2.2 该项目中定义如下接口:
namespace MyInterface
{
/// <summary>
/// 定义接口类型
/// </summary>
public interface IMyInterface
{
string GetPluginName();
int GetPluginId();
}
}
2.3 因为后续的Plugin
项目需要引用该接口生成的dll
文件,所以需要先对该接口项目进行编译,以生成对应的dll
文件,在此编译生成 MyInterface.dll
文件。
3、创建插件项目PluginA
该项目为UserControl
类型,如下图所示
3.1 对XAML
文件进行设计,如下所示:
<UserControl x:Class="PluginA.UserControl1"
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:PluginA"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<StackPanel>
<Label x:Name="labelA" Content="Pugin A Page" FontSize="40" HorizontalAlignment="Center"/>
</StackPanel>
</UserControl>
3.2 .cs
文件中,添加两个对应的dll
到References,并引用到命名空间中
(1)、在References
中添加MEF
对应dll
:System.ComponentModel.Composition;
,并引用到命名空间中,如下图:
(2)、在References
中添加接口项目编译生成的dll
:MyInterface.dll
,并引用到命名空间中,如下图:
3.3 导入并实现该接口,代码如下:
using System.Windows.Controls;
using System.ComponentModel.Composition;
using MyInterface;
namespace PluginA
{
/// <summary>
/// Interaction logic for UserControl1.xaml
/// </summary>
// 导入该接口
[Export(typeof(IMyInterface))]
public partial class UserControl1 : UserControl, IMyInterface
{
public UserControl1()
{
InitializeComponent();
}
//对接口IMyInterface的实现
public int GetPluginId()
{
return 0;
}
public string GetPluginName()
{
return "This is Plugin A";
}
}
}
3.3 编译生成对应的PluginA.dll
文件
【以同样的方式创建PluginB
】
4、完善主程序MainProgram
,加载上面编译生成的各个Plugin
插件
4.1 设计主程序的UI
,对应的MainWindow.xaml
文件如下:
<Window x:Class="MainProgram.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:MainProgram"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<!--左侧为ListView栏-->
<ListView x:Name="ItemsList" Grid.Column="0" Background="Bisque"/>
<!--中间为分割条-->
<GridSplitter Grid.Column="1" Width="6" VerticalAlignment="Stretch" HorizontalAlignment="Center" ShowsPreview="False"/>
<!--右侧显示加载的各个Plugin的内容-->
<Frame x:Name="frameContent" Grid.Column="2"/>
</Grid>
</Window>
4.2 在MainWindow.xaml.cs
中添加对应的dll
文件
(1)、在References
中添加MEF
对应dll
:System.ComponentModel.Composition;
,并引用到命名空间中。
(2)、在References
中添加接口项目编译生成的dll
:MyInterface.dll
,并引用到命名空间中。
4.3 在主程序中解析、加载对应Plugin
程序集,代码如下:
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition.Hosting;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using MyInterface;
namespace MainProgram
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private IEnumerable<IMyInterface> myPlugins;
public MainWindow()
{
InitializeComponent();
//new DirectoryCatalog 的默认加载目录为:该程序的exe文件目录下,为Debug目录或Release目录
var catalog = new DirectoryCatalog("../../MyPlugins");
var container = new CompositionContainer(catalog);
myPlugins = container.GetExportedValues<IMyInterface>();
foreach (var plg in myPlugins)
{
/*该方式只是简单的加载显示,但没有任何事件,点击也无效
* string name = plg.GetPluginName();
this.ItemsList.Items.Add(new TabItem() { Header = name, Content = plg });*/
Button btn = new Button();
btn.FontSize = 20;
btn.FontFamily = new FontFamily("Times New Roman");
btn.HorizontalAlignment = HorizontalAlignment.Center;
btn.Content = plg.GetPluginName();
btn.Tag = plg.GetPluginId();
btn.Click += Btn_Clicked;
ItemsList.Items.Add(btn);
}
}
private void Btn_Clicked(object sender, RoutedEventArgs e)
{
Button btn = sender as Button;
int id = Convert.ToInt32(btn.Tag); //Tag为object类型,需要显示的转换成int类型
frameContent.Content = myPlugins.ElementAt(id);
}
}
}
4.4 在主程序目录下创建文件夹,存放各个Plugin
编译生成的dll
文件,以被主程序加载,整体的文件结构如下所示:
MainProgram
:为主程序项目目录MyInterface
:为接口程序项目目录PluginA
:为插件A项目目录PluginB
:为插件B项目目录
4.5 运行结果如下:
参考资料:
[1] Managed Extensibility Framework
[2] System.ComponentModel.Composition Namespace