插件(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編程寶典》示例。