最近闲来无事,想着搞点事情来着,想搞一个工具箱程序,又想着自由灵活点,于是就选用了MEF.这玩意适用于小的项目,可以方便的做出一个插件开发系统.
顺道又把之前做的截图工具也加入进来了,基础框架弄好之后,再开发别的插件,就是依样画葫芦喽,
现在还没做什么,只是开发了几个插件嘿嘿
先来看看效果吧.
刚开始做,还没有做几个插件.后续几个文章中,我会把项目源码带出来,大家学会了之后,可以一起来开发,打造一个牛逼哄哄的插件系统喽.
后续打算把它做成从服务器上检索插件的形式,找到想要的插件,就点击下载直接使用.嘿嘿.一步一步来吧.
下面我先介绍一下MEF
MEF简介
MEF可以方便的在c#程序中实现插件式开发。通过接口暴露公开方法,插件内继承接口的类可以通过[export]特性公开出去,宿主程序通过[import]特性建立接口类型的属性,启用插件方法.插件可以和主程序不在一个程序集,实现插件完全独立的开发.
使用MEF主要由4个步骤完成
1、定义插件插口
2、公开接口的实现类.(使用[export]特性)
3、通过[import]特性调用插件
4、加载到主程序中
话不多说,直接上代码,懂的人自然懂.不懂的建议先学一下c#基础哦
1.新建DLL工程.创建一个IPlugin 接口.用于对外公开
项目目录如下:
定义插件接口,实现插件必要的属性.代码如下:
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.其它文件请自动忽略
代码如下,代码解释,我都写在注释里了.各位请看:
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代码如下:
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编程大全,扫码关注!
需加微信交流群的,请加小编微信号z438679770,切记备注 加群,小编将会第一时间邀请你进群!
每日分享不易,"在看"就是鼓励!