概念:
插件(add-in、plug-in)是应用程序能够动态发现、加载和使用的单独编译过的组件。
- 优点:
允许第三方开发人员扩展应用程序的功能。如PS中的插件提供大量图片处理效果; firefox中插件提供了增强Web冲浪及全新功能。插件模型的主要优点是不需要为许多任务(如发现)编写底层代码,主要缺点是非常复杂。
内容:
MAF:托管插件框架下的插件模型。较可靠的框架,适用于应用程序和插件不同团队各自开发,还特别适于第三方插件;
MAF依赖于定义的接口,在处理不同版本、允许将插件加载到独立应用程序域中,有很大灵活优势。缺陷是为支持这些功能,MAF显得复杂,设置繁琐。
MEF:托管可扩展性框架的新模型。轻量级选择,适用于单个开发团队,用于以不同方式组装模块化程序,为单独的发布提供不同的功能实现。
缺陷是太松散,相互关联的部件一复杂就容易变得混乱。详情查看http://tinyurl.com/37s2jdx。
如对可组合的应用程序有兴趣,可查看复合应用程序库CAL(CAL只针对WPF应用程序)。而MEF是用于构建各种模块化.NET应用程序的通用解决方案。以下是MAF内容示例。
1 了解插件管道
2 管道的工作原理
3 插件文件夹结构
ps:a,当进行编译时,output目录通常放置应用程序和所有管道组件的地方。
b,修改好每个组件项目的生成路径,及防止复制引用的程序集,设置copy local为false。
4 分析使用过程:
首先,宿主应用程序调用宿主视图中的方法。背后体现为应用程序通过宿主视图调用宿主方适配器中的方法,
然后宿主方适配器调用协定接口的相应方法,该方法是由插件方适配器实现的。
最后,插件方适配器调用插件视图中的方法。这个方法是由插件实现的,负责执行实际工作。
5 程序代码及介绍
效果图
- 协定:
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:插件适配器必须提供接收恰当视图类的实例作为参数的构造函数,以备后用。
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,宿主适配器
接收一个实现了协定的对象,然后调用宿主方适配器的方法使用该对象。然后后台调用协定接口方法,向前穿过应用程序边界,并转换为调用插件适配器的相应方法。
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宿主。
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编程宝典》示例。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。