Winform界面中實(shí)現(xiàn)菜單列表的動態(tài)個性化配置管理

在我們一般的應(yīng)用系統(tǒng)里面,由于系統(tǒng)是面向不同類型的用戶卷拘,我們所看到的菜單會越來越多,多一點(diǎn)的甚至上百個匣缘,但是我們實(shí)際工作接觸的菜單可能就是那么幾個浆熔,那么對于這種龐大的菜單體系还棱,尋找起來非常不便蝶桶。因此對菜單的個性化配置就顯得尤為重要黄娘,本篇隨筆就是基于這樣的理念,提供用戶對可見菜單進(jìn)行一個動態(tài)配置天通,只選自己喜歡泊窘、常用的菜單顯示出來即可熄驼,菜單的配置存儲在數(shù)據(jù)庫里面像寒,在不同的客戶端體驗(yàn)都是一樣烘豹。本篇隨筆主要介紹實(shí)現(xiàn)這樣的功能的一個完整思路,部分代碼邏輯可供參考诺祸。

1携悯、 菜單列表的動態(tài)個性化配置的過程

在我們有些軟件里面,我們可能在界面上頂部放置菜單筷笨,也可能在界面的左側(cè)放置樹形列表菜單憔鬼,這種情況都有可能,本篇摘取其中之一胃夏,左側(cè)菜單進(jìn)行一個介紹菜單的配置處理轴或。

例如我們在左側(cè)根據(jù)用戶權(quán)限展示相關(guān)的菜單信息,動態(tài)生成整個列表展示仰禀,大致的界面效果如下所示照雁。

image

然后在功能列表上提供一個右鍵的菜單進(jìn)行菜單的刷新、配置管理答恶,如下界面所示饺蚊。

image

通過配置功能,我們讓用戶進(jìn)入一個配置管理界面悬嗓,在其中配置顯示自己感興趣的菜單污呼,然后進(jìn)行保存即可,保存后同時刷新界面的功能菜單顯示包竹。

image

以上幾個界面效果就是為了介紹整個菜單配置管理的一般過程燕酷,之所以把界面效果放在前面介紹,就是能夠讓我們有一個類似原型設(shè)計方式的感性認(rèn)識映企,了解了相關(guān)的處理過程悟狱,我們就可以著手通過編碼的方式來實(shí)現(xiàn)這個處理邏輯了。

2堰氓、菜單動態(tài)個性化配置的功能實(shí)現(xiàn)

上面介紹了大概的界面效果挤渐,有了參考,我們可以把它的實(shí)現(xiàn)思路通過代碼實(shí)現(xiàn)出來双絮。

1)參數(shù)的數(shù)據(jù)存儲

首先我們需要了解浴麻,用戶配置可以通過XML保存在本地,也可以通過數(shù)據(jù)庫存儲保存在服務(wù)器囤攀,后者在分布式的客戶端的時候软免,可以處處一樣,這樣就不會造成體驗(yàn)上的差異焚挠,因此我們這里采用存儲在數(shù)據(jù)庫的方案膏萧。

這個存儲我們沿用我之前介紹過的配置管理組件(SettingsProvider.net),我在隨筆《Winform開發(fā)框架之參數(shù)配置管理功能實(shí)現(xiàn)-基于SettingsProvider.net的構(gòu)建》中對它的使用進(jìn)行了詳細(xì)的介紹。

這個配置管理組件SettingsProvider.net使用起來也是比較方便的榛泛,可以選擇存儲在本地的對象蝌蹂,也可以選擇存儲在數(shù)據(jù)庫的存儲對象。

首先我們先定義一個存儲的參數(shù)類曹锨,這個是使用這個組件所必須的存儲對象信息孤个,如下代碼所示。

    /// <summary>
    /// 用來控制人員管理顯示菜單的參數(shù)配置
    /// </summary>
    public class UserMenuParameter
    {
        [DefaultValue("")]
        [Description("用戶ID")]
        public string UserID { get; set; }
        
        
        [Description("用戶設(shè)置可見的菜單")]
        public Dictionary<string, bool> VisibleDict { get; set; }
    }

需要獲取或存儲這個對象信息的時候沛简,我們初始化幾個管理類齐鲤,如下代碼所示。

        //參數(shù)存儲所需的相關(guān)對象
        private SettingsProvider settings;
        private ISettingsStorage store;
        private UserMenuParameter parameter;

然后在配置管理界面窗體里面椒楣,初始化這幾個對象给郊,如下代碼所示。

                // PortableStorage: 在運(yùn)行程序目錄創(chuàng)建一個setting的文件記錄參數(shù)數(shù)據(jù)
                // DatabaseStorage:在數(shù)據(jù)庫TB_UserParameter表存儲用戶配置參數(shù)
                store = new DatabaseStorage(LoginUserInfo.ID);
                settings = new SettingsProvider(store);
                parameter = settings.GetSettings<UserMenuParameter>();

