Получение пунктов меню из базы данных в asp.net
Я пытаюсь получить пункты меню из базы данных SQL Server 2008.
Я попробовал что-то вроде этого, когда я погуглил и нашел эти уроки в первую и вторую:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Data;
using System.Data.SqlClient;
using System.Configuration;
using System.Text;
namespace MenuDriven
{
public partial class MenuDB : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
menuBar.MaximumDynamicDisplayLevels = 3;
if (!IsPostBack)
{
SqlConnection con = new SqlConnection(ConfigurationManager.ConnectionStrings["MyConnection"].ConnectionString);
con.Open();
DataSet ds = new DataSet();
DataTable dt = new DataTable();
string sql = "Select * from MenuItems";
SqlDataAdapter da = new SqlDataAdapter(sql, con);
da.Fill(ds);
dt = ds.Tables[0];
DataRow[] drowpar = dt.Select("ParentID=" + 0);
foreach (DataRow dr in drowpar)
{
menuBar.Items.Add(new MenuItem(dr["MenuName"].ToString(),
dr["MenuID"].ToString(), "",
dr["MenuLocation"].ToString()));
}
foreach (DataRow dr in dt.Select("ParentID >" + 0))
{
MenuItem mnu = new MenuItem(dr["MenuName"].ToString(), dr["MenuID"].ToString(),
"", dr["MenuLocation"].ToString());
//Code for Multiple Menu Levels
string valuePath = getValuePath(Convert.ToInt32(dr["ParentID"].ToString()), dt);
//menuBar.FindItem(dr["ParentID"].ToString()).ChildItems.Add(mnu);
menuBar.FindItem(valuePath).ChildItems.Add(mnu);**NullReferenceException was handled by the code**
//End Code for Multiple Menu Levels
}
con.Close();
}
}
private string getValuePath(Int32 Parent, DataTable dt)
{
int predecessor = Parent;
StringBuilder valuePath = new StringBuilder();
valuePath.Append(Parent.ToString());
DataRow[] drPar;
while (true)
{
drPar = dt.Select("MenuID=" + predecessor);
if (drPar[0]["ParentID"].ToString().Equals("0"))**//Index out of range exception**
break;
valuePath.Insert(0, '/');
valuePath.Insert(0, drPar[0]["ParentID"].ToString());
predecessor = Convert.ToInt32(drPar[0]["ParentID"].ToString());
}
return valuePath.ToString();
}
}
}
У меня есть две ошибки из этого кода:
- Исключение индекса вне диапазона
NullReferenceException
был обработан кодом
Я указал код, сгенерировавший ошибку.
Это моя база данных вместе с заголовками.
MenuID MenuName MenuLocation ParentID Value
1 Parent1 NULL 0 p1
2 Parent2 NULL 0 p2
3 Parent3 NULL 0 p3
11 SubMenuItem1 NULL 1 s1
12 SubMenuItem2 NULL 1 s1
21 SubMenuItem3 NULL 2 s1
111 SubSubMenuItem4 NULL 1 ss1
211 SubSubMenuItem5 NULL 2 ss1
3 ответа
У меня есть решение для любого количества уровней в пунктах меню.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Data;
using System.Data.SqlClient;
using System.Configuration;
namespace MenuDriven
{
public partial class AnotherMenuTest : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
populateMenuItem();
}
private void populateMenuItem()
{
DataTable menuData = GetMenuData();
AddTopMenuItems(menuData);
}
private DataTable GetMenuData()
{
using (SqlConnection con = new SqlConnection(ConfigurationManager.ConnectionStrings["ServerString"].ConnectionString))
{
using (SqlCommand cmd = new SqlCommand("SELECT MenuID,MenuName,ParentID FROM MenuItems", con))
{
SqlDataAdapter da = new SqlDataAdapter(cmd);
DataTable dt = new DataTable();
da.Fill(dt);
return dt;
}
}
}
/// Filter the data to get only the rows that have a
/// null ParentID (This will come on the top-level menu items)
private void AddTopMenuItems(DataTable menuData)
{
DataView view = new DataView(menuData);
view.RowFilter = "ParentID = 0";
foreach (DataRowView row in view)
{
MenuItem newMenuItem = new MenuItem(row["MenuName"].ToString(), row["MenuID"].ToString());
menuBar.Items.Add(newMenuItem);
AddChildMenuItems(menuData, newMenuItem);
}
}
//This code is used to recursively add child menu items by filtering by ParentID
private void AddChildMenuItems(DataTable menuData, MenuItem parentMenuItem)
{
DataView view = new DataView(menuData);
view.RowFilter = "ParentID=" + parentMenuItem.Value;
foreach (DataRowView row in view)
{
MenuItem newMenuItem = new MenuItem(row["MenuName"].ToString(), row["MenuID"].ToString());
parentMenuItem.ChildItems.Add(newMenuItem);
AddChildMenuItems(menuData, newMenuItem);
}
}
}
}
Спасибо за KnowledgeSeeker и Антонио за ваши ответы.
и база данных такая.
Это моя база данных вместе с заголовками.
MenuID MenuName MenuLocation ParentID
1 Parent1 NULL 0
2 Parent2 NULL 0
3 Parent3 NULL 0
11 SubMenuItem1 NULL 1
12 SubMenuItem2 NULL 1
21 SubMenuItem3 NULL 2
111 SubSubMenuItem4 NULL 1
211 SubSubMenuItem5 NULL 2
Решение: установить родительские узлы в самой базе данных.
Пример: вставить в значения dbo.MenuItems(333, SubSubMenuItem8,NULL,21)
Это установит SubSubMenuItem8
как ребенок к родительскому узлу SubMenuItem3
Измените свой код в соответствии с приведенным ниже примером. Я использую следующее меню. Это работает Вам может потребоваться изменить код в соответствии с вашими потребностями
String sqlQuery = "SELECT * FROM pMenuItems";
dsMenu = DataProvider.Connect_Select(sqlQuery);
DataTable tableMenu = dsMenu.Tables[0];
DataView dvMenu = new DataView(tableMenu);
//dvMenu.RowFilter = "PageInheritance is NULL";
dvMenu.RowFilter = "PageInheritance = 0";
int dsMenuRowCount = dsMenu.Tables[0].Rows.Count;
//Create Top Menu
//StringBuilder sb = new StringBuilder();
StringBuilder sbMenuFooter = new StringBuilder();
if (dsMenu != null && dsMenu.Tables.Count > 0 && dsMenu.Tables[0].Rows.Count > 0)
{
foreach (DataRowView row in dvMenu)
{
// MenuItem menuItem = new MenuItem(row["MenuName"].ToString().ToUpper(), row["MenuID"].ToString());
MenuItem menuItem = new MenuItem(row["PageName"].ToString(), row["PageId"].ToString());
bool PageInternalLink = bool.Parse(row["PageInternalLink"].ToString());
if (PageInternalLink == true)
{
menuItem.NavigateUrl = row["PageURL"].ToString() + "?PageId=" + row["PageId"];
}
else
{
menuItem.NavigateUrl = row["PageURL"].ToString();
menuItem.Target = row["PageWindow"].ToString();
}
//menuItem.Target =
Menu1.Items.Add(menuItem);
AddChildItems(tableMenu, menuItem, PageInternalLink );
}
private static void AddChildItems(DataTable table, MenuItem menuItem, bool PageInternalLink)
{
DataView viewItem = new DataView(table);
viewItem.RowFilter = "PageInheritance = " + menuItem.Value;
//foreach (DataRowView row in dvMenu)
foreach (DataRowView childView in viewItem)
{
MenuItem childItem = new MenuItem(childView["MenuName"].ToString(), childView["MenuID"].ToString());
childItem.NavigateUrl = childView["PageURL"].ToString() + "?PageId=" + childView["PageId"];
if (PageInternalLink == true)
{
childItem.NavigateUrl = childView["PageURL"].ToString() + "?PageId=" + childView["PageId"];
}
else
{
childItem.NavigateUrl = childView["PageURL"].ToString();
childItem.Target = childView["PageWindow"].ToString();
}
menuItem.ChildItems.Add(childItem);
AddChildItems(table, childItem, PageInternalLink);
}
}
HTML
<script type="text/javascript">
$(document).ready(function () {
$('#<%=Menu1.ClientID %> ul', '#<%=Menu1.ClientID %> li').removeClass();
});
$(document).ready(function () {
$("#dMenuContainer").delay(500).fadeIn(500);
$('#smoothmenu1 ul, #smoothmenu1 li').removeClass();
});
ddsmoothmenu.init({
mainmenuid: "<%=Menu1.ClientID %>", //menu DIV id
orientation: 'h', //Horizontal or vertical menu: Set to "h" or "v"
classname: 'ddsmoothmenu', //class added to menu's outer DIV
customtheme: ["", ""], //78AC1B
contentsource: "markup" //"markup" or ["container_id", "path_to_menu_file"]
})
</script>
...
<table align="center" cellpadding="0" cellspacing="0" align="center" >
<tr>
<td align="center" valign="middle"><div id="smoothmenu1" class="ddsmoothmenu">
<asp:Menu ID="Menu1" runat="server" Orientation="Horizontal" CssClass="ddsmoothmenu" IncludeStyleBlock="False" DisappearAfter="500" >
</asp:Menu>
</div>
</td>
</tr>
</table>
Вы должны отсортировать данные по ParentID, а затем выполнить циклическую таблицу, и для каждого элемента найдите родительский элемент и добавьте его в его ChildItems, если вы не нашли родительский элемент для добавления в корневой каталог.
получить данные:
string sql = "Select * from MenuItems Order By ParentID";
DataTable data = GetTable(sql);
а затем заполните меню:
foreach (DataRow rw in data.Rows)
{
IEnumerable<MenuItem> menuItems = Extensions.GetItems<MenuItem>(menuBar.Items, item => item.ChildItems);
MenuItem parent = menuItems.FirstOrDefault(mi => mi.Value == rw.Field<int>("ParentID").ToString());
MenuItem newItem = new MenuItem(rw.Field<string>("MenuName"), rw.Field<int>("MenuID").ToString());
if (parent == null)
menuBar.Items.Add(newItem);
else
parent.ChildItems.Add(newItem);
}
а вот метод GetItems, который будет возвращать все узлы дерева, взятый отсюда: /questions/10703044/perechislenie-kollektsij-kotoryie-po-svoej-suti-ne-yavlyayutsya-ienumerable/10703081#10703081
public static class Extensions
{
public static IEnumerable<T> GetItems<T>(this IEnumerable collection, Func<T, IEnumerable> selector)
{
Stack<IEnumerable<T>> stack = new Stack<IEnumerable<T>>();
stack.Push(collection.OfType<T>());
while (stack.Count > 0)
{
IEnumerable<T> items = stack.Pop();
foreach (var item in items)
{
yield return item;
IEnumerable<T> children = selector(item).OfType<T>();
stack.Push(children);
}
}
}
}