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

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

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

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

    先来看看效果吧.

955fcbc1a7efdcd9d181d4cb4930f7c2.png

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

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

下面我先介绍一下MEF

  • MEF简介

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

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

1、定义插件插口

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

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

4、加载到主程序中

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

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

项目目录如下:

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

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类

4b01147941b79294e9473e13a4a42ff2.png

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

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

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接口

目录如下 :

4c21c400e0eb99d104c4f9ce5090126d.png

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

Plugin.cs代码如下:

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方法,就可以加载这个插件了.至于加载后怎么处理,等下一篇文章再说吧,这个文章字太多啦,打字打的累手...

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

    手机上看代码不方便,建议使用电脑版微信,用浏览器打开看哦

获取更多知识,公众号:dotNET编程大全,扫码关注!

542ae5f036704ead7f03a7c8eff87058.png

需加微信交流群的,请加小编微信号z438679770,切记备注 加群,小编将会第一时间邀请你进群!

 每日分享不易,"在看"就是鼓励!

c0fb8d3509d13e5f8b38479a279c603d.gif

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
当谈到 WPF(Windows Presentation Foundation)工具箱时,它是一个包含了一组可重用的控件、组件和工具的集合,用于在 WPF 应用程序中构建用户界面。这些工具箱提供了一种简化开发过程的方式,使开发人员能够快速创建各种功能丰富的界面。 下面是一些常见的 WPF 工具箱工具及其功能的讲解: 1. Button(按钮):这是一个常用的控件,用于在应用程序中添加交互性。你可以为按钮添加命令、事件和样式,以实现各种点击操作。 2. TextBox(文本框):文本框用于输入和显示文本。你可以设置文本框的样式、验证规则和事件处理程序。 3. ComboBox(下拉列表框):这个控件允许用户从预定义的选项中进行选择。你可以设置选项列表、默认选项和选项更改的事件处理程序。 4. ListBox(列表框):列表框用于显示一组项目,并允许用户进行选择。你可以设置项模板、选择模式和数据绑定。 5. DataGrid(数据表格):这是一个用于显示和编辑数据的高级控件。你可以定义列、绑定数据源和设置排序、筛选等功能。 6. DatePicker(日期选择器):日期选择器允许用户选择日期。你可以设置日期格式、默认日期和日期更改的事件处理程序。 7. Slider(滑块):滑块用于在一个范围内选择一个值。你可以设置最小值、最大值和滑块值更改的事件处理程序。 8. MediaElement(媒体元素):媒体元素用于播放音频和视频文件。你可以设置媒体源、控制播放进度和添加事件处理程序。 这只是 WPF 工具箱中的一小部分工具,还有很多其他的控件和组件可供使用。你可以根据你的项目需求选择适当的工具,并根据需要定制它们的外观和行为。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值