這樣我們就可以根據(jù)用戶的ID捧灰,獲取對應(yīng)記錄的信息并轉(zhuǎn)換為相關(guān)的對象了丑罪,如果我們需要把修改的信息寫會到存儲介質(zhì)里面,代碼如下所示凤壁。

    try
    {
        parameter = settings.GetSettings<UserMenuParameter>();
        parameter.VisibleDict = dict;
        parameter.UserID = LoginUserInfo.ID;
        settings.SaveSettings<UserMenuParameter>(parameter);

        ProcessDataSaved(sender, e);//觸發(fā)外部事件

        this.DialogResult = System.Windows.Forms.DialogResult.OK;
    }
    catch (Exception ex)
    {
        LogHelper.Error(ex);
        MessageDxUtil.ShowError(ex.Message);
        return;
    }

2)配置管理界面的實(shí)現(xiàn)

解決了參數(shù)的獲取及存儲功能后吩屹,我們需要編寫一個界面來管理用戶的菜單配置,也就是我們前面介紹的菜單配置管理界面拧抖。

image

我們這個界面的定義代碼如下所示煤搜。

image

其中參數(shù)的數(shù)據(jù)存儲就是應(yīng)用了前面介紹的代碼,這里需要根據(jù)用戶的配置項(xiàng)初始化樹形菜單的顯示處理唧席,通過InitTree的函數(shù)實(shí)現(xiàn)菜單的顯示擦盾。

在顯示菜單前,我們先介紹一下功能菜單顯示的規(guī)則淌哟,僅當(dāng)參數(shù)存在對應(yīng)記錄迹卢,并且該記錄顯式設(shè)置不可見,菜單才不可見徒仓,否則默認(rèn)菜單是可以看到的腐碱。

這樣確保了,在參數(shù)沒有配置前掉弛,所有的菜單對當(dāng)前用戶是可見的症见,只有用戶設(shè)置為不不可見,該菜單才不顯示為不可見殃饿。

        /// <summary>
        /// 獲取菜單是否可見谋作。
        /// 僅當(dāng)參數(shù)存在對應(yīng)記錄,并且該記錄顯式設(shè)置不可見乎芳,菜單才不可見遵蚜,否則默認(rèn)菜單是可以看到的帖池。
        /// </summary>
        /// <param name="id">菜單ID</param>
        /// <returns></returns>
        private bool GetVisibleMenu(string id)
        {
            bool result = true;
            if (parameter != null)
            {
                var dict = parameter.VisibleDict;
                if(dict != null && dict.ContainsKey(id))
                {
                    result = dict[id];
                }
            }
            return result;
        }

顯示菜單的相關(guān)處理邏輯,就是根據(jù)上面的判斷吭净,然后確定是否勾選記錄碘裕,如下代碼所示。

image

存儲用戶勾選的記錄的時候攒钳,我們需要遍歷整個樹節(jié)點(diǎn),判斷勾選了那些選項(xiàng)雷滋,然后把它保存數(shù)據(jù)庫即可不撑。

        /// <summary>
        /// 遞歸獲取選中的樹節(jié)點(diǎn)集合
        /// </summary>
        /// <param name="node">樹節(jié)點(diǎn)</param>
        /// <param name="dict">字典集合</param>
        /// <returns></returns>
        private Dictionary<string, bool> GetTreeSelection(TreeNode node, Dictionary<string, bool> dict)
        {
            if (node.Tag != null)
            {
                var check = node.Checked;
                var menuId = string.Concat(node.Tag);
                if(!dict.ContainsKey(menuId))
                {
                    dict.Add(menuId, check);
                }
            }

            foreach (TreeNode child in node.Nodes)
            {
                GetTreeSelection(child, dict);
            }

            return dict;
        }

參數(shù)的保存操作如下所示。

        /// <summary>
        /// 保存用戶配置信息
        /// </summary>
        private void btnOK_Click(object sender, EventArgs e)
        {
            //獲取用戶勾選的樹列表晤斩,存放在字典集合里面
            var dict = new Dictionary<string, bool>();
            foreach(TreeNode node in this.treeView1.Nodes)
            {
                GetTreeSelection(node, dict);
            }

            try
            {
                //重新獲取參數(shù)信息焕檬,并設(shè)置新值后保存
                parameter = settings.GetSettings<UserMenuParameter>();
                parameter.VisibleDict = dict;
                parameter.UserID = LoginUserInfo.ID;
                settings.SaveSettings<UserMenuParameter>(parameter);

                ProcessDataSaved(sender, e);//觸發(fā)外部事件

                this.DialogResult = System.Windows.Forms.DialogResult.OK;
            }
            catch (Exception ex)
            {
                LogHelper.Error(ex);
                MessageDxUtil.ShowError(ex.Message);
                return;
            }
        }

3)主界面的相關(guān)處理

