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


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

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

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

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

    image

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

    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 { 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 "-"; } }
          }
      }
    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 { 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 "-"; } }
          }
      }
    6. 运行。 这样就构建了一个简单的运算器,其中的Export、Import就像一个个管道一样相互连接。
      image
    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 { setget; }
              
      string Label { getset; }
          }
      }
      编译通过后在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的引用.
      image 
    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 { 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);
              }
          }
      }
    11. 上面的是静态加载,那么现在我们使用MEF实现动态扩展运算器。桌面程序的动态扩展是动态加载DLL,而对于Silverlight的Web程序则是动态加载Xap包了。
      新建普通Silverlight Application(Named DynamicExtension).

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

    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,最终界面。
      image 
      点击DynamicLoadOperate按钮后
      image

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

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

        

  • 相关阅读:
    【工作感悟】2022年8月23日
    [Anaconda]查看anaconda的方法
    [MACOS]BOOKS
    Mac_每次都要执行source ~/.bash_profile 配置的环境变量才生效
    [Anaconda]查看当前已经激活的anaconda的方法
    解决:DeprecationWarning: executable_path has been deprecated, please pass in a Service object
    [BREW]如何在MAC上安装Consolas
    20220917
    对net5.0的理解
    如何做到高效CI/CD
  • 原文地址:https://www.cnblogs.com/ysisl/p/1668054.html
Copyright © 2020-2023  润新知