“托管扩展性框架(Managed Extensibility Framework,简称MEF),是微软.NET框架下为提高应用和组件复用程度而推出的,用于使组件能够最大化的重用。使用MEF能够使静态编译的.NET应用程序转换为动态组合,这将是创建可扩展应用、可扩展框架和应用扩展的好途径。它将做为.NET Framework 4.0的组成部分之一发布。现在,MEF也将被包含在Silverlight 4.0中。
- Export (输出)
- Import (输入)
- Compose (组合)
简短说一下MEF的工作原理,MEF的核心包括一个catalog和一个CompositionContainer。category用于发现扩展,而container用于协调创建和梳理依赖性。每个可组合的Part提供了一个或多个Export,并且通常依赖于一个或多个外部提供的服务或Import。每个Part管理一个实例为应用程序运行。
下面我们做一个小型可扩展计算器示例来解释这三个过程
- 首先下载MEF框架包,Silverlight 4.0会自带,不过微软已经将其开源了。
http://www.codeplex.com/MEF - 创建一个Silverlight Navigate Application ,并添加程序集引用(MEF_Beta_2\bin\SL目录下 System.ComponentModel.Composition.dll)
在项目下添加两个类文件Package.cs和PackageCatalog.cs,这两个文件在最新的MEF版本中没有提供,主要用于加载silverlight的Xap包。
这两个文件在MEF框架的Sample中提供了(MEF_Beta_2\Samples\PictureViewer\PictureViewer.Common),将这两个类的访问修饰符改为public, 添加后注意修改命名空间。 - 修改Home.cs 类
代码using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.ComponentModel.Composition;
using System.ComponentModel;
namespace MefDemo
{
// 用于更新界面的委托
public delegate void OperateHandler(IOperate Op);
/// <summary>
/// 运算器接口
/// </summary>
public interface IOperate
{
double Op( double left, double right);
string Symbol { set ; get ; }
string Label { get ; set ; }
}
/// <summary>
/// 加法运算器
/// </summary>
[Export( typeof (IOperate))]
public class AddButton : Button, IOperate
{
[Import( " AddButtonContract " ,AllowRecomposition = true )]
public string Label { get { return this .Content.ToString(); } set { this .Content = value; } }
[Import( " AddSybomContract " , AllowRecomposition = true )]
public string Symbol { set ; get ; }
[Import( " ClickHandler " )]
public OperateHandler ClickAction { get ; set ; }
#region IOperate 成员
public double Op( double left, double right)
{
return left + right;
}
#endregion
public AddButton()
{
this .Click += (s, e) => ClickAction( this );
}
}
/// <summary>
/// 减法运算器
/// </summary>
[Export( typeof (IOperate))]
public class SubButton : Button, IOperate
{
[Import( " SubButtonContract " ,AllowRecomposition = true )]
public string Label { get { return this .Content.ToString(); } set { this .Content = value; } }
[Import( " SubSybomContract " , AllowRecomposition = true )]
public string Symbol { set ; get ; }
[Import( " ClickHandler " )]
public OperateHandler ClickAction { get ; set ; }
#region IOperate 成员
public double Op( double left, double right)
{
return left - right;
}
#endregion
public SubButton()
{
this .Click += (s, e) => ClickAction( this );
}
}
/// <summary>
/// 为每个运算器的属性提供值
/// </summary>
public class ComponentAttributeProvider
{
[Export( " AddButtonContract " )]
public string AddLabel { get { return " Add " ; } }
[Export( " AddSybomContract " )]
public string AddSymbol { get { return " + " ; } }
[Export( " SubButtonContract " )]
public string SubLabel { get { return " Sub " ; } }
[Export( " SubSybomContract " )]
public string SubSymbol { get { return " - " ; } }
}
} - 修改 Home.xaml
代码< navigation:Page x:Class ="MefDemo.Home"
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:navigation ="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
mc:Ignorable ="d" d:DesignWidth ="640" d:DesignHeight ="480"
Title ="Home"
Style =" {StaticResource PageStyle} " >
< navigation:Page.Resources >
< ItemsPanelTemplate x:Key ="ItemsPanelTemplate1" >
< StackPanel Orientation ="Horizontal" HorizontalAlignment ="Center" VerticalAlignment ="Center" />
</ ItemsPanelTemplate >
</ navigation:Page.Resources >
< Grid x:Name ="LayoutRoot" >
< ScrollViewer x:Name ="PageScrollViewer" Style =" {StaticResource PageScrollViewerStyle} " >
< StackPanel x:Name ="ContentStackPanel" Background ="Black" >
< StackPanel Orientation ="Horizontal" Width ="455" Height ="89" Margin ="91,0,91,-30" >
< TextBox x:Name ="LeftNum" HorizontalAlignment ="Left" VerticalAlignment ="Center" Width ="83" TextWrapping ="Wrap" />
< TextBlock x:Name ="Symbol" Width ="62" Text ="+" TextWrapping ="Wrap" FontSize ="24" Foreground ="#FFF80606" TextAlignment ="Center" VerticalAlignment ="Center" HorizontalAlignment ="Left" />
< TextBox x:Name ="RightNum" HorizontalAlignment ="Left" VerticalAlignment ="Center" Width ="78" TextWrapping ="Wrap" />
< TextBlock Width ="64" Text ="=" TextWrapping ="Wrap" Foreground ="#FFF20808" FontSize ="21.333" TextAlignment ="Center" VerticalAlignment ="Center" HorizontalAlignment ="Left" />
< TextBox x:Name ="Result" HorizontalAlignment ="Left" VerticalAlignment ="Center" Width ="146" TextWrapping ="Wrap" />
</ StackPanel >
< ListBox x:Name ="operateList" ItemsSource =" {Binding} " ItemsPanel =" {StaticResource ItemsPanelTemplate1} " Height ="99" Background =" {x:Null} " BorderBrush =" {x:Null} " />
< Button x:Name ="DynamicLoadButton" Height ="40" Width ="196" Content ="DynamicLoadOperate" />
</ StackPanel >
</ ScrollViewer >
</ Grid >
</ navigation:Page >
- 新建类 OperatorComponent.cs
代码using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.ComponentModel.Composition;
using System.ComponentModel;
namespace MefDemo
{
// 用于更新界面的委托
public delegate void OperateHandler(IOperate Op);
/// <summary>
/// 运算器接口
/// </summary>
public interface IOperate
{
double Op( double left, double right);
string Symbol { set ; get ; }
string Label { get ; set ; }
}
/// <summary>
/// 加法运算器
/// </summary>
[Export( typeof (IOperate))]
public class AddButton : Button, IOperate
{
[Import( " AddButtonContract " ,AllowRecomposition = true )]
public string Label { get { return this .Content.ToString(); } set { this .Content = value; } }
[Import( " AddSybomContract " , AllowRecomposition = true )]
public string Symbol { set ; get ; }
[Import( " ClickHandler " )]
public OperateHandler ClickAction { get ; set ; }
#region IOperate 成员
public double Op( double left, double right)
{
return left + right;
}
#endregion
public AddButton()
{
this .Click += (s, e) => ClickAction( this );
}
}
/// <summary>
/// 减法运算器
/// </summary>
[Export( typeof (IOperate))]
public class SubButton : Button, IOperate
{
[Import( " SubButtonContract " ,AllowRecomposition = true )]
public string Label { get { return this .Content.ToString(); } set { this .Content = value; } }
[Import( " SubSybomContract " , AllowRecomposition = true )]
public string Symbol { set ; get ; }
[Import( " ClickHandler " )]
public OperateHandler ClickAction { get ; set ; }
#region IOperate 成员
public double Op( double left, double right)
{
return left - right;
}
#endregion
public SubButton()
{
this .Click += (s, e) => ClickAction( this );
}
}
/// <summary>
/// 为每个运算器的属性提供值
/// </summary>
public class ComponentAttributeProvider
{
[Export( " AddButtonContract " )]
public string AddLabel { get { return " Add " ; } }
[Export( " AddSybomContract " )]
public string AddSymbol { get { return " + " ; } }
[Export( " SubButtonContract " )]
public string SubLabel { get { return " Sub " ; } }
[Export( " SubSybomContract " )]
public string SubSymbol { get { return " - " ; } }
}
}
- 运行。 这样就构建了一个简单的运算器,其中的Export、Import就像一个个管道一样相互连接。
- 按照这样的设计,我们想要对其进行扩展,就必须把接口分离。新建一个Silverlight ClassLibrary Project(Named ContractLibrary),这个Library用来封装所有的扩展接口,定义Import/Export契约。
现在把原项目中的OperatorComponent.cs 类中的接口迁移到Library项目中,新建类文件OperateContract.cs 。
代码编译通过后在Silverlight主工程(MefDemo)中添加对ContractLibrary项目的引用using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
namespace ContractLibrary
{
public delegate void OperateHandler(IOperate Op);
/// <summary>
/// 运算器接口
/// </summary>
public interface IOperate
{
double Op( double left, double right);
string Symbol { set ; get ; }
string Label { get ; set ; }
}
}
- 再新建一个Silverlight ClassLibrary Project (Named StaticExtension),,这个工程就是我们用来静态扩展的DLL。
a). 在工程下新建我们的运算器类,AddButton.cs , SubButton.cs.(代码不变).
b). 但注意要添加对ContractLibrary项目的引用和MEF的框架集引用) 。
c). 添加全局属性配置类(ComponentConfiguration.cs)
d). 删除主工程中的ComponetOperater.cs.
e). 添加对StaticExtension的引用.
-
OK,这样我们就可以任意扩展运算器,添加更多的扩展运算了。
-
那么下面是添加一个新的乘法运算所要做的工作。
在StaticExtension中添加新类 Multiply.cs代码using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.ComponentModel.Composition;
using ContractLibrary;
namespace StaticExtension
{
/// <summary>
/// 乘法运算器
/// </summary>
[Export( typeof (IOperate))]
public class MultiplyButton : Button, IOperate
{
[Import( " MultiplyButtonContract " , AllowRecomposition = true )]
public string Label { get { return this .Content.ToString(); } set { this .Content = value; } }
[Import( " MultiplySybomContract " , AllowRecomposition = true )]
public string Symbol { set ; get ; }
[Import( " ClickHandler " )]
public OperateHandler ClickAction { get ; set ; }
#region IOperate 成员
public double Op( double left, double right)
{
return left * right;
}
#endregion
public MultiplyButton()
{
this .Click += (s, e) => ClickAction( this );
}
}
}
- 上面的是静态加载,那么现在我们使用MEF实现动态扩展运算器。桌面程序的动态扩展是动态加载DLL,而对于Silverlight的Web程序则是动态加载Xap包了。
新建普通Silverlight Application(Named DynamicExtension).
去掉勾选Add a test page that references the application.
1). 删掉App和Main等不必要的文件,只留一个空的Silverlight项目,以减少Xap包的大小。
2). 添加ContractLibrary和MEF框架集的引用(可以将引用程序集属性CopyLocal设置为false,因为我们在主工程中已经添加了,可以重用)
3). 添加类Division.cs.
代码using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.ComponentModel.Composition;
using ContractLibrary;
namespace DynamicExtension
{
/// <summary>
/// 乘法运算器
/// </summary>
[Export( typeof (IOperate))]
public class DivisionButton : Button, IOperate
{
[Import( " DivisionButtonContract " , AllowRecomposition = true )]
public string Label { get { return this .Content.ToString(); } set { this .Content = value; } }
[Import( " DivisionSybomContract " , AllowRecomposition = true )]
public string Symbol { set ; get ; }
[Import( " ClickHandler " )]
public OperateHandler ClickAction { get ; set ; }
#region IOperate 成员
public double Op( double left, double right)
{
return left * right;
}
#endregion
public DivisionButton()
{
this .Click += (s, e) => ClickAction( this );
}
}
}
代码using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.ComponentModel.Composition;
namespace DynamicExtension
{
/// <summary>
/// 为每个运算器的属性配置值
/// </summary>
public class ComponentConfiguration
{
[Export( " DivisionButtonContract " )]
public string AddLabel { get { return " Div " ; } }
[Export( " DivisionSybomContract " )]
public string AddSymbol { get { return " / " ; } }
}
}
5). 修改Home.cs ,为其注册下载包的相关事件和回调
-
代码using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;
using System.Collections.ObjectModel;
using System.ComponentModel.Composition.Hosting;
using System.Reflection;
using System.ComponentModel.Composition;
using ContractLibrary;
namespace MefDemo
{
public partial class Home : Page
{
[ImportMany( typeof (IOperate),AllowRecomposition = true )]
public ObservableCollection < IOperate > Operates = new ObservableCollection < IOperate > ();
[Export( " ClickHandler " )]
public OperateHandler ClickHandler { get { return OperateButton_Click; } }
private PackageCatalog Catalog;
/// <summary>
/// 用于界面控件响应运算后的一些更新工作
/// </summary>
/// <param name="Operate"> 运算器 </param>
public void OperateButton_Click(IOperate Operate)
{
try
{
Symbol.Text = Operate.Symbol;
double left = double .Parse(LeftNum.Text);
double right = double .Parse(RightNum.Text);
this .Result.Text = Operate.Op(left, right).ToString();
}
catch (Exception e)
{
ChildWindow errorWin = new ErrorWindow(e);
errorWin.Show();
}
}
public Home()
{
InitializeComponent();
this .Loaded += new RoutedEventHandler(Home_Loaded);
// 注册按钮事件
this .DynamicLoadButton.Click += (s, e) =>
{
// 下载包
Package.DownloadPackageAsync(
new Uri( " DynamicExtension.xap " , UriKind.Relative),
(args, package) => Catalog.AddPackage(package)
);
// 包被添加到PackageCatalog后会自动重新组合
// 并对添加了AllowRecomposition = true属性的Import导入器重新输入数据
};
}
void Home_Loaded( object sender, RoutedEventArgs e)
{
// 组合当前XAP包中所有部件(Parts)
Catalog = new PackageCatalog();
Catalog.AddPackage(Package.Current);
CompositionContainer container = new CompositionContainer(Catalog);
container.ComposeParts( this );
// 组合后所有实现运算接口(IOperate)的运算器都将被自动填充到 Operates 集合。
// 将运算器绑定到 ListBox 控件,用于呈现。
this .operateList.DataContext = Operates;
}
// Executes when the user navigates to this page.
protected override void OnNavigatedTo(NavigationEventArgs e)
{
}
}
}
点击DynamicLoadOperate按钮后
程序中还有很多细节没有展开说明,理论性的介绍可以参考MSDN和CodePlex上的文档。
源码下载
值此新春佳节,祝大家春节快乐!:)