WPF:插件模型

概念:

插件(add-in、plug-in)是應用程序可以動態發現、加載和使用的單獨編譯過的組件。編程

  • 優勢:

容許第三方開發人員擴展應用程序的功能。如PS中的插件提供大量圖片處理效果; firefox中插件提供了加強Web衝浪及全新功能。插件模型的主要優勢是不須要爲許多任務(如發現)編寫底層代碼,主要缺點是很是複雜。架構

內容:

MAF:託管插件框架下的插件模型。較可靠的框架,適用於應用程序和插件不一樣團隊各自開發,還特別適於第三方插件;
MAF依賴於定義的接口,在處理不一樣版本、容許將插件加載到獨立應用程序域中,有很大靈活優點。缺陷是爲支持這些功能,MAF顯得複雜,設置繁瑣。
MEF:託管可擴展性框架的新模型。輕量級選擇,適用於單個開發團隊,用於以不一樣方式組裝模塊化程序,爲單獨的發佈提供不一樣的功能實現。
缺陷是太鬆散,相互關聯的部件一複雜就容易變得混亂。詳情查看http://tinyurl.com/37s2jdx
如對可組合的應用程序有興趣,可查看複合應用程序庫CAL(CAL只針對WPF應用程序)。而MEF是用於構建各類模塊化.NET應用程序的通用解決方案。如下是MAF內容示例。框架

1 瞭解插件管道


clipboard.png

2 管道的工做原理

clipboard.png

clipboard.png

3 插件文件夾結構

ps:a,當進行編譯時,output目錄一般放置應用程序和全部管道組件的地方。
b,修改好每一個組件項目的生成路徑,及防止複製引用的程序集,設置copy local爲false。
clipboard.pngdom

4 分析使用過程:

首先,宿主應用程序調用宿主視圖中的方法。背後體現爲應用程序經過宿主視圖調用宿主方適配器中的方法,
而後宿主方適配器調用協定接口的相應方法,該方法是由插件方適配器實現的。
最後,插件方適配器調用插件視圖中的方法。這個方法是由插件實現的,負責執行實際工做。ide

5 程序代碼及介紹

效果圖模塊化

clipboard.png
clipboard.png

  • 協定:
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編程寶典》示例。

相關文章
相關標籤/搜索