Winform開(kāi)發(fā)主界面菜單的動(dòng)態(tài)樹(shù)形列表展示

我在之前很多文章里面,介紹過(guò)Winform主界面的開(kāi)發(fā),基本上都是標(biāo)準(zhǔn)的界面追迟,在頂部放置工具欄溶其,中間區(qū)域則放置多文檔的內(nèi)容,但是在頂部菜單比較多的時(shí)候敦间,就需要把菜單分為幾級(jí)處理瓶逃,如可以在頂部菜單放置一二級(jí)菜單,這種方式在一般功能點(diǎn)不算太多的情況下廓块,呈現(xiàn)的界面效果較為直觀厢绝、也較為美觀。不過(guò)隨著一些系統(tǒng)功能的增多带猴,這種方式可能就會(huì)顯得工具欄比較擁擠昔汉,那么我們是否可以在左側(cè)放置一個(gè)樹(shù)形列表,這樣通過(guò)樹(shù)形列表的收縮折疊拴清,就可以放置非常多的菜單功能了靶病。

1、菜單的樹(shù)形列表展示

一般情況下口予,樹(shù)形列表的顯示可以分為多個(gè)節(jié)點(diǎn)娄周,節(jié)點(diǎn)可以收縮也可以展開(kāi),當(dāng)然節(jié)點(diǎn)是有不同的圖標(biāo)的了沪停。這樣就可以把很多功能點(diǎn)整合在一個(gè)樹(shù)列表里面了煤辨,樹(shù)的節(jié)點(diǎn)也可以分為很多級(jí)別,很多層次



如果我們想按照業(yè)務(wù)的范疇來(lái)區(qū)分木张,也可以分為多個(gè)模塊展示众辨,類似選項(xiàng)卡的方式,一個(gè)模塊的功能菜單列表集合在一起展示舷礼,如下所示泻轰。

上面這樣的折疊展示,有利于業(yè)務(wù)范疇的區(qū)分且轨,并且可以讓樹(shù)菜單菜單不會(huì)很大浮声,是一種比較好的界面組織方式虚婿。

2、菜單的動(dòng)態(tài)配置管理

上面介紹了樹(shù)形菜單的展示泳挥,以及如何組織菜單的內(nèi)容然痊,做好這些,就為我們奠定了界面菜單組織的雛形了屉符。
那么問(wèn)題來(lái)了剧浸,我們一般是需要根據(jù)系統(tǒng)創(chuàng)建很多菜單的,如果是能通過(guò)配置的方式矗钟,這樣才能較好的管理這些菜單唆香,而且可以動(dòng)態(tài)給菜單指定權(quán)限,實(shí)現(xiàn)不同角色用戶的權(quán)限控制吨艇。
那么我們就需要在系統(tǒng)里面引入一個(gè)菜單管理模塊躬它,實(shí)現(xiàn)菜單的配置管理功能,方便我們后面的動(dòng)態(tài)創(chuàng)建菜單操作东涡。



通過(guò)菜單的配置冯吓,我們可以指定菜單的圖標(biāo),是否可見(jiàn)疮跑,是否展開(kāi)组贺,權(quán)限控制點(diǎn),以及菜單觸發(fā)點(diǎn)擊后祖娘,處理的窗體對(duì)象等信息失尖,有了這些基礎(chǔ)信息,我們就很方便把菜單在樹(shù)形列表里面進(jìn)行合適渐苏、美觀的展示了雹仿。

3、菜單動(dòng)態(tài)構(gòu)建的實(shí)現(xiàn)

前面介紹了整以,如何在數(shù)據(jù)庫(kù)里面對(duì)菜單數(shù)據(jù)進(jìn)行了存儲(chǔ)胧辽,這樣我們就可以在系統(tǒng)主界面里面,動(dòng)態(tài)的構(gòu)建屬性列表進(jìn)行菜單的展示操作了公黑。
首先邑商,我們需要在設(shè)計(jì)時(shí)刻對(duì)主界面的布局進(jìn)行一定的設(shè)計(jì),放置一些初始化的樹(shù)形列表凡蚜,方便查看效果人断。至于里面的內(nèi)容,我們可以根據(jù)數(shù)據(jù)庫(kù)的菜單配置朝蜘,動(dòng)態(tài)從數(shù)據(jù)庫(kù)里面獲取菜單信息恶迈,在左側(cè)樹(shù)形列表里面進(jìn)行構(gòu)建。



我們可以通過(guò)一個(gè)輔助類進(jìn)行菜單的動(dòng)態(tài)創(chuàng)建谱醇,如下所示暇仲。

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

也就是輔助類步做,傳入當(dāng)前窗體,以及左側(cè)的導(dǎo)航控件等參數(shù)后奈附,我們?cè)谳o助類里面封裝對(duì)應(yīng)的動(dòng)態(tài)構(gòu)建菜單的邏輯處理全度。
首先我們動(dòng)態(tài)創(chuàng)建的開(kāi)始,先要清空原來(lái)控件展示的菜單內(nèi)容斥滤,并重新從數(shù)據(jù)庫(kù)里面獲取将鸵,如下代碼所示。

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

//限定顯示幾個(gè)導(dǎo)航選項(xiàng)卡
barControl.NavigationPaneMaxVisibleGroups = 3;

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

