插件式编程之使用反射分离MDI的父窗口和子窗口

插件式编程之使用反射分离MDI的父窗口和子窗口

 

作者:[email protected],发表于:http://www.cnblogs.com/sagahu,2012-10-09

 

现在越来越多的软件,包括许多的管理系统,都在使用“主体+插件”式的结构,具有很大的优越性。例如主体与各插件分别开发,便于团队合作;插件“即插即用”,便于定制系统功能;插件模块可方便的单独升级等等。

 

插件式结构的本质在于不修改主体程序的情况下,通过插件模块来对软件功能进行扩展或者修订。

 

主体+插件式结构是将一个软件分为两部分。一部分为程序的主体模块,实现程序的主界面框架与主控调度。另一部分为遵循系统整体结构预先约定的某项/某方面的功能模块,可以方便地往主体“即插即用”。

 

下面使用具体的案例来探讨这种编程方式。

 

我们经常发现很多的管理系统使用MDI式的父/子窗口界面,初学者通常是把父子窗口放在同一个项目里,其实它们也是可以分离的。下面使用VS2005/C#来逐步实现。

 

在VS里建立一个解决方案,建3个项目:

(1)类库:MdiLib,定义MDI父窗口与子窗口的规范;

(2)类库:FuncModule1,模拟一个功能插件,准备把MDI子窗口放在这里面;

(3)WindowsApplication:MainModuleDynamic,模拟程序主体,实现主窗口与主控调度。

注意:(1)进入每个项目的属性页,把它们的生成输出路径都设置为:..\bin,便于使用反射机制来动态加载程序集。(2)MainModuleDynamic项目引用MdiLib项目,FuncModule1项目也引用MdiLib项目。

 

在MdiLib项目里添加两个接口:

 

    public interface IMainForm

    {

        void SetStatusText(string statusInfo);  // 便于子窗口给父窗口设置状态信息

    }

 

    public interface IFuncForm  // 规范化子窗口的编码

    {

        bool GetData();

        void BindData();

    }

 

在FuncModule1项目里添加两个Form,一个名为StudentForm,另一个名为TeacherForm。两个Form都是简单地设置窗口Text分别为“学生名单”、“教师名单”;上面各放一个DataGridView,改名为“gridData”。

 

插件式编程之使用反射分离MDI的父窗口和子窗口

 

StudentForm代码如下:

 

using MdiLib;

 

namespace FuncModule1

{

    public partial class StudentForm : Form, IFuncForm

    {

        // 修改构造函数

        private StudentForm(IMainForm mainForm)

        {

            InitializeComponent();

 

            this._MainForm = mainForm;

        }

 

        private IMainForm _MainForm;

 

        // 本窗体用的数据容器

        IList<Student> list = null;

 

        // 学生名单功能的唯一入口方法

        public static void ShowMe(IMainForm mainForm)

        {

            StudentForm frm = null;

 

            foreach (Form mdiChild in (mainForm as Form).MdiChildren)

            {

                if (mdiChild.Text == "学生名单")

                {

                    frm = mdiChild as StudentForm;

                    break;

                }

            }

            if (frm != null)

            {

                if (frm.WindowState == FormWindowState.Minimized)

                    frm.WindowState = FormWindowState.Maximized;

                frm.Activate();

            }

            else

            {

                frm = new StudentForm(mainForm);

                frm.MdiParent = (Form)mainForm;

                if (frm.GetData())  // 如果获取数据发生错误,再显示窗口就没有意义

                {

                    frm.BindData();

                    frm.Show();

                }

            }

        }

 

        #region IFuncForm 成员

 

        public bool GetData()

        {

            // 模拟获得数据

            try

            {

                this.list = new List<Student>();

                list.Add(new Student("小马", 18, "男"));

                list.Add(new Student("小牛", 17, "男"));

                list.Add(new Student("小朱", 19, "女"));

                list.Add(new Student("小杨", 18, "男"));

            }

            catch (Exception ex)

            {

                MessageBox.Show(ex.Message);

                return false;

            }

            return true;

        }

 

        public void BindData()

        {

            this.gridData.DataSource = this.list;

        }

 

        #endregion

    }

 

    // 仅供演示用

