Winform開發主界面菜單的動態樹形列表展示


我在之前很多文章里面,介紹過Winform主界面的開發,基本上都是標准的界面,在頂部放置工具欄,中間區域則放置多文檔的內容,但是在頂部菜單比較多的時候,就需要把菜單分為幾級處理,如可以在頂部菜單放置一二級菜單,這種方式在一般功能點不算太多的情況下,呈現的界面效果較為直觀、也較為美觀。不過隨着一些系統功能的增多,這種方式可能就會顯得工具欄比較擁擠,那么我們是否可以在左側放置一個樹形列表,這樣通過樹形列表的收縮折疊,就可以放置非常多的菜單功能了。

1、菜單的樹形列表展示

一般情況下,樹形列表的顯示可以分為多個節點,節點可以收縮也可以展開,當然節點是有不同的圖標的了。這樣就可以把很多功能點整合在一個樹列表里面了,樹的節點也可以分為很多級別,很多層次

  

如果我們想按照業務的范疇來區分,也可以分為多個模塊展示,類似選項卡的方式,一個模塊的功能菜單列表集合在一起展示,如下所示。

上面這樣的折疊展示,有利於業務范疇的區分,並且可以讓樹菜單菜單不會很大,是一種比較好的界面組織方式。

 

2、菜單的動態配置管理

上面介紹了樹形菜單的展示,以及如何組織菜單的內容,做好這些,就為我們奠定了界面菜單組織的雛形了。 

那么問題來了,我們一般是需要根據系統創建很多菜單的,如果是能通過配置的方式,這樣才能較好的管理這些菜單,而且可以動態給菜單指定權限,實現不同角色用戶的權限控制。

那么我們就需要在系統里面引入一個菜單管理模塊,實現菜單的配置管理功能,方便我們后面的動態創建菜單操作。

通過菜單的配置,我們可以指定菜單的圖標,是否可見,是否展開,權限控制點,以及菜單觸發點擊后,處理的窗體對象等信息,有了這些基礎信息,我們就很方便把菜單在樹形列表里面進行合適、美觀的展示了。

 

3、菜單動態構建的實現

前面介紹了,如何在數據庫里面對菜單數據進行了存儲,這樣我們就可以在系統主界面里面,動態的構建屬性列表進行菜單的展示操作了。

首先,我們需要在設計時刻對主界面的布局進行一定的設計,放置一些初始化的樹形列表,方便查看效果。至於里面的內容,我們可以根據數據庫的菜單配置,動態從數據庫里面獲取菜單信息,在左側樹形列表里面進行構建。

我們可以通過一個輔助類進行菜單的動態創建,如下所示。

        private void InitToolbar()
        {
            TreeMenuHelper helper = new TreeMenuHelper(this, this.nvBarMenu, this.imageList1);
            helper.Init();
        }

也就是輔助類,傳入當前窗體,以及左側的導航控件等參數后,我們在輔助類里面封裝對應的動態構建菜單的邏輯處理。

首先我們動態創建的開始,先要清空原來控件展示的菜單內容,並重新從數據庫里面獲取,如下代碼所示。

            //清空所有導航控件的內容
            barControl.Controls.Clear();
            barControl.Groups.Clear();
            barControl.Items.Clear();
            this.imageList.Images.Clear();

            //限定顯示幾個導航選項卡
            barControl.NavigationPaneMaxVisibleGroups = 3;

            //約定菜單共有3級,第一級為大的類別,第二級為小模塊分組,第三級為具體的菜單
            List<MenuNodeInfo> menuList = BLLFactory<SysMenu>.Instance.GetTree(Portal.gc.SystemType);
            if (menuList.Count == 0) return;

