新手求教:如何从带分类与子分类的SQL循环生成PopupMenu并添加指定条目
需求说明
从包含分类、子分类的SQL层级数据(带Level_Depth字段标记层级)中循环生成PopupMenu,并为所有Level_Depth=3的菜单节点追加两个固定条目。
第一步:准备SQL表与测试数据
先创建存储菜单层级的表,并插入测试数据:
-- 创建菜单分类表 CREATE TABLE MenuCategories ( ID INT PRIMARY KEY, ParentID INT NULL, -- 父分类ID,根节点为NULL或0 Name NVARCHAR(100) NOT NULL, Level_Depth INT NOT NULL -- 层级深度,根节点为1 ); -- 插入测试数据 INSERT INTO MenuCategories VALUES (1, NULL, '主分类1', 1), (2, 1, '子分类1-1', 2), (3, 2, '孙分类1-1-1', 3), (4, 1, '子分类1-2', 2), (5, 4, '孙分类1-2-1', 3), (6, NULL, '主分类2', 1), (7, 6, '子分类2-1', 2), (8, 7, '孙分类2-1-1', 3);
第二步:桌面端(C# WinForms)实现示例
以下代码通过WinForms的ContextMenuStrip实现PopupMenu,从SQL读取数据并构建层级菜单,最后为三级节点添加固定条目:
using System; using System.Collections.Generic; using System.Data; using System.Data.SqlClient; using System.Windows.Forms; namespace PopupMenuDemo { public partial class MainForm : Form { // 替换为你的数据库连接字符串 private const string ConnectionString = "Data Source=YOUR_SERVER;Initial Catalog=YOUR_DB;Integrated Security=True"; public MainForm() { InitializeComponent(); LoadPopupMenu(); } private void LoadPopupMenu() { // 1. 从SQL获取菜单数据 DataTable menuTable = GetMenuData(); // 2. 存储所有菜单项,方便快速查找父节点 Dictionary<int, ToolStripMenuItem> menuItemDict = new Dictionary<int, ToolStripMenuItem>(); // 先创建所有菜单项 foreach (DataRow row in menuTable.Rows) { int id = Convert.ToInt32(row["ID"]); string menuName = row["Name"].ToString(); ToolStripMenuItem item = new ToolStripMenuItem(menuName); item.Tag = id; // 存储ID用于后续逻辑 menuItemDict.Add(id, item); } // 3. 构建菜单父子层级 foreach (DataRow row in menuTable.Rows) { int id = Convert.ToInt32(row["ID"]); int? parentId = row["ParentID"] != DBNull.Value ? (int?)Convert.ToInt32(row["ParentID"]) : null; if (parentId.HasValue && menuItemDict.ContainsKey(parentId.Value)) { // 子节点添加到父节点的下拉列表 menuItemDict[parentId.Value].DropDownItems.Add(menuItemDict[id]); } else { // 根节点直接添加到主PopupMenu contextMenuStrip1.Items.Add(menuItemDict[id]); } } // 4. 为所有Level_Depth=3的节点添加固定条目 foreach (DataRow row in menuTable.Rows) { int level = Convert.ToInt32(row["Level_Depth"]); if (level == 3) { int id = Convert.ToInt32(row["ID"]); if (menuItemDict.ContainsKey(id)) { // 添加分隔线 menuItemDict[id].DropDownItems.Add(new ToolStripSeparator()); // 添加固定条目并绑定点击事件 menuItemDict[id].DropDownItems.Add("固定条目1", null, FixedItem_Click); menuItemDict[id].DropDownItems.Add("固定条目2", null, FixedItem_Click); } } } } // 从SQL读取菜单数据 private DataTable GetMenuData() { DataTable dt = new DataTable(); using (SqlConnection conn = new SqlConnection(ConnectionString)) { string sql = "SELECT ID, ParentID, Name, Level_Depth FROM MenuCategories ORDER BY Level_Depth, ID"; SqlCommand cmd = new SqlCommand(sql, conn); SqlDataAdapter adapter = new SqlDataAdapter(cmd); adapter.Fill(dt); } return dt; } // 固定条目的点击事件 private void FixedItem_Click(object sender, EventArgs e) { ToolStripMenuItem clickedItem = sender as ToolStripMenuItem; MessageBox.Show($"你点击了:{clickedItem.Text}"); } } }
第三步:Web端(Bootstrap Dropdown)实现示例
如果是Web场景,用Bootstrap的下拉菜单组件实现类似功能:
<!-- HTML容器 --> <div class="dropdown"> <button class="btn btn-secondary dropdown-toggle" type="button" id="mainMenuBtn" data-bs-toggle="dropdown" aria-expanded="false"> 弹出菜单 </button> <ul class="dropdown-menu" aria-labelledby="mainMenuBtn" id="menuContainer"> </ul> </div> <script> // 模拟从后端接口获取的SQL菜单数据(实际项目用AJAX请求) const menuData = [ { ID: 1, ParentID: null, Name: '主分类1', Level_Depth: 1 }, { ID: 2, ParentID: 1, Name: '子分类1-1', Level_Depth: 2 }, { ID: 3, ParentID: 2, Name: '孙分类1-1-1', Level_Depth: 3 }, { ID: 4, ParentID: 1, Name: '子分类1-2', Level_Depth: 2 }, { ID: 5, ParentID: 4, Name: '孙分类1-2-1', Level_Depth: 3 }, { ID: 6, ParentID: null, Name: '主分类2', Level_Depth: 1 }, { ID: 7, ParentID: 6, Name: '子分类2-1', Level_Depth: 2 }, { ID: 8, ParentID: 7, Name: '孙分类2-1-1', Level_Depth: 3 } ]; function buildPopupMenu() { const menuContainer = document.getElementById('menuContainer'); const menuItemMap = {}; // 1. 创建所有菜单项 menuData.forEach(item => { const li = document.createElement('li'); const link = document.createElement('a'); link.className = 'dropdown-item'; link.href = '#'; link.textContent = item.Name; if (item.Level_Depth !== 3) { // 非三级节点作为下拉菜单容器 link.classList.add('dropdown-toggle'); link.setAttribute('data-bs-toggle', 'dropdown'); link.setAttribute('aria-expanded', 'false'); const subMenu = document.createElement('ul'); subMenu.className = 'dropdown-menu'; li.appendChild(link); li.appendChild(subMenu); } else { // 三级节点直接作为可点击项 li.appendChild(link); } menuItemMap[item.ID] = li; }); // 2. 构建父子层级关系 menuData.forEach(item => { if (item.ParentID !== null) { const parentLi = menuItemMap[item.ParentID]; const subMenu = parentLi.querySelector('.dropdown-menu'); subMenu.appendChild(menuItemMap[item.ID]); } else { menuContainer.appendChild(menuItemMap[item.ID]); } }); // 3. 为三级节点添加固定条目 menuData.forEach(item => { if (item.Level_Depth === 3) { const li = menuItemMap[item.ID]; // 添加分隔线 const divider = document.createElement('li'); divider.className = 'dropdown-divider'; li.appendChild(divider); // 添加固定条目1 const fixedItem1 = createFixedMenuItem('固定条目1'); li.appendChild(fixedItem1); // 添加固定条目2 const fixedItem2 = createFixedMenuItem('固定条目2'); li.appendChild(fixedItem2); } }); } // 创建固定菜单项的工具函数 function createFixedMenuItem(text) { const link = document.createElement('a'); link.className = 'dropdown-item'; link.href = '#'; link.textContent = text; link.addEventListener('click', () => alert(`点击了:${text}`)); const li = document.createElement('li'); li.appendChild(link); return li; } // 页面加载完成后构建菜单 document.addEventListener('DOMContentLoaded', buildPopupMenu); </script>
内容的提问来源于stack exchange,提问作者Ricky Rock
相关产品推荐
相关产品推荐