    public class Student

    {

        public Student(string name, int age, string sex)

        {

            this._Name = name;

            this._Age = age;

            this._Sex = sex;

        }

 

        private string _Name;

        public string Name

        {

            get { return _Name; }

            set { _Name = value; }

        }

 

        private int _Age;

        public int Age

        {

            get { return _Age; }

            set { _Age = value; }

        }

 

        private string _Sex;

        public string Sex

        {

            get { return _Sex; }

            set { _Sex = value; }

        }

    }

}

 

TeacherForm代码与StudentForm代码非常类似,也贴在下面:

 

插件式编程之使用反射分离MDI的父窗口和子窗口

 

using MdiLib;

 

namespace FuncModule1

{

    public partial class TeacherForm : Form, IFuncForm

    {

        // 修改构造函数

        private TeacherForm(IMainForm mainForm)

        {

            InitializeComponent();

 

            this._MainForm = mainForm;

        }

 

        private IMainForm _MainForm;

 

        // 本窗体用的数据容器

        IList<Teacher> list = null;

 

        // 教师名单功能的唯一入口方法

        public static void ShowMe(IMainForm mainForm)

        {

            TeacherForm frm = null;

 

            foreach (Form mdiChild in (mainForm as Form).MdiChildren)

            {

                if (mdiChild.Text == "教师名单")

                {

                    frm = mdiChild as TeacherForm;

                    break;

                }

            }

            if (frm != null)

            {

                if(frm.WindowState ==FormWindowState.Minimized)

                    frm.WindowState = FormWindowState.Maximized;

                frm.Activate();

            }

            else

            {               

                frm = new TeacherForm(mainForm);

                frm.MdiParent = (Form)mainForm;

                if (frm.GetData())  // 如果获取数据发生错误,再显示窗口就没有意义

                {

                    frm.BindData();

                    frm.Show();

                }

            }           

        }

 

        #region IFuncForm 成员

 

        public bool GetData()

        {   // 模拟获得数据

            try

            {

                this.list = new List<Teacher>();

                list.Add(new Teacher("张三", "语文"));

                list.Add(new Teacher("李四", "数学"));

                list.Add(new Teacher("王五", "英语"));

            }

            catch (Exception ex)

            {

                MessageBox.Show(ex.Message);

                return false;

            }

            return true;

        }

 

        public void BindData()

        {

            this.gridData.DataSource = this.list;

        }

 

        #endregion

    }

 

    // 仅供演示用

    public class Teacher

    {

        public Teacher(string name, string course)

        {

            this._Name = name;

            this._Course = course;

        }

 

        private string _Name;

        public string Name

        {

            get { return _Name; }

            set { _Name = value; }

        }

 

        private string _Course;

        public string Course

        {

            get { return _Course; }

            set { _Course = value; }

        }

    }

}

 

这样,我们在FuncMoudle1里面模拟实现了一些单独的程序功能,包括其界面,看起来系统结构很良好。下面实现程序主体模块。

 

在MainModuleDynamic里加入一个Form:

  

插件式编程之使用反射分离MDI的父窗口和子窗口

 

(1)命名为:MainForm;Text修改为“主窗口”。

(2)修改IsMdiContainer为true。

(3)放一个timer,命名为:timerStatusRestore,设置时间间隔为:1000。

(4)放一个ToolStrip,其自动命名为:toolStrip1。

(5)放一个MenuStrip,其自动命名为:menuStrip1。增加一个菜单项:mnuFile,Text为“File”。准备自动往其下面生成子菜单。

(6)放一个StautsStrip,其自动命名为:statusStrip1。增加一个ToolStripStatusLabel,改名为:tsStatus ,Text设为:“良好”, Spring设为true。再增加一个ToolStripStatusLabel,随便了。

 

这个窗体的代码如下:

 

using MdiLib;

using System.Reflection;

 

namespace MainModuleDynamic

{

    public partial class MainForm : Form, IMainForm

    {

        public MainForm()

        {

            InitializeComponent();

        }

 

        #region 主窗口状态栏管理

 

        #region IMainForm 成员

 

        public void SetStatusText(string statusInfo)

