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

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

使用MEF构建可扩展的Silverlight应用_第1张图片
那么MEF是怎样工作的呢?简单分为三个步骤:

  • Export (输出)
  • Import (输入)
  • Compose (组合)

简短说一下MEF的工作原理,MEF的核心包括一个catalog和一个CompositionContainer。category用于发现扩展,而container用于协调创建和梳理依赖性。每个可组合的Part提供了一个或多个Export,并且通常依赖于一个或多个外部提供的服务或Import。每个Part管理一个实例为应用程序运行。

使用MEF构建可扩展的Silverlight应用_第2张图片

下面我们做一个小型可扩展计算器示例来解释这三个过程

  1. 首先下载MEF框架包,Silverlight 4.0会自带,不过微软已经将其开源了。
    http://www.codeplex.com/MEF
  2. 创建一个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, 添加后注意修改命名空间。
  3. 修改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   " - " ; } }
        }
    }
  4. 修改 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 >
  5. 新建类 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   " - " ; } }
        }
    }

     

  6. 运行。 这样就构建了一个简单的运算器,其中的Export、Import就像一个个管道一样相互连接。
    使用MEF构建可扩展的Silverlight应用_第3张图片
  7. 按照这样的设计,我们想要对其进行扩展,就必须把接口分离。新建一个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 {  set get ; }
            
    string  Label {  get set ; }
        }
    }
    编译通过后在Silverlight主工程(MefDemo)中添加对ContractLibrary项目的引用
  8. 再新建一个Silverlight ClassLibrary Project (Named StaticExtension),,这个工程就是我们用来静态扩展的DLL。
    a). 在工程下新建我们的运算器类,AddButton.cs , SubButton.cs.(代码不变).
    b). 但注意要添加对ContractLibrary项目的引用和MEF的框架集引用) 。
    c). 添加全局属性配置类(ComponentConfiguration.cs)
    d). 删除主工程中的ComponetOperater.cs.
    e). 添加对StaticExtension的引用.
    使用MEF构建可扩展的Silverlight应用_第4张图片 
  9. OK,这样我们就可以任意扩展运算器,添加更多的扩展运算了。

  10. 那么下面是添加一个新的乘法运算所要做的工作。
    在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 );
            }
        }
    }

     

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

    使用MEF构建可扩展的Silverlight应用_第5张图片
    去掉勾选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 );
            }
        }
    }

     

    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 ,为其注册下载包的相关事件和回调

  1. 代码
    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,最终界面。
    使用MEF构建可扩展的Silverlight应用_第6张图片 
    点击DynamicLoadOperate按钮后
    使用MEF构建可扩展的Silverlight应用_第7张图片

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

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

    

你可能感兴趣的:(使用MEF构建可扩展的Silverlight应用)