然后我們會對菜單進行遍歷,並判斷是否具有對應的權限點,如果沒有對應的權限,那么對應菜單的子菜單也不會進一步展示。

            //遞歸遍歷所有的菜單,進行分級展示
            foreach (MenuNodeInfo firstInfo in menuList)
            {
                //如果沒有菜單的權限,則跳過
                if (!Portal.gc.HasFunction(firstInfo.FunctionId)) continue;

創建菜單的時候,我們注意到整個菜單項是動態構建的,因此我們需要根據NavBarControl的控件屬性,動態構建對應的選項卡NavBarGroup、展示容器NavBarGroup、樹形對象TreeView、樹形節點TreeNode等內容,如下代碼所示。

                TreeView treeView = new TreeView();
                treeView.Dock = DockStyle.Fill;
                treeView.ImageList = this.imageList;
                treeView.ItemHeight = 30;//設置高度,顯示更美觀

                NavBarGroupControlContainer container = new NavBarGroupControlContainer();
                container.Size = new System.Drawing.Size(213, 412);
                container.Controls.Add(treeView);
                barControl.Controls.Add(container);

                //加載圖標
                this.imageList.Images.Add(LoadIcon(firstInfo.Icon));
                int index = this.imageList.Images.Count - 1;//最后一個序號

                NavBarGroup group = new NavBarGroup();
                group.Caption = firstInfo.Name;
                group.ControlContainer = container;
                group.Expanded = true;
                group.GroupClientHeight = 410;
                group.GroupStyle = NavBarGroupStyle.ControlContainer;
                group.LargeImageIndex = index;
                group.SmallImageIndex = index;
                barControl.Groups.Add(group);

                //創建一級列表
                TreeNode pNode = new TreeNode();
                pNode.Text = firstInfo.Name;
                pNode.Tag = firstInfo.WinformType;
                pNode.ImageIndex = index;
                pNode.SelectedImageIndex = index;
                treeView.Nodes.Add(pNode);

                //遞歸創建子列表
                AddTreeItems(pNode, firstInfo.Children);

通過遞歸的方式,我們就很容易遞歸構建了所有層次的樹形菜單,並進行合適的展示了。

菜單的單擊事件,我們通過一個函數代碼實現對它進行處理就可以了。

                //處理樹形菜單的點擊操作,如果TAG存在,則解析並加載對應的頁面到多文檔里面
                treeView.AfterSelect += (sender, e) =>
                {
                    string tag = e.Node.Tag as string;
                    if (!string.IsNullOrEmpty(tag))
                    {
                        LoadPlugInForm(tag);
                    }
                };

這里面就是對它的AfterSelect 事件進行處理,實現我們動態加載窗體對象到多文檔界面的處理了。

其中加載窗體是根據菜單配置的選項,動態構建界面出來的,具體分析代碼如下所示。

 

        /// <summary>
        /// 加載插件窗體
        /// </summary>
        private void LoadPlugInForm(string typeName)
        {
            try
            {
                string[] itemArray = typeName.Split(new char[] { ',', ';' });

                string type = itemArray[0].Trim();
                string filePath = itemArray[1].Trim();//必須是相對路徑

                //判斷是否配置了顯示模式,默認窗體為Show非模式顯示
                string showDialog = (itemArray.Length > 2) ? itemArray[2].ToLower() : "";
                bool isShowDialog = (showDialog == "1") || (showDialog == "dialog");

                string dllFullPath = Path.Combine(Application.StartupPath, filePath);
                Assembly tempAssembly = System.Reflection.Assembly.LoadFrom(dllFullPath);
                if (tempAssembly != null)
                {
                    Type objType = tempAssembly.GetType(type);
                    if (objType != null)
                    {
                        LoadMdiForm(this.mainForm, objType, isShowDialog);
                    }
                }
            }
            catch (Exception ex)
            {
                LogTextHelper.Error(string.Format("加載模塊【{0}】失敗,請檢查書寫是否正確。", typeName), ex);
            }
        }

 

加載多文檔的操作,就是在集合里面判斷是否存在,如果沒有存在就創建,否則就激活顯示即可,具體處理如下所示。

        /// <summary>
        /// 唯一加載某個類型的窗體,如果存在則顯示,否則創建。
        /// </summary>
        /// <param name="mainDialog">主窗體對象</param>
        /// <param name="formType">待顯示的窗體類型</param>
        /// <returns></returns>
        public Form LoadMdiForm(Form mainDialog, Type formType, bool isShowDialog)
        {
            Form tableForm = null;
            bool bFound = false;
            if (!isShowDialog) //如果是模態窗口,跳過
            {
                foreach (Form form in mainDialog.MdiChildren)
                {
                    if (form.GetType() == formType)
                    {
                        bFound = true;
                        tableForm = form;
                        break;
                    }
                }
            }

            //沒有在多文檔中找到或者是模態窗口,需要初始化屬性
            if (!bFound || isShowDialog)
            {
                tableForm = (Form)Activator.CreateInstance(formType);

                //如果窗體集成了IFunction接口(第一次創建需要設置)
                IFunction function = tableForm as IFunction;
                if (function != null)
                {
                    //初始化權限控制信息
                    function.InitFunction(Portal.gc.LoginUserInfo, Portal.gc.FunctionDict);

                    //記錄程序的相關信息
                    function.AppInfo = new AppInfo(Portal.gc.AppUnit, Portal.gc.AppName, Portal.gc.AppWholeName, Portal.gc.SystemType);
                }

            }

            if (isShowDialog)
            {
                tableForm.ShowDialog();
            }
            else
            {
                tableForm.MdiParent = mainDialog;
                tableForm.Show();
            }
            tableForm.BringToFront();
            tableForm.Activate();

            return tableForm;
        }

 

4、系統界面的總體效果

最后,為了更好理解整個動態菜單的界面效果,貼出幾個做好的界面展示圖,供參考學習。

1)標准界面的處理方式

 

2)樹形列表界面的處理方式

打開多文檔頁面后如下所示。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM