前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >WPF使用MEF插件开发打造自己的工具箱

WPF使用MEF插件开发打造自己的工具箱

作者头像
zls365
发布2021-04-02 01:41:05
1.8K0
发布2021-04-02 01:41:05
举报
文章被收录于专栏:CSharp编程大全

最近闲来无事,想着搞点事情来着,想搞一个工具箱程序,又想着自由灵活点,于是就选用了MEF.这玩意适用于小的项目,可以方便的做出一个插件开发系统.

顺道又把之前做的截图工具也加入进来了,基础框架弄好之后,再开发别的插件,就是依样画葫芦喽,

现在还没做什么,只是开发了几个插件嘿嘿

先来看看效果吧.

刚开始做,还没有做几个插件.后续几个文章中,我会把项目源码带出来,大家学会了之后,可以一起来开发,打造一个牛逼哄哄的插件系统喽.

后续打算把它做成从服务器上检索插件的形式,找到想要的插件,就点击下载直接使用.嘿嘿.一步一步来吧.

下面我先介绍一下MEF

  • MEF简介

MEF可以方便的在c#程序中实现插件式开发。通过接口暴露公开方法,插件内继承接口的类可以通过[export]特性公开出去,宿主程序通过[import]特性建立接口类型的属性,启用插件方法.插件可以和主程序不在一个程序集,实现插件完全独立的开发.

  • 使用MEF主要由4个步骤完成

1、定义插件插口

2、公开接口的实现类.(使用[export]特性)

3、通过[import]特性调用插件

4、加载到主程序中

话不多说,直接上代码,懂的人自然懂.不懂的建议先学一下c#基础哦

1.新建DLL工程.创建一个IPlugin 接口.用于对外公开

项目目录如下:

定义插件接口,实现插件必要的属性.代码如下:

代码语言:javascript
复制
namespace Plugin.Base
{
    public interface IPlugin
    {
        //主程序调用此方法,启动插件
        void Startup(object args = null);
        //主程序调用此方法,退出插件
        void Close(object args = null);
        //插件名,用于主程序中获取并显示
        string PluginName { get; }
        //插件唯一Key,用于区分插件,(后期做下载更新插件的时候会用到,现在没啥用)
        Guid PluginKey { get; }
        //插件的图标,用于主程序中获取并显示
        ImageSource PluginIcon { get; }
        //插件的启动Command,用于Wpf程序中绑定使用
        System.Windows.Input.ICommand StartupCommand { get; }
        //插件的关闭Command,用于Wpf程序中绑定使用
        System.Windows.Input.ICommand CloseCommand { get; }
        //插件的描述,用于主程序中获取并显示
        string PluginDescription { get; }
        //忘记了为啥,当时写的时候,加上了这个属性,现在想不起来了,算了,先放这吧,不用管它
        bool LoadSucceed { get; set; }
        //是否支持自启动.想着后期工具箱要做开机自启的.工具箱启动后,又能自动启动这些设置了自启的插件
        bool SupportAutoStartup { get; set; }
        //插件被加载手,主程序调用此方法,初始化插件
        void Initialize();
    }
}

2.主程序创建加载插件的PluginLoader类

本节主要关注这个PlginLoader.cs.其它文件请自动忽略

代码如下,代码解释,我都写在注释里了.各位请看:

