首先看看上面的資料庫設計結構,其中ID為自動編号的主鍵字段,Code為菜單編碼(也稱業務編碼),Name為菜單名稱, Url為單擊菜單所導航的路徑,ImageUrl為菜單上顯示的圖檔位址,ShortCut為快捷鍵,Order為菜單項顯示順序,Grade為菜單級限,EndGrade為是否為末級菜單(既最後一菜單項),SupMenuCode為目前菜單項的父級菜單編碼(對應于Code字段),完整的還應該包括是否受權限控制等字段,這裡不一一列出。
上面的資料庫設計其實很簡單,需要主要的字段有Code菜單編碼(也稱業務編碼),在業務系統裡通常設計為每一菜單項都可以認為是一個業務操作,這裡的Code非常重要,業務系統的後續工作都是從這裡開始,通常與權限設計結合使用得最多。
系統菜單應該是多層次結構的,比如常見的Windows菜單,業務系統裡的菜單設計同樣如此,通過SupMenuCode字段來标記該菜單項屬于那一項菜單的子菜單,如果沒有父級菜單(系統頂層菜單)則标記為0。
既然菜單有層次結構,那就一定有層次辨別,通過Grade來标記;EndGrade則表示目前菜單項在該層次級别上的菜單項上是否為末級。
分析清楚了需求和菜單設計方案,下面我們着手代碼開發,下圖為項目解決方案截圖:
如上圖,Menu.cs裡封裝了資料庫查詢功能,App_Data下存放的Access資料庫,RadControls為RadMenu控件所需要的相關檔案,Default.aspx則為菜單UI界面。
首先需要配置資料庫連串,如下:
1 private static string connectionString = "Provider=Microsoft.jet.OLEDB.4.0;data Source="
2 + HttpContext.Current.Server.MapPath(@"App_Data\MenuDB.mdb");
接着我們需要一個執行SQL的方法,如下代碼塊:
1 private DataTable ExecuteQuery(string cmdText)
2 {
3 using (OleDbConnection conn = new OleDbConnection(connectionString))
4 {
5 using (OleDbDataAdapter oda = new OleDbDataAdapter(cmdText, conn))
6 {
7 DataSet ds = new DataSet();
8 oda.Fill(ds);
9 return ds.Tables[0];
10 }
11 }
12 }
最後我們還需要寫兩個方法,一個是查詢頂層菜單的方法,一個則是實作查詢菜單下的子菜單方法(做法有很多種,我個人建議把所有的資料查詢出然後在操作記憶體資料,這裡為了友善了解我故采用多次查詢資料庫的方式來實作)。
1 public DataTable GetMenu()
3 string sql = "select * from menu where grade=0";
4 return ExecuteQuery(sql);
5 }
6
7 public DataTable GetMenuBySupCode(string supMenuCode)
8 {
9 string sql = "select * from menu where supmenucode='" + supMenuCode + "'";
10 return ExecuteQuery(sql);
11 }
OK,準備工作做好後,現在就是需要到UI層上去做菜單的展現工作了。菜單的展現我通過RadContrls的RadMenu控件來實作,此控件相對ASP.NET的标準Menu控件功能強大。要使用RadMenu則需要注冊控件的引用,如果你是通過RadContrls控件庫安裝包安裝的則會自動注冊到VS工具箱,和标準控件使用方法一樣。
1 <%@ Register Assembly="RadMenu.Net2" Namespace="Telerik.WebControls" TagPrefix="rad" %>
1 <%--菜單開始--%>
2 <rad:RadMenu ID="SystemMenu" runat="server" Skin="Vista">
3 </rad:RadMenu>
4 <%--菜單結束--%>
上面準備好了頂層菜單的查詢方法,直接通過該方法查詢出頂層菜單項對菜單進行初始化:
1 protected void Page_Load(object sender, EventArgs e)
3 if (!IsPostBack)
5 InitMenu();
6 }
7 }
8
9 Menu menu = new Menu();
10 private void InitMenu()
11 {
12 //查詢出菜單配置資訊
13 DataTable dtMenu = menu.GetMenu();
14
15 for (int i = 0; i < dtMenu.Rows.Count; i++)
16 {
17 //Rad菜單項
18 RadMenuItem item = new RadMenuItem();
19
20 item.ID = dtMenu.Rows[i]["Code"].ToString();
21 item.Text = dtMenu.Rows[i]["Name"].ToString();
22 item.Value = dtMenu.Rows[i]["Url"].ToString();
23 item.AccessKey = dtMenu.Rows[i]["ShortCut"].ToString();
24
25 this.SystemMenu.Items.Add(item);
26
27 //菜建子菜單
28 InitSubMenu(item, dtMenu.Rows[i]["Code"].ToString());
29 }
30 }
我們設計為多層次菜單結構,那麼在初始化頂層菜單的時候就應該判斷該菜單項是否有子菜單,如果有則也初始化子菜單,如果是多級層次的結構,通過遞歸算法來完成多層次的初始化。如下代碼塊:
1
2 private void InitSubMenu(RadMenuItem item, string supMenuCode)
3 {
4 //根據父菜單編碼查詢出子菜單配置項
5 DataTable dtMenu = menu.GetMenuBySupCode(supMenuCode);
7 if (dtMenu != null)
8 {
9 foreach (DataRow dataRow in dtMenu.Rows)
10 {
11 RadMenuItem subItem = new RadMenuItem();
12
13 subItem.ID = dataRow["ID"].ToString();
14 subItem.Text = dataRow["Name"].ToString();
15 subItem.Value = dataRow["Url"].ToString();
16 subItem.AccessKey = dataRow["ShortCut"].ToString();
17
18 item.Items.Add(subItem);
20 if (Convert.ToInt32(dataRow["EndGrade"]) != 1)
21 {
22 InitSubMenu(subItem, Convert.ToString(dataRow["Code"]));
23 }
24 }
25 }
26 }
如上就完成了系統的菜單設計,實際項目開發中的設計與這樣的設計幾乎都大同小異。主要是了解設計思想,或許看完了本文你還是沒有明白為什麼要這樣設計,如果是這樣那請你關注下一篇文章,下一篇文章中就用到了菜單中的業務編碼,應該可以給一個滿意的答案。
本文轉自 beniao 51CTO部落格,原文連結:http://blog.51cto.com/beniao/202205,如需轉載請自行聯系原作者