WPF:插件模型

代码拓虹客
• 阅读 4369

概念:

插件(add-in、plug-in)是应用程序能够动态发现、加载和使用的单独编译过的组件。

  • 优点:

允许第三方开发人员扩展应用程序的功能。如PS中的插件提供大量图片处理效果; firefox中插件提供了增强Web冲浪及全新功能。插件模型的主要优点是不需要为许多任务(如发现)编写底层代码,主要缺点是非常复杂。

内容:

MAF:托管插件框架下的插件模型。较可靠的框架,适用于应用程序和插件不同团队各自开发,还特别适于第三方插件;
MAF依赖于定义的接口,在处理不同版本、允许将插件加载到独立应用程序域中,有很大灵活优势。缺陷是为支持这些功能,MAF显得复杂,设置繁琐。
MEF:托管可扩展性框架的新模型。轻量级选择,适用于单个开发团队,用于以不同方式组装模块化程序,为单独的发布提供不同的功能实现。
缺陷是太松散,相互关联的部件一复杂就容易变得混乱。详情查看http://tinyurl.com/37s2jdx
如对可组合的应用程序有兴趣,可查看复合应用程序库CAL(CAL只针对WPF应用程序)。而MEF是用于构建各种模块化.NET应用程序的通用解决方案。以下是MAF内容示例。

1 了解插件管道


WPF:插件模型

2 管道的工作原理

WPF:插件模型

WPF:插件模型

3 插件文件夹结构

ps:a,当进行编译时,output目录通常放置应用程序和所有管道组件的地方。
b,修改好每个组件项目的生成路径,及防止复制引用的程序集,设置copy local为false。
WPF:插件模型

4 分析使用过程:

首先,宿主应用程序调用宿主视图中的方法。背后体现为应用程序通过宿主视图调用宿主方适配器中的方法,
然后宿主方适配器调用协定接口的相应方法,该方法是由插件方适配器实现的。
最后,插件方适配器调用插件视图中的方法。这个方法是由插件实现的,负责执行实际工作。

5 程序代码及介绍

效果图

WPF:插件模型
WPF:插件模型

  • 协定:
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编程宝典》示例。

点赞
收藏
评论区
推荐文章
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
Prodan Labs Prodan Labs
4年前
Kubernetes Ingress — Kong
Kong是由Mashape公司开源的一个高性能、高可用、易扩展的APIGateway项目,基于OpenResty(NginxLua模块),并提供了插件实现API的AOP功能。可以通过负载均衡、插件扩展等方式,来处理网络请求。Kong主要的概念Servi
东方客主 东方客主
4年前
Android动态加载基础 ClassLoader工作机制
类加载器ClassLoader早期使用过Eclipse等Java编写的软件的同学可能比较熟悉,Eclipse可以加载许多第三方的插件(或者叫扩展),这就是动态加载。这些插件大多是一些Jar包,而使用插件其实就是动态加载Jar包里的Class进行工作。这其实非常好理解,Java代码都是写在Class里面的,程序运行在虚拟机上
Easter79 Easter79
4年前
Taro 2.2 全面插件化,支持拓展和定制个性化功能
!(https://oscimg.oschina.net/oscnet/up889c8772b4d4c4a678d00fc4ead5c097c76.png)自2.2开始,Taro引入了插件化机制,允许开发者通过编写插件的方式来为Taro拓展更多功能或者为自身业务定制个性化功能,欢迎大家进行尝试,共同讨论~当前版本2.2.1官
Stella981 Stella981
4年前
Eclipse插件开发_学习_00_资源帖
一、官方资料 1.eclipseapi(https://www.oschina.net/action/GoToLink?urlhttp%3A%2F%2Fhelp.eclipse.org%2Fmars%2Findex.jsp%3Ftopic%3D%252Forg.eclipse.platform.doc.isv%252Fguide%2
子桓 子桓
2年前
AE 3D粒子系统插件 Trapcode Particular激活最新版
TrapcodeParticular是一款由RedGiant开发的强大的粒子效果插件,常用于AdobeAfterEffects中。该插件提供了广泛的粒子生成和控制工具,使用户能够轻松创建出令人惊叹的视觉效果和动态图形。以下是TrapcodeParticul
公孙晃 公孙晃
2年前
Video Copilot Element 3D Mac破解版(E3D三维模型AE插件)支持ae2022
是一款强大的三维模型和特效插件,专为影视制作和动画设计而开发。它由知名的视效专家AndrewKramer和他的团队在VideoCopilot公司推出。该插件在AdobeAfterEffects中作为插件使用,它具有强大的实时渲染功能,可以在制作3D效果过程
流浪剑客 流浪剑客
2年前
Macos 红巨星粒子插件:Red Giant Trapcode Suite Mac破解版
RedGiantTrapcodeSuiteMac是一款专业的视觉特效插件集合,适用于AdobeAfterEffects和PremierePro等视频软件。这款插件集能够提供丰富的视觉特效,如火花、烟雾、爆炸、飞溅等,以及3D粒子效果。它功能强大,允许用户创
程序员小五 程序员小五
1年前
融云IM干货丨uni-app的插件生态系统具体有哪些功能?
UNIapp的插件生态系统提供了丰富的功能,具体包括以下几个方面:1.基础功能插件:这些插件提供基本的功能,如网络请求、本地存储、事件处理等,对于大多数UniApp应用都是必需的。2.UI组件插件:提供各种用户界面组件,例如按钮、列表、表单、弹窗等,帮助开
程序员小五 程序员小五
1年前
融云IM干货丨如何确保插件与UNI-app的兼容性?
确保插件与UNIapp的兼容性,可以采取以下几个步骤:1.使用官方插件市场:尽量在寻找插件,因为官方市场提供的插件会有UNIapp兼容性描述,而第三方市场如npm的插件可能没有兼容性描述,容易下载到无法跨平台的、仅适配web的插件。2.检查平台兼容性:在插
公孙晃 公孙晃
2年前
AE Saber插件 for Mac 1.0.39 汉化版
AESaber插件是一款可以帮助用户在AfterEffects中创建高质量光剑和能量效果的插件。该插件集成在AfterEffects中,可以直接在软件中使用,提供了丰富的工具和功能,方便用户轻松地创作出各种精美的光剑和能量效果。