代码语言:javascript
复制
using Plugin.Base;
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.Linq;
using ViewModel.Base;
namespace Tools
{
    public class PluginLoader
    {
      //些特性表示加载实现了Iplugin接口的类
        [ImportMany(typeof(IPlugin))]
        private IEnumerable<IPlugin> Plugins { get; set; }
        public PluginLoader()
        {
            //这个下载插件的的功能,还没有做好,先把事件写了,放在这,先不用管,后面会再讲解
            Global.OnPluginDownLoadSucceed += OnPluginDownLoadSucceed;
        }
        //插件下载成功事件,下载后加载插件,按照插件的PluginKey加载(不懂的看上面的Iplugin接口)
        private void OnPluginDownLoadSucceed(string guid)
        {
            try
            {
                AggregateCatalog catalog = new AggregateCatalog();
                //设置插件目录为程序目录,插件名称为Tools.Plugin.{guid}.dll
                //这是我们的定义的规定,后面开发的插件,dll名字都要是这样的
                //没有规矩不成方圆
                catalog.Catalogs.Add(new DirectoryCatalog(".", $"Tools.Plugin.{guid}.dll"));
                //加载插件
                using (CompositionContainer container = new CompositionContainer(catalog))
                {
                    container.ComposeParts(this);
                    foreach (var plugin in Plugins)
                    {
                        //初始化插件
                        plugin.Initialize();
                        //存到主程序中,后面调用使用
                        Global.Plugins.Add(plugin);
                    }
                }
                //我们添加的更多插件,这是一个默认插件,想着搞个官网装下B呢嘿嘿
                var more = Global.Plugins.FirstOrDefault(r => r.PluginKey == Global.MorePluginKey);
                if (more != null)
                {
                    Global.Plugins.Remove(more);
                    Global.Plugins.Add(more);
                }
            }
            catch (Exception ex)
            {
                Log.File(ex, "加载插件异常");
            }
        }
        //主程序启动后,调用此方法,可加载目录中所有插件
        public void Load()
        {
            try
            {
                AggregateCatalog catalog = new AggregateCatalog();
                //设置插件目录为程序目录,插件名称为Tools.Plugin.*.dll
                catalog.Catalogs.Add(new DirectoryCatalog(".", "Tools.Plugin.*"));
                using (CompositionContainer container = new CompositionContainer(catalog))
                {
                    container.ComposeParts(this);
                    foreach (var plugin in Plugins)
                    {
                        plugin.Initialize();
                        Global.Plugins.Add(plugin);
                    }
                }
                var more = Global.Plugins.FirstOrDefault(r => r.PluginKey == Global.MorePluginKey);
                if (more != null)
                {
                    Global.Plugins.Remove(more);
                    Global.Plugins.Add(more);
                }
            }
            catch (Exception ex)
            {
                Log.File(ex, "加载插件异常");
            }
        }
    }
}

3.主程序有了,接口有了.那么,来搞个插件试试吧

新建Dll项目,命名为:Tools.Plugin.More (和我们上面 写的加载的插件格式对应)

再写个Plugin类,实现IPlugin接口

目录如下 :

主要看Plugin.cs和引用的Plugin.Base项目.其它请自动忽略

Plugin.cs代码如下:

代码语言:javascript
复制
using Plugin.Base;
using System;
using System.ComponentModel.Composition;
using System.Windows.Input;
using System.Windows.Media;
using ViewModel.Base;

namespace Tools.Plugin.More
{
    //务必使用此特性,向外公开IPlugin类型
    //实现IPlugin
    [Export(typeof(IPlugin))]
    public class Plugin : IPlugin
    {
        public string PluginName => "更多精彩";

        public Guid PluginKey => new Guid("be8ff5b2-733e-4b76-a9c2-f7ec57ee6d18");
        //这里提供一个图片,作为主程序显示插件的图标用,这个还要转一下类型,唉,当时设计失误,算了,不想改了
        public ImageSource PluginIcon => ImageHelper.BitmapToBitmapImage(Properties.Resources.More64);

        public string PluginDescription => "打开官网查看更多";
        public ICommand StartupCommand { get; private set; }
        public ICommand CloseCommand { get; private set; }
        public bool LoadSucceed
        {
            get => true;
            set
            {
                throw new NotSupportedException($"本插件不支持修改此属性:{nameof(LoadSucceed)}");
            }
        }

        public bool SupportAutoStartup { get => false; set { } }
        public void Initialize()
        {
            StartupCommand = new RelayParameterizedCommand((args) => Startup(args));
            CloseCommand = new RelayParameterizedCommand((args) => Close(args));
        }
       
        public void Startup(object args = null)
        {
            System.Diagnostics.Process.Start("https://www.gggcloud.top");
        }
        public void Close(object args = null)
        {
        }
    }
}

好了,把这个插件项目,和主程序编译到一个目录下,启动主程序时,调用PluginLoader.Load方法,就可以加载这个插件了.至于加载后怎么处理,等下一篇文章再说吧,这个文章字太多啦,打字打的累手...

好吧,就先写到这了,下期再会

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-03-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 CSharp编程大全 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档