现在的位置: 首页 > 综合 > 正文

Winform 窗口加载实例(一)—–MenuStrip

2013年02月24日 ⁄ 综合 ⁄ 共 5154字 ⁄ 字号 评论关闭

       在开发软件的时候,常常会遇到这样的功能要求,根据不同的用户权限加载不同的功能界面。这样就需要我们在设计软件的时候动态添加添加功能。当然也可以把所有的功能都列出来,在根据权限来显示和隐藏功能,虽然这样也可以满足需求,但是需要很多额外的编码,性能上也可能大打折扣。

      在WinForm编程中,MDI是一种很常用的结构。由于Windows界面的影响,很多时候我们设计软件的时候都喜欢上菜单下界面的模式。下面就结合代码来实现动态添加MenuStrip。

       动态添加MenuStrip主要要解决三个方面的问题:

      1、数据库设计

      2、MenuStrip菜单的加载

      3、菜单和窗口的结合

      首先来解决数据库的设计问题。

      一个窗口显示给用户看到的一般就是一个名称。另外菜单可能有级数,级数的解决一般有两种:根据编号来区分,设置父菜单。我喜欢根据编号来区分。比如:000 代表父菜单 000001代表一级菜单......如此类推。

      数据库里面存储模块表的结构一般是 sBoardID  sBoardName   sFile  sForm  sMethod

      sBoardID  菜单编号 

      sBoardName 菜单名称

      sFile 菜单Form所在的文件(exe或者dll)

      sForm  菜单对应的Form

      sMethod 菜单对应的要执行的方法(有的菜单只有功能,没有界面,比如“退出系统”,“重新登录”)

      其次来看看MenuStrip菜单的加载

      因为菜单可能有多级,所以需要循环加递归来完成加载。会在下面的代码里面说明。

      最后来看看菜单和窗口的结合

      在数据库中,我们存储的都是字符串,而在程序中,我们却需要显示对应的窗口,这样就要求我们根据字符串来实例化对象。在.net里面我们可以用反射来实现。

      上面说了,有些菜单只执行方法而没有界面,同样的数据库中存储的也只是字符串类型的方法名称,这样就要求我们根据字符串方法名执行对应的方法,自然而然的我们就会想到委托。

      以上的问题都解决了,下面我们来看看代码:

     menuMain是一个MenuStrip控件

     private delegate void dlRunMethod();//定义一个委托,用于执行方法

 

       //动态生成MenuStrip项
        public void InstallMenus(ToolStripDropDownItem OwnerMenu, DataRow OwnerRow)
        {
            string strModule = OwnerRow == null ? "" : OwnerRow["sBoardID"].ToString();

            //根据用户和父菜单来查询对应的子菜单,可换成你的查询代码
            DataTable tabMenu = (new SysBoard()).QueryData(strModule,UserInfo.UserType);

            //初始调用的时候,清空MenuStrip的所有内容
            if (OwnerMenu == null) menuMain.Items.Clear();       
            if (tabMenu.Rows.Count == 0 && OwnerMenu == null) return;

 

            //没有子级菜单,加载菜单单击事件

            if (tabMenu.Rows.Count == 0) 
            {
                Form frmCur = null;
                if ((OwnerRow["sFile"].ToString().Length==0 || OwnerRow["sForm"].ToString().Length==0) && OwnerRow["sMethod"].ToString().Length==0)
                    OwnerMenu.Enabled = false;
                else
                {
                   //生成菜单的单击事件
                    OwnerMenu.Click += delegate
                    #region 加载页面
                    {
                        GC.Collect(); 
                        try
                        {
                            if (OwnerRow["sFile"].ToString().Length==0) frmCur = this;
                            else if (frmCur == null || frmCur.IsDisposed)
                            {

                                 //加载Form窗口所在的程序集
                                 Assembly assDLL = Assembly.LoadFrom(Application.StartupPath + "//" + OwnerRow["sFile"].ToString()); 
                                if (assDLL == null)
                                    throw new Exception("找不到文件" + OwnerRow["sFile"].ToString() + "/n请更新程序");

                                //根据窗体名称生成对应的窗体
                                frmCur = (Form)assDLL.CreateInstance(OwnerRow["sForm"].ToString()); 
                                if (frmCur == null)
                                    throw new Exception("在文件" + OwnerRow["sFile"].ToString() + "中找不到类文件" + OwnerRow["sForm"].ToString() + "/n请更新程序");
                                frmCur.Tag = OwnerRow["sBoardID"];
                                frmCur.Text = OwnerRow["sBoardName"].ToString();
                            }

 

                            //如果数据库中有对应的方法,则生成该方法的委托,如果没有则生成窗体显示方法的委托

                            dlRunMethod RunMethod = null;
                            if (!OwnerRow.IsNull("sMethod") && OwnerRow["sMethod"].ToString() != "")
                                RunMethod = (dlRunMethod)System.Delegate.CreateDelegate(typeof(dlRunMethod), frmCur, OwnerRow["sMethod"].ToString()); 
                            else if (frmCur != this)
                            {
                                RunMethod = frmCur.Show; 
                                frmCur.MdiParent = this;
                            }
                            RunMethod();
                            if (RunMethod.Method.Name == "Show") frmCur.BringToFront(); 
                        }
                        catch (Exception ex)
                        {
                            MessageBox.Show("装载文件出错/n" + ex.Message);
                        }
                    };
                    #endregion
                }
            }

           //有子级菜单,加载子级菜单
            else 
                foreach (DataRow oneRow in tabMenu.Rows) 
                {

                    //是否是分割符
                    if (oneRow["sBoardName"].ToString() == "-" && OwnerMenu == null) continue;
                    else if (oneRow["sBoardName"].ToString() == "-") OwnerMenu.DropDownItems.Add("-"); 
                    else
                    {
                        ToolStripDropDownItem menuNew = new ToolStripMenuItem(oneRow["sBoardName"].ToString());
                        menuNew.Tag = oneRow["sBoardID"].ToString();

                        (OwnerMenu == null ? menuMain.Items : OwnerMenu.DropDownItems).AddRange(new ToolStripItem[] { menuNew }); 

                        //递归调用
                        InstallMenus(menuNew, oneRow); 
                    }
                }
        }

       整个功能实现起来都是比较简单的,唯一有些疑惑的地方可能就是那个sMethod的使用。举个例子来说明。很多软件中都有”退出系统“整个功能,整个功能菜单是只有整个功能而没有界面,和前面的菜单不同。也就是这个菜单是调用的一个方法,比如我们命名为Loginout:

      private void Loginout()

      {

            Application.Exit();

       }

       如果菜单不是动态生成的,点击菜单的时候调用这个方法就行了。但是菜单是动态生成,也就是这个方法要动态调用。其在数据库中的数据为: 

sBoardID          sBoardName            sFile           sForm              sMethod

   009                  重新登录                                                           Loginout   

抱歉!评论已关闭.