構建可擴展的應用程序,特別是對於WinForm應用程序是特別有好處的。我們知道,企業的需求是瞬息萬變的,企業在使用軟件的過程中,很可能對於現有的需求有變動甚至是提出新的需求來,可是我們的軟件已經部署在企業的各個客戶端中,要想將根據企業新的需求編寫的模塊集成到現有程序中去,我們必須重新編譯整個軟件,然后打包再進行重新部署,這無疑有非常大的工作量。怎樣才能將新編寫的模塊集成到現有程序中去,而又不用重新編譯整個應用程序?這就是我們接下來要討論的話題。
利用C# 構建可擴展的應用程序,就是利用.Net的反射機制以及特性編程實現,原理我就不介紹了,很多C#的書籍都說的很清楚,接下來,我將演示如何搭建可擴展的應用程序。
一、首先建立一個類庫IplugTypes
該類庫定義了插件程序必須遵循的接口Iplug和一個自定義特性,在該類庫中導入System.Windows.Forms的引用,代碼如下:
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自定義屬性類描述該窗體類,代碼如下:
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的命名空間,該窗體類的代碼如下:
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