以上處理完成后,我們在主界面的工具欄右鍵菜單添加一個菜單項(xiàng)澳泵,用來進(jìn)入配置界面的实愚,如下邏輯代碼所示。

        private void tool_MenuSetting_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e)
        {
            MenuSetting();
        }
        /// <summary>
        /// 配置菜單項(xiàng)
        /// </summary>
        private void MenuSetting()
        {
            FrmMenuSetting dlg = new FrmMenuSetting();
            dlg.OnDataSaved += (s, arg) =>
            {
                //用戶保存參數(shù)后兔辅,提示用戶更新樹形列表
                InitToolbar();
            };
            dlg.ShowDialog();
        }

這樣界面配置參數(shù)并保存后腊敲,界面的樹形菜單會及時得到更新處理。

另外维苔,我們主界面的樹形列表碰辅,也要根據(jù)配置參數(shù)的信息作相關(guān)的調(diào)整,如果用戶配置了不顯示某個菜單介时,那么主界面也要根據(jù)配置參數(shù)控制顯示没宾。

image

3、總結(jié)

以上就是整個菜單列表的動態(tài)個性化配置管理的整體思路和實(shí)現(xiàn)步驟代碼沸柔,主要的界面考量還是以用戶的視覺來考慮界面的布局和功能循衰,如果在幾百個菜單項(xiàng)中尋找?guī)讉€常用的菜單,每次是一個比較耗時無聊的操作褐澎,因此提供一個個性化的界面会钝,根據(jù)工作情況的不同,顯示一些和自己相關(guān)的功能即可工三。

例如有些情況下顽素,我們的菜單顯示,希望通過工具欄的方式進(jìn)行控制顯示徒蟆,如下界面效果所示胁出。

image

那么配置維護(hù)界面還是差不多,只是我們控制工具欄的顯示邏輯有所不同而已段审,對于RibbonPage及其功能菜單的動態(tài)生成處理如下所示全蝶。

image

本篇隨筆主要還是希望讀者借鑒配置存儲和菜單個性化管理的思路,具體的邏輯會因用戶界面的不同,使用的控件不同而有所差異抑淫,不過總體思路是一致的即可绷落。

例如有些參數(shù)的配置管理,可以統(tǒng)一使用一個配置管理界面進(jìn)行維護(hù)始苇,如我之前的隨筆介紹的界面功能一樣砌烁。

image

希望本篇隨筆對你有所啟發(fā),感謝耐心閱讀催式。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末函喉,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子荣月,更是在濱河造成了極大的恐慌管呵,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,265評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件哺窄,死亡現(xiàn)場離奇詭異捐下,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)萌业,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評論 2 385
  • 文/潘曉璐 我一進(jìn)店門坷襟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人生年,你說我怎么就攤上這事啤握。” “怎么了晶框?”我有些...
    開封第一講書人閱讀 156,852評論 0 347
  • 文/不壞的土叔 我叫張陵排抬,是天一觀的道長。 經(jīng)常有香客問我授段,道長蹲蒲,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,408評論 1 283
  • 正文 為了忘掉前任侵贵,我火速辦了婚禮届搁,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘窍育。我一直安慰自己卡睦,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評論 5 384
  • 文/花漫 我一把揭開白布漱抓。 她就那樣靜靜地躺著表锻,像睡著了一般。 火紅的嫁衣襯著肌膚如雪乞娄。 梳的紋絲不亂的頭發(fā)上瞬逊,一...
    開封第一講書人閱讀 49,772評論 1 290
  • 那天显歧,我揣著相機(jī)與錄音,去河邊找鬼确镊。 笑死士骤,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的蕾域。 我是一名探鬼主播拷肌,決...
    沈念sama閱讀 38,921評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼旨巷!你這毒婦竟也來了巨缘?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,688評論 0 266
  • 序言:老撾萬榮一對情侶失蹤契沫,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后昔汉,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體懈万,經(jīng)...
    沈念sama閱讀 44,130評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評論 2 325
  • 正文 我和宋清朗相戀三年靶病,在試婚紗的時候發(fā)現(xiàn)自己被綠了会通。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,617評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡娄周,死狀恐怖涕侈,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情煤辨,我是刑警寧澤裳涛,帶...
    沈念sama閱讀 34,276評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站众辨,受9級特大地震影響端三,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜鹃彻,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評論 3 312
  • 文/蒙蒙 一郊闯、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蛛株,春花似錦团赁、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至笋粟,卻和暖如春剧浸,著一層夾襖步出監(jiān)牢的瞬間锹引,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評論 1 265
  • 我被黑心中介騙來泰國打工唆香, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留嫌变,地道東北人。 一個月前我還...
    沈念sama閱讀 46,315評論 2 360
  • 正文 我出身青樓躬它,卻偏偏與公主長得像腾啥,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子冯吓,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評論 2 348

推薦閱讀更多精彩內(nèi)容