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


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

    作者:sagahu@163.com,发表于: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”。

    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代码非常类似,也贴在下面:

    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:

      

    (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; }

            }

        }

    }

    运行效果示例如下:

      

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

  • 相关阅读:
    springboot中如何获取配置文件的值
    自动获取当月天数
    控制输入前端时间格式及其它常见需求
    页面输出格式常用注解----@JsonIgnore,@JsonFormat,@JsonFormat
    echart动态生成标题
    echart柱状图中每个柱子超过阈值改变颜色状态
    如何把箭头放置在文本前面,并根据数据的正负显示向上和向下箭头
    02、GPIO初始化
    Linux文件目录的权限
    Linux执行命令./command与直接输入命令的区别
  • 原文地址:https://www.cnblogs.com/sagahu/p/2716021.html
Copyright © 2020-2023  润新知