插件(add-in、plug-in)是应用程序可以动态发现、加载和使用的单独编译过的组件。编程
容许第三方开发人员扩展应用程序的功能。如PS中的插件提供大量图片处理效果; firefox中插件提供了加强Web冲浪及全新功能。插件模型的主要优势是不须要为许多任务(如发现)编写底层代码,主要缺点是很是复杂。架构
MAF:托管插件框架下的插件模型。较可靠的框架,适用于应用程序和插件不一样团队各自开发,还特别适于第三方插件;
MAF依赖于定义的接口,在处理不一样版本、容许将插件加载到独立应用程序域中,有很大灵活优点。缺陷是为支持这些功能,MAF显得复杂,设置繁琐。
MEF:托管可扩展性框架的新模型。轻量级选择,适用于单个开发团队,用于以不一样方式组装模块化程序,为单独的发布提供不一样的功能实现。
缺陷是太松散,相互关联的部件一复杂就容易变得混乱。详情查看http://tinyurl.com/37s2jdx。
如对可组合的应用程序有兴趣,可查看复合应用程序库CAL(CAL只针对WPF应用程序)。而MEF是用于构建各类模块化.NET应用程序的通用解决方案。如下是MAF内容示例。框架
ps:a,当进行编译时,output目录一般放置应用程序和全部管道组件的地方。
b,修改好每一个组件项目的生成路径,及防止复制引用的程序集,设置copy local为false。dom
首先,宿主应用程序调用宿主视图中的方法。背后体现为应用程序经过宿主视图调用宿主方适配器中的方法,
而后宿主方适配器调用协定接口的相应方法,该方法是由插件方适配器实现的。
最后,插件方适配器调用插件视图中的方法。这个方法是由插件实现的,负责执行实际工做。ide
效果图模块化
using System.AddIn.Pipeline; using System.AddIn.Contract; namespace Contract { [AddInContract] public interface IImageProcessorContract : IContract { byte[] ProcessImageBytes(byte[] pixels); } }
ps:可在协定程序集中自定义传递类型,可串行化的。或者设计接口提供一个返回一系列可配置参数的方法。函数
namespace AddInView { [AddInBase] public abstract class ImageProcessorAddInView { public abstract byte[] ProcessImageBytes(byte[] pixels); } }
using System; using System.AddIn; namespace FadeImageAddIn { [AddIn("Fade Image Processor", Version = "1.0.0.0", Publisher = "SupraImage", Description = "Darkens the picture")] public class FadeImageProcessor : AddInView.ImageProcessorAddInView { public override byte[] ProcessImageBytes(byte[] pixels) { Random rand = new Random(); int offset = rand.Next(0, 10); for (int i = 0; i < pixels.Length - 1 - offset; i++) { if ((i + offset) % 5 == 0) { pixels[i] = 0; } } return pixels; } } }
ps:插件适配器必须提供接收恰当视图类的实例做为参数的构造函数,以备后用。this
using System.AddIn.Pipeline; namespace AddInSideAdapter { [AddInAdapter] public class ImageProcessorViewToContractAdapter : ContractBase, Contract.IImageProcessorContract { private AddInView.ImageProcessorAddInView view; public ImageProcessorViewToContractAdapter(AddInView.ImageProcessorAddInView view) { this.view = view; } public byte[] ProcessImageBytes(byte[] pixels) { return view.ProcessImageBytes(pixels); } } }
namespace HostView { public abstract class ImageProcessorHostView { public abstract byte[] ProcessImageBytes(byte[] pixels); } }
e,宿主适配器
接收一个实现了协定的对象,而后调用宿主方适配器的方法使用该对象。而后后台调用协定接口方法,向前穿过应用程序边界,并转换为调用插件适配器的相应方法。url
using System.AddIn.Pipeline; namespace HostSideAdapter { [HostAdapter] public class ImageProcessorContractToViewHostAdapter : HostView.ImageProcessorHostView { private Contract.IImageProcessorContract contract; private ContractHandle contractHandle; public ImageProcessorContractToViewHostAdapter(Contract.IImageProcessorContract contract) { this.contract = contract; contractHandle = new ContractHandle(contract); } public override byte[] ProcessImageBytes(byte[] pixels) { return contract.ProcessImageBytes(pixels); } } }
如今已构建好了插件模型的基础架构,最后是建立使用插件模型的应用程序,任何类型的.net可执行程序均可以做为宿主,但当前为WPF宿主。spa
using System; using System.Collections.Generic; using System.Windows; using System.Windows.Controls; using System.Windows.Media.Imaging; using System.AddIn.Hosting; namespace ApplicationHost { /// <summary> /// Interaction logic for Window1.xaml /// </summary> public partial class Window1 : Window { public Window1() { InitializeComponent(); } private void Window_Loaded(object sender, RoutedEventArgs e) { string path = Environment.CurrentDirectory; AddInStore.Update(path); IList<AddInToken> tokens = AddInStore.FindAddIns(typeof(HostView.ImageProcessorHostView), path); lstAddIns.ItemsSource = tokens; } private void cmdProcessImage_Click(object sender, RoutedEventArgs e) { BitmapSource originalSource = (BitmapSource)img.Source; int stride = originalSource.PixelWidth * originalSource.Format.BitsPerPixel/8; stride = stride + (stride % 4) * 4; byte[] originalPixels = new byte[stride * originalSource.PixelHeight * originalSource.Format.BitsPerPixel / 8]; originalSource.CopyPixels(originalPixels, stride, 0); AddInToken token = (AddInToken)lstAddIns.SelectedItem; HostView.ImageProcessorHostView addin = token.Activate<HostView.ImageProcessorHostView>(AddInSecurityLevel.Internet); byte[] changedPixels = addin.ProcessImageBytes(originalPixels); BitmapSource newSource = BitmapSource.Create(originalSource.PixelWidth, originalSource.PixelHeight, originalSource.DpiX, originalSource.DpiY, originalSource.Format, originalSource.Palette, changedPixels, stride); img.Source = newSource; } private void lstAddIns_SelectionChanged(object sender, SelectionChangedEventArgs e) { cmdProcessImage.IsEnabled = (lstAddIns.SelectedIndex != -1); } } }
<Window x:Class="ApplicationHost.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="ApplicationHost" Height="300" Width="300" Loaded="Window_Loaded"> <Grid Margin="3"> <Grid.RowDefinitions> <RowDefinition></RowDefinition> <RowDefinition Height="2*"></RowDefinition> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition></ColumnDefinition> <ColumnDefinition Width="Auto"></ColumnDefinition> </Grid.ColumnDefinitions> <ListBox Name="lstAddIns" Margin="3" SelectionChanged="lstAddIns_SelectionChanged"> <ListBox.ItemTemplate> <DataTemplate> <StackPanel Margin="3,3,0,8" HorizontalAlignment="Stretch"> <TextBlock Text="{Binding Path=Name}" FontWeight="Bold" ></TextBlock> <TextBlock Text="{Binding Path=Publisher}" ></TextBlock> <TextBlock Text="{Binding Path=Description}" FontSize="10" FontStyle="Italic"></TextBlock> </StackPanel> </DataTemplate> </ListBox.ItemTemplate> </ListBox> <Button Grid.Column="1" Name="cmdProcessImage" Click="cmdProcessImage_Click" Margin="0,3,3,3" Padding="3" VerticalAlignment="Top" IsEnabled="False">Go</Button> <Image Grid.Row="1" Grid.ColumnSpan="2" Name="img" Source="Forest.jpg" Margin="3" /> </Grid> </Window>
ps:当调用AddInToken.Actiovate<T>方法时,在后台须要执行较多步骤:
(1)为插件建立新的应用程序域。
(2)插件程序集被加载到新的应用程序域。
(3)在新的应用程序域中实例化插件适配器。
(4)(经过远程代理)使得宿主额应用程序域中科院得到插件适配器。
(5)在苏州应用程序域中实例化宿主适配器。
(6)将宿主适配器返回到宿主应用程序(做为宿主视图类型)
另外:可以使用相同的插件建立任意数量的不一样插件。该例子有两个插件,以不一样方式处理图片。
以上《WPF编程宝典》示例。