C#构建可扩展的应用程序(插件)
构建可扩展的应用程序,特别是对于WinForm应用程序是特别有好处的。我们知道,企业的需求是瞬息万变的,企业在使用软件的过程中,很可能对于现有的需求有变动甚至是提出新的需求来,可是我们的软件已经部署在企业的各个客户端中,要想将根据企业新的需求编写的模块集成到现有程序中去,我们必须重新编译整个软件,然后打包再进行重新部署,这无疑有非常大的工作量。怎样才能将新编写的模块集成到现有程序中去,而又不用重新编译整个应用程序?这就是我们接下来要讨论的话题。
利用C# 构建可扩展的应用程序,就是利用.Net的反射机制以及特性编程实现,原理我就不介绍了,很多C#的书籍都说的很清楚,接下来,我将演示如何搭建可扩展的应用程序。
一、首先建立一个类库IplugTypes
该类库定义了插件程序必须遵循的接口Iplug和一个自定义特性,在该类库中导入System.Windows.Forms的引用,代码如下:
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Windows.Forms;
6
7 namespace IplugTypes
8 {
9 /// <summary>
10 /// 插件必须继承该接口规范
11 /// </summary>
12 public interface Iplug
13 {
14 void FormShow(Form mainForm);
15 }
16 [AttributeUsage(AttributeTargets.Class)]
17 public sealed class AssemblyInfoAndCompanyInfoAttribute : System.Attribute
18 {
19 /// <summary>
20 /// 程序集名称(不包括扩展名)
21 /// </summary>
22 public string AssemblyName { getset; }
23 /// <summary>
24 /// 程序集版本
25 /// </summary>
26 public string AssemblyVersion { getset; }
27 /// <summary>
28 /// 公司名称
29 /// </summary>
30 public string CompanyName { getset; }
31 /// <summary>
32 /// 菜单名(在承载的主程序中要显示的名称)
33 /// </summary>
34 public string MenuName { getset; }
35 public AssemblyInfoAndCompanyInfoAttribute()
36 {
37
38 }
39
40 }
41 }
二、建立WinForm插件程序
建立一个WinForm窗体程序,引入上面建立的IplugTypes类库的引用,如下图:
在该窗体类中继承IPlug接口,在Iplug接口的FormShow方法中实例化本窗体,同时用AssemblyInfoAndCompanyInfo自定义属性类描述该窗体类,代码如下:
1 using System.Drawing;
2 using System.Linq;
3 using System.Text;
4 using System.Windows.Forms;
5 using IplugTypes;
6
7 namespace WinFormIPlug
8 {
9 [AssemblyInfoAndCompanyInfo(AssemblyName = "WinFormIPlug",AssemblyVersion="1.0.0.0",CompanyName="DTXY",MenuName="C#插件")]
10 public partial class Form1 : Form,Iplug
11 {
12 public Form1()
13 {
14 InitializeComponent();
15 }
16
17 private void button1_Click(object sender, EventArgs e)
18 {
19 MessageBox.Show("这是一个插件程序!");
20 }
21
22 void Iplug.FormShow(Form mainForm)
23 {
24 Form1 fm = new Form1();
25 fm.MdiParent = mainForm;
26 fm.Show();
27 // throw new NotImplementedException();
28 }
29 }
30 }
将该窗体的输出类型设置为类库,然后进行编译,如下图
三、构建可扩展的应用程序
建立一个名称为WindowsFormMain的窗体应用程序,添加menuStrip控件,并设置该窗体的IsMdiContainer属性为True,指示该窗体为MDI对文档父窗体。如下图:
引入对程序集IplugTypes的引用,导入System.Reflection的命名空间。然后,添加FrmAdd窗体,用于导入插件程序,如下图:
同样在FrmAdd窗体类中引入对程序集IplugTypes的引用,导入System.Reflection的命名空间,该窗体类的代码如下:
1 using System;
2 using System.Collections.Generic;
3 using System.ComponentModel;
4 using System.Data;
5 using System.Drawing;
6 using System.Linq;
7 using System.Text;
8 using System.Windows.Forms;
9 using IplugTypes;
10 using System.Reflection;
11 using System.IO;
12
13 namespace WindowsFormMain
14 {
15 public partial class FrmAdd : Form
16 {
17 public FrmAdd()
18 {
19 InitializeComponent();
20 }
21 /// <summary>
22 /// 插件加载路径
23 /// </summary>
24 public string Path { getprivate set; }
25
26 private void btnBrowse_Click(object sender, EventArgs e)
27 {
28
29 if (this.openFileDialog1.ShowDialog() == DialogResult.OK)
30 {
31 if (this.openFileDialog1.SafeFileName.Split('.')[1].ToUpper() != "DLL")
32 {
33 MessageBox.Show("您选择的不是动态类型库,扩展名为.DLL!");
34 return;
35 }
36 string strFilePath = this.openFileDialog1.FileName;
37 Path = strFilePath;
38 this.textBox1.Text = Path;
39 if (!LoadAssembly(strFilePath))
40 {
41 MessageBox.Show("插件加载失败,请确认插件是否支持Iplug接口!");
42 }
43 }
44 }
45 private bool LoadAssembly(string strFilePath)
46 {
47 bool isRight = false;
48 try
49 {
50 Assembly asm = Assembly.LoadFrom(strFilePath);
51 var types = from t in asm.GetTypes()
52 where t.IsClass && t.GetInterface("Iplug"!= null
53 select t;
54 if (types.Count() <= 0)
55 {
56 return isRight;
57 }
58 foreach (Type item in types)
59 {
60 DisplayAssemblyInfo(item);
61 }
62 isRight = true;
63 }
64 catch (Exception ex)
65 {
66 MessageBox.Show(ex.Message);
67 return isRight;
68 }
69 return isRight;
70 }
71 private void DisplayAssemblyInfo(Type t)
72 {
73 var asmInfo = from n in t.GetCustomAttributes(false)
74 where n.GetType() == typeof(AssemblyInfoAndCompanyInfoAttribute)
75 select n;
76 foreach (AssemblyInfoAndCompanyInfoAttribute item in asmInfo)
77 {
78 string[] strItems = { t.Assembly.GetName().Name, item.AssemblyVersion, item.CompanyName };
79 ListViewItem lv = new ListViewItem(strItems);
80 this.listView1.Items.Add(lv);
81 }
82 }
83
84 private void btnSub_Click(object sender, EventArgs e)
85 {
86 if (string.IsNullOrEmpty(Path))
87 {
88 MessageBox.Show("没有任何可导入的插件程序!");
89 return;
90 }
91 FileInfo fi = new FileInfo(Path);
92 fi.CopyTo(Application.StartupPath + "\\" + fi.Name, true);
93 DialogResult = DialogResult.OK;
94 }
95
96 private void btnCancel_Click(object sender, EventArgs e)
97 {
98 DialogResult = DialogResult.Cancel;
99 }
100 }
101 }
在主窗体Form1中的MenuScript控件的File->Add-in..菜单中实例话FrmAdd窗体并以模式窗体的方式显示出来,代码如下:
1 using System;
2 using System.Collections.Generic;
3 using System.ComponentModel;
4 using System.Data;
5 using System.Drawing;
6 using System.Linq;
7 using System.Text;
8 using System.Windows.Forms;
9 using IplugTypes;
10 using System.Reflection;
11
12 namespace WindowsFormMain
13 {
14 public partial class Form1 : Form
15 {
16 public Form1()
17 {
18 InitializeComponent();
19 }
20
21 private void Form1_Load(object sender, EventArgs e)
22 {
23
24 }
25
26 private void addInToolStripMenuItem_Click(object sender, EventArgs e)
27 {
28 FrmAdd fm = new FrmAdd();
29 fm.ShowDialog();
30 }
31 }
32 }
现在,插件已导入到应用程序的启动目录下,那怎样在主窗体的MenuScript菜单中创建一个插件窗体的菜单并且单击该菜单会显示出插件窗体来? 我们接下来继续讨论:
我的设计思路是:维护一个XML文件,该XML文件用于记录主窗体MenuScript的菜单项,主窗体在启动时读取该XML文件,然后动态创建菜单。
当然为了演示,我这里只是手动创建一个名叫:Iplug.xml的文件,并且保存在应用程序的启动目录下。读者完全可以在导入插件时,用程序自动创建。该XML文件的格式如下:
1 <?xml version="1.0" encoding="gb2312"?>
2 <root>
3 <menu name="C#插件" assemblyName="WinFormIPlug">
4 </menu>
5 </root>
当然如果有多个插件,可建立多个<menu>节点,这里<menu>节点的 name属性是该插件要在主程序中显示的菜单名,assemblyName属性是插件程序集的名称(不包括.dll的扩展名)。
建立好该XML文件后,保存到主程序的启动目录下,然后在主窗体启动时读取该XML文件,动态的创建菜单。主窗体的代码如下:
1 using System;
2 using System.Collections.Generic;
3 using System.ComponentModel;
4 using System.Data;
5 using System.Drawing;
6 using System.Linq;
7 using System.Text;
8 using System.Windows.Forms;
9 using IplugTypes;
10 using System.Reflection;
11 using System.Xml;
12 using System.Xml.Linq;
13 using System.IO;
14
15 namespace WindowsFormMain
16 {
17 public partial class Form1 : Form
18 {
19 public Form1()
20 {
21 InitializeComponent();
22 }
23
24 private void Form1_Load(object sender, EventArgs e)
25 {
26 try
27 {
28 this.AddMenus();
29 }
30 catch (Exception ex)
31 {
32 MessageBox.Show(ex.Message, "提示", MessageBoxButtons.OK);
33 }
34 }
35 /// <summary>
36 /// 加载程序菜单
37 /// </summary>
38 private void AddMenus()
39 {
40 string strFilePath = Application.StartupPath + "\\" + "Iplug.xml";
41 if (!File.Exists(strFilePath))
42 {
43 XmlDocument dom = new XmlDocument();
44 XmlDeclaration declar = dom.CreateXmlDeclaration("1.0""utf-8"null);
45 dom.AppendChild(declar);
46 XmlElement root = dom.CreateElement("root");
47 dom.AppendChild(root);
48 dom.Save(strFilePath);
49 }
50 else
51 {
52 XDocument xd = XDocument.Load(strFilePath);
53
54 var menus = from n in xd.Descendants("menu")
55 select n;
56 if (menus.Count() <= 0)
57 {
58 return;
59 }
60 foreach (var item in menus)
61 {
62 string menuName = item.Attribute("name").Value;
63 ToolStripMenuItem ts = (ToolStripMenuItem)this.menuStrip1.Items.Add(menuName);
64 ts.Tag = item.Attribute("assemblyName").Value;
65 ts.Click += new EventHandler(ts_Click);
66 }
67 }
68 }
69 void ts_Click(object sender, EventArgs e)
70 {
71 try
72 {
73 ToolStripMenuItem tool = (ToolStripMenuItem)sender;
74 string assemblyName = tool.Tag.ToString();
75 Assembly asm = Assembly.Load(assemblyName);
76 var types = from n in asm.GetTypes()
77 where n.IsClass && n.GetInterface("Iplug"!= null
78 select n;
79 if (types.Count() <= 0)
80 {
81 return;
82 }
83 foreach (Type t in types)
84 {
85 Iplug plug = (Iplug)Activator.CreateInstance(t);
86 plug.FormShow(this);
87 }
88 }
89 catch (Exception ex)
90 {
91 MessageBox.Show(ex.Message);
92 }
93 //throw new NotImplementedException();
94 }
95
96 private void addInToolStripMenuItem_Click(object sender, EventArgs e)
97 {
98 FrmAdd fm = new FrmAdd();
99 fm.ShowDialog();
100 }
101 }
102 }
C#构建可扩展的应用程序(插件)的更多相关文章
- 构建可扩展的GPU加速应用程序(NVIDIA HPC)
构建可扩展的GPU加速应用程序(NVIDIA HPC) 研究人员.科学家和开发人员正在通过加速NVIDIA GPU上的高性能计算(HPC)应用来推进科学发展,NVIDIA GPU具有处理当今最具挑战性 ...
- Gradle 1.12用户指南翻译——第四十五章. 应用程序插件
本文由CSDN博客貌似掉线翻译,其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Githu ...
- 【原创】Odoo开发文档学习之:构建接口扩展(Building Interface Extensions)(边Google翻译边学习)
构建接口扩展(Building Interface Extensions) 本指南是关于为Odoo的web客户创建模块. 要创建有Odoo的网站,请参见建立网站;要添加业务功能或扩展Odoo的现有业务 ...
- [Qt插件]-02创建应用程序插件(插件化开发的一种思路)
本篇是学习Qt Creator快速入门,插件开发的笔记 分为两部分 创建插件 使用插件的应用程序(测试插件) 插件是被使用的应用程序加载使用的. 是使用插件的应用程序定义接口,插件按照接口来实 ...
- NVIDIA DeepStream 5.0构建智能视频分析应用程序
NVIDIA DeepStream 5.0构建智能视频分析应用程序 无论是要平衡产品分配和优化流量的仓库,工厂流水线检查还是医院管理,要确保员工和护理人员在照顾病人的同时使用个人保护设备(PPE),就 ...
- Permit.js – 用于构建多状态原型的 jQuery 插件
Permit.js 是一个 jQuery 插件,用于构建交互的,多态的网站原型和应用程序原型.也许你的网站有的功能仅适用于登录的成员,只有管理员才能使用或者你的应用程序会根据线上或离线有不同的功能,这 ...
- 面向 Java 开发人员的 Ajax: 构建动态的 Java 应用程序
面向 Java 开发人员的 Ajax: 构建动态的 Java 应用程序 Ajax 为更好的 Web 应用程序铺平了道路 在 Web 应用程序开发中,页面重载循环是最大的一个使用障碍,对于 Java™ ...
- Building Modern Web Apps-构建现代的 Web 应用程序
Building Modern Web Apps-构建现代的 Web 应用程序 视频长度:1 小时左右 视频作者:Scott Hunter 和 Scott Hanselman 视频背景:Visual ...
- 如何使用TDD和React Testing Library构建健壮的React应用程序
如何使用TDD和React Testing Library构建健壮的React应用程序 当我开始学习React时,我努力的一件事就是以一种既有用又直观的方式来测试我的web应用程序. 每次我想测试它时 ...
随机推荐
- Django:表多对多查询、聚合分组、FQ查询、事务
1表多对多的关系查询 准备工作创建表结构 from django.db import models # Create your models here. class Publisher(models. ...
- RTP包的结构
live555中数据的发送最后是要使用RTP协议发送的,下面介绍一下RTP包格式. RTP packet RTP是基于UDP协议的,RTP服务器会通过UDP协议,通常每次会发送一个RTP packet ...
- 深入理解jvm--性能监控工具
1.jvm监控工具介绍 1.1.jconsole JConsole是一个基于JMX的GUI工具,用于连接正在运行的JVM,不过此JVM需要使用可管理的模式启动. 1.2.启动jconsole 通过JD ...
- JavaScript笔记02_对象
目录 1. 函数 1. 函数创建 2. 函数的参数 2. return.break.continue 3. 立即执行函数 4. 对象 5. 枚举对象中的属性 6. 声明提前 1.变量的声明提前 2. ...
- python-pyhon与模块安装
python 安装Python,配置环境变量,路径为python安装路径,如D:\pythoncmd中输入python可以识别则安装成功 pip升级指令python -m pip install -- ...
- Non-boring sequences(启发式分治)
题意:一个序列被称作是不无聊的,当且仅当,任意一个连续子区间,存在一个数字只出现了一次,问给定序列是否是不无聊的. 思路:每次找到一个只出现了一次的点,其位置的pos,那么继续分治[L,pos-1], ...
- SQL操作Spark SQL--CatalogApiTest
object CatalogApiTest { def main(args: Array[String]): Unit = { val spark = SparkSession .builder() ...
- web自动化测试-自动化测试模型介绍
一.线性测试 什么是线性测试? 通过录制或编写对应用程序的操作步骤产生相应的线性脚本,每个测试脚本相对独立,不产生依赖和调用,单纯的来模拟用户完整的操作场景 缺点 1.开发成本高,测试用例之间存在重复 ...
- 基于思岚A1激光雷达+OpenGL+VS2017的Ramer-Douglas-Peucker算法的实现
时隔两年 又借到了之前的那个激光雷达,最老版本的思岚A1,甚至不支持新的固件,并且转接板也不见了,看了下淘宝店卖¥80,但是官方提供了一个基于STM32的实现方式,于是我估摸着这个转接板只是一个普通的 ...
- 手写队列以及stl中队列的使用
一,手写队列. struct queue { ; ,rear=,a[maxn]; void push(int x) { a[++rear]=x; } void pop() { first++; } i ...