然后我們會(huì)對(duì)菜單進(jìn)行遍歷挑胸,并判斷是否具有對(duì)應(yīng)的權(quán)限點(diǎn)痒筒,如果沒(méi)有對(duì)應(yīng)的權(quán)限,那么對(duì)應(yīng)菜單的子菜單也不會(huì)進(jìn)一步展示嗜暴。

//遞歸遍歷所有的菜單,進(jìn)行分級(jí)展示
foreach (MenuNodeInfo firstInfo in menuList)
{
    //如果沒(méi)有菜單的權(quán)限议蟆,則跳過(guò)
    if (!Portal.gc.HasFunction(firstInfo.FunctionId)) continue;

創(chuàng)建菜單的時(shí)候闷沥,我們注意到整個(gè)菜單項(xiàng)是動(dòng)態(tài)構(gòu)建的,因此我們需要根據(jù)NavBarControl的控件屬性咐容,動(dòng)態(tài)構(gòu)建對(duì)應(yīng)的選項(xiàng)卡NavBarGroup舆逃、展示容器NavBarGroup、樹(shù)形對(duì)象TreeView戳粒、樹(shù)形節(jié)點(diǎn)TreeNode等內(nèi)容路狮,如下代碼所示。

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

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

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

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);

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

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

通過(guò)遞歸的方式奄妨,我們就很容易遞歸構(gòu)建了所有層次的樹(shù)形菜單,并進(jìn)行合適的展示了苹祟。

菜單的單擊事件砸抛,我們通過(guò)一個(gè)函數(shù)代碼實(shí)現(xiàn)對(duì)它進(jìn)行處理就可以了。

//處理樹(shù)形菜單的點(diǎn)擊操作树枫,如果TAG存在直焙,則解析并加載對(duì)應(yīng)的頁(yè)面到多文檔里面
treeView.AfterSelect += (sender, e) =>
{
    string tag = e.Node.Tag as string;
    if (!string.IsNullOrEmpty(tag))
    {
        LoadPlugInForm(tag);
    }
};

這里面就是對(duì)它的AfterSelect 事件進(jìn)行處理,實(shí)現(xiàn)我們動(dòng)態(tài)加載窗體對(duì)象到多文檔界面的處理了砂轻。
其中加載窗體是根據(jù)菜單配置的選項(xiàng)奔誓,動(dòng)態(tài)構(gòu)建界面出來(lái)的,具體分析代碼如下所示搔涝。

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

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

        //判斷是否配置了顯示模式厨喂,默認(rèn)窗體為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}】失敗和措,請(qǐng)檢查書(shū)寫是否正確。", typeName), ex);
    }
}

加載多文檔的操作杯聚,就是在集合里面判斷是否存在臼婆,如果沒(méi)有存在就創(chuàng)建,否則就激活顯示即可幌绍,具體處理如下所示颁褂。

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

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

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

            //記錄程序的相關(guān)信息
            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誓酒、系統(tǒng)界面的總體效果

最后,為了更好理解整個(gè)動(dòng)態(tài)菜單的界面效果贮聂,貼出幾個(gè)做好的界面展示圖靠柑,供參考學(xué)習(xí)。
1)標(biāo)準(zhǔn)界面的處理方式


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



打開(kāi)多文檔頁(yè)面后如下所示吓懈。


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末歼冰,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子耻警,更是在濱河造成了極大的恐慌隔嫡,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,548評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件甘穿,死亡現(xiàn)場(chǎng)離奇詭異腮恩,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)温兼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門秸滴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人募判,你說(shuō)我怎么就攤上這事缸榛。” “怎么了兰伤?”我有些...
    開(kāi)封第一講書(shū)人閱讀 167,990評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵内颗,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我敦腔,道長(zhǎng)均澳,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,618評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮找前,結(jié)果婚禮上糟袁,老公的妹妹穿的比我還像新娘。我一直安慰自己躺盛,他們只是感情好项戴,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,618評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著槽惫,像睡著了一般周叮。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上界斜,一...
    開(kāi)封第一講書(shū)人閱讀 52,246評(píng)論 1 308
  • 那天仿耽,我揣著相機(jī)與錄音,去河邊找鬼各薇。 笑死项贺,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的峭判。 我是一名探鬼主播开缎,決...
    沈念sama閱讀 40,819評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼林螃!你這毒婦竟也來(lái)了奕删?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,725評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤治宣,失蹤者是張志新(化名)和其女友劉穎急侥,沒(méi)想到半個(gè)月后砌滞,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體侮邀,經(jīng)...
    沈念sama閱讀 46,268評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,356評(píng)論 3 340
  • 正文 我和宋清朗相戀三年贝润,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了绊茧。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,488評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡打掘,死狀恐怖华畏,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情尊蚁,我是刑警寧澤亡笑,帶...
    沈念sama閱讀 36,181評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站横朋,受9級(jí)特大地震影響仑乌,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,862評(píng)論 3 333
  • 文/蒙蒙 一晰甚、第九天 我趴在偏房一處隱蔽的房頂上張望衙传。 院中可真熱鬧,春花似錦厕九、人聲如沸蓖捶。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,331評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)俊鱼。三九已至,卻和暖如春穿香,著一層夾襖步出監(jiān)牢的瞬間亭引,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,445評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工皮获, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留焙蚓,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,897評(píng)論 3 376
  • 正文 我出身青樓洒宝,卻偏偏與公主長(zhǎng)得像购公,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子雁歌,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,500評(píng)論 2 359

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