我在之前很多文章里面,介紹過(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è)面后如下所示吓懈。