        {

            this.tsStatus.Text = statusInfo;

            this.timerStatusRestore.Start();

        }

 

        private void timerStatusRestore_Tick(object sender, EventArgs e)

        {

            this.SetStatusText("良好");

            this.timerStatusRestore.Stop();

        }

 

        private void MainForm_MdiChildActivate(object sender, EventArgs e)

        {

            this.SetStatusText(this.ActiveMdiChild.Text);

        }

 

        #endregion

 

        #endregion

 

        #region 根据用户权限许可动态生成菜单

 

        private void CreateMenus()

        {

            IList<FuncEntryItem> list = new List<FuncEntryItem>();

            list.Add(new FuncEntryItem("学生名单", "FuncModule1", "StudentForm", "ShowMe"));

            list.Add(new FuncEntryItem("教师名单", "FuncModule1", "TeacherForm", "ShowMe"));

 

            foreach (FuncEntryItem func in list)

            {

                ToolStripMenuItem tsmi = new ToolStripMenuItem();

                tsmi.Text = func.FuncName;

                tsmi.Size = new System.Drawing.Size(152, 22);

                tsmi.Click += new EventHandler(this.MenuItemClick);

                this.mnuFile.DropDownItems.Add(tsmi);

                tsmi.Tag = func;

 

                ToolStripButton tsb = new ToolStripButton();

                tsb.Text = func.FuncName;

                tsb.Size = new System.Drawing.Size(51, 22);

                tsb.Click += new EventHandler(this.MenuItemClick);

                this.toolStrip1.Items.Add(tsb);

                tsb.Tag = func;

            }

        }

 

        private void MenuItemClick(object sender, EventArgs e)

        {

            FuncEntryItem func = (FuncEntryItem)((ToolStripItem)sender).Tag;

 

            #region 使用反射实现调用功能模块的静态方法(那个入口方法)

 

            Assembly asmFuncMoudle = Assembly.Load(func.AssemblyName);  // 动态加载功能模块的Assembly

            Type funcType = asmFuncMoudle.GetType(func.AssemblyName + "." + func.EntryClassName);   // 根据功能模块类名获得该类型

 

            Type[] parameterTypes = new Type[1];    // 预先知道我们设计的该功能模块入口方法只有一个参数

            Assembly asmMdiLib = Assembly.Load("MdiLib");   // 动态加载MdiLib的Assembly

            parameterTypes[0] = asmMdiLib.GetType("MdiLib.IMainForm");  // 这个唯一参数的类型是MdiLib.IMainForm           

 

            MethodInfo funcMethod = funcType.GetMethod(func.EntryMethodName, parameterTypes);   // 根据功能模块入口方法名称获得MethodInfo

 

            object[] parameters = new object[1]; parameters[0] = this;  // 这个唯一参数的传入值是this

 

            funcMethod.Invoke(null, parameters);    // 可以调用这个静态方法了

 

            #endregion

 

        }

 

        #endregion

 

        private void MainForm_Load(object sender, EventArgs e)

        {

            this.CreateMenus();

        }

    }

 

    // 功能调用数据结构

    public class FuncEntryItem

    {

        public FuncEntryItem(string funcName, string assemblyName, string entryClassName, string entryMethodName)

        {

            this._FuncName = funcName;

            this._AssemblyName = assemblyName;

            this._EntryClassName = entryClassName;

            this._EntryMethodName = entryMethodName;

        }

 

        private string _FuncName;

        public string FuncName

        {

            get { return _FuncName; }

            set { _FuncName = value; }

        }

 

        private string _AssemblyName;

        public string AssemblyName

        {

            get { return _AssemblyName; }

            set { _AssemblyName = value; }

        }

 

        private string _EntryClassName;

        public string EntryClassName

        {

            get { return _EntryClassName; }

            set { _EntryClassName = value; }

        }

 

        private string _EntryMethodName;

        public string EntryMethodName

        {

            get { return _EntryMethodName; }

            set { _EntryMethodName = value; }

        }

    }

}

 

运行效果示例如下:

  

插件式编程之使用反射分离MDI的父窗口和子窗口

 

愿这个示例可以作为管理系统实现插件式编程的启发,非常希望与同好者交流。

你可能感兴趣的:(编程)