使用MEF构建可扩展的Silverlight应用

托管扩展性框架(Managed Extensibility Framework,简称MEF),是微软.NET框架下为提高应用和组件复用程度而推出的,用于使组件能够最大化的重用。使用MEF能够使静态编译的.NET应用程序转换为动态组合,这将是创建可扩展应用、可扩展框架和应用扩展的好途径。它将做为.NET Framework 4.0的组成部分之一发布。现在,MEF也将被包含在Silverlight 4.0中。


那么MEF是怎样工作的呢?简单分为三个步骤:

·      Export (输出)

·      Import (输入)

·      Compose (组合)

简短说一下MEF的工作原理,MEF的核心包括一个catalog和一个CompositionContainercategory用于发现扩展,而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.csPackageCatalog.cs,这两个文件在最新的MEF版本中没有提供,主要用于加载silverlightXap包。
这两个文件在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 { setget; }
        
string Label { getset; }
    }

    
/// <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 { setget; }

        [Import(
"ClickHandler")]
        
public OperateHandler ClickAction { getset; }

        
#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 { setget; }

        [Import(
"ClickHandler")]
        
public OperateHandler ClickAction { getset; }

        
#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 { setget; }
        
string Label { getset; }
    }

    
/// <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 { setget; }

        [Import(
"ClickHandler")]
        
public OperateHandler ClickAction { getset; }

        
#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 { setget; }

        [Import(
"ClickHandler")]
        
public OperateHandler ClickAction { getset; }

        
#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 "-"; } }
    }
}

 

·         运行。 这样就构建了一个简单的运算器,其中的ExportImport就像一个个管道一样相互连接。

·         按照这样的设计,我们想要对其进行扩展,就必须把接口分离。新建一个Silverlight ClassLibrary Project(Named ContractLibrary),这个Library用来封装所有的扩展接口,定义Import/Export契约。
现在把原项目中的OperatorComponent.cs 类中的接口迁移到Library项目中,新建类文件OperateContract.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;

namespace ContractLibrary
{
    
public delegate void OperateHandler(IOperate Op);

    
/// <summary>
    
/// 运算器接口
    
/// </summary>
    public interface IOperate
    {
        
double Op(double left, double right);
        
string Symbol { setget; }
        
string Label { getset; }
    }
}

编译通过后在Silverlight主工程(MefDemo)中添加对ContractLibrary项目的引用

·         再新建一个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 { setget; }

        [Import(
"ClickHandler")]
        
public OperateHandler ClickAction { getset; }

        
#region IOperate 成员

        
public double Op(double left, double right)
        {
            
return left * right;
        }

        
#endregion

        
public MultiplyButton()
        {
            
this.Click += (s, e) => ClickAction(this);
        }
    }
}

 

·         上面的是静态加载,那么现在我们使用MEF实现动态扩展运算器。桌面程序的动态扩展是动态加载DLL,而对于SilverlightWeb程序则是动态加载Xap包了。
新建普通Silverlight ApplicationNamed DynamicExtension.


去掉勾选Add a test page that references the application.
1).
删掉AppMain等不必要的文件,只留一个空的Silverlight项目,以减少Xap包的大小。
2).
添加ContractLibraryMEF框架集的引用(可以将引用程序集属性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 { setget; }

        [Import(
"ClickHandler")]
        
public OperateHandler ClickAction { getset; }

        
#region IOperate 成员

        
public double Op(double left, double right)
        {
            
return left * right;
        }

        
#endregion

        
public DivisionButton()
        {
            
this.Click += (s, e) => ClickAction(this);
        }
    }
}

 

4).  添加配置类ComponentConfiguration.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;

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)
        {
        }

    }
}

 

Ok,最终界面。
 
点击DynamicLoadOperate按钮后

    程序中还有很多细节没有展开说明,理论性的介绍可以参考MSDNCodePlex上的文档。
    

    源码下载
    
    值此新春佳节,祝大家春节快乐!:)

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值