MVC树控件,mvc中应用treeview,实现复选框树的多层级表单控件
类似于多层级的角色与权限控制功能,用MVC实现MVC树控件,mvc中应用treeview,实现复选框树的多层级表单控件。最近我们的项目中需要用到树型菜单,以前使用WebForm时,树型菜单有微软提供的控件,非常方便,但现在需要在asp.netmvc中使用树形菜单,先说明下我们对树形菜单的需求:
1,支持CheckBox,允许对菜单项进行选择;
2,当选择父菜单时,它下面的子菜单全部选中;
3,当取消父菜单的选中状态时,下面的子菜单也全部取消;
4,比较方便的与MVC结合;
5,能够初始化选中状态。
6,能够以提交表单的方式,一次性将树绑定到后台接收的对象。
首先菜单数据对象:
完整的 CheckboxTreeHelper.cs 代码:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Web; 5 using System.Web.Mvc; 6 using System.Web.Mvc.Html; 7 using System.Text; 8 9 /* 10 author:熊学浩 11 time:2016年6月5日 12 description:树形菜单控件(有问题请致信:xiongzaiqiren@163.com),或者请参考【http://www.cnblogs.com/xiongzaiqiren/】 13 */ 14 namespace System.Web.Mvc 15 { 16 /// <summary> 17 /// 菜单数据对象 18 /// </summary> 19 public class CheckboxTreeItem 20 { 21 /// <summary> 22 /// 显示的文本 23 /// </summary> 24 public string Text { get; set; } 25 /// <summary> 26 /// 显示文本对应的值 27 /// </summary> 28 public string Value { get; set; } 29 /// <summary> 30 /// 是否被选中 31 /// </summary> 32 public bool Checked { get; set; } 33 /// <summary> 34 /// 结合表单的辅助属性,View上使用(注意每个层级的索引必需从0开始并且是连续的) 35 /// </summary> 36 public string Index { get; set; } 37 38 /// <summary> 39 /// 子菜单集合 40 /// </summary> 41 public IList<CheckboxTreeItem> Items { get; set; } 42 } 43 44 /// <summary> 45 /// 复选框树控件 46 /// 熊学浩 47 /// 2016年6月3日 48 /// </summary> 49 public static class CheckboxTreeHelper 50 { 51 private static bool baselayer; 52 53 public static String CheckboxTree(this HtmlHelper helper, string Name, CheckboxTreeItem Model, out string this_HtmlDomName, bool isChildNode = false) 54 { 55 if (null == Model) 56 { 57 this_HtmlDomName = string.Empty; 58 return string.Empty; 59 } 60 61 StringBuilder sb = new StringBuilder(); 62 if (!baselayer) 63 sb.Append("<div class=\"CheckboxTree\">"); 64 65 if (!isChildNode) 66 sb.Append("<ul class=\"CBT-ul\">"); 67 sb.Append("<li class=\"CBT-li\">"); 68 69 this_HtmlDomName = Name + "[" + Model.Index + "]"; 70 71 sb.Append("<label style=\"cursor:pointer;\">"); 72 sb.AppendFormat("<input type=\"checkbox\" onchange=\"setCheckboxTreeItemValue(this);\" name=\"{0}\" value=\"{1}\" />", (this_HtmlDomName + ".Checked"), Model.Checked.ToString()); 73 sb.AppendFormat("<span>{0}</span>", Model.Text); 74 sb.Append("</label>"); 75 76 sb.AppendFormat("<input type=\"hidden\" name =\"{0}\" value=\"{1}\" />", (this_HtmlDomName + ".Text"), Model.Text); 77 sb.AppendFormat("<input type=\"hidden\" name =\"{0}\" value=\"{1}\" />", (this_HtmlDomName + ".Value"), Model.Value); 78 sb.AppendFormat("<input type=\"hidden\" name =\"{0}\" value=\"{1}\" />", (this_HtmlDomName + ".Index"), Model.Index); 79 80 if (null != Model.Items) 81 { 82 sb.Append("<ul class=\"CBT-ul\">"); 83 for (var i = 0; i < Model.Items.Count; i++) 84 { 85 string _this_HtmlDomName; 86 sb.Append(CheckboxTree(helper, this_HtmlDomName + ".Items", Model.Items[i], out _this_HtmlDomName, true)); 87 } 88 sb.Append("</ul>"); 89 } 90 91 sb.Append("</li>"); 92 if (!isChildNode) 93 sb.Append("</ul>"); 94 95 if (!baselayer) 96 sb.Append("</div>"); 97 98 return sb.ToString(); 99 } 100 public static String CheckboxTree(this HtmlHelper helper, string Name, IList<CheckboxTreeItem> Models) 101 { 102 if (null == Models) return string.Empty; 103 if (Models.Count > 0) 104 baselayer = true; 105 106 StringBuilder sb = new StringBuilder(); 107 if (baselayer) 108 sb.Append("<div class=\"CheckboxTree\">"); 109 sb.Append("<ul class=\"CBT-ul\">"); 110 111 string _this_HtmlDomName; 112 113 foreach (CheckboxTreeItem item in Models) 114 { 115 sb.Append(CheckboxTree(helper, Name, item, out _this_HtmlDomName, true)); 116 } 117 118 sb.Append("</ul>"); 119 if (baselayer) 120 sb.Append("</div>"); 121 122 return sb.ToString(); 123 } 124 } 125 }
字段说明:
Text:用于显示的文本,比如:总裁
Value:显示文本对应的ID,比如:0
Index:这个是结合表单的辅助属性,View上使用(注意每个层级的索引必需从0开始并且是连续的)
Checked:当前菜单项是否被选中,用户提交表单后我们可以通过这个属性判断用户的选择项
Items:当前菜单下的子菜单集合
树形菜单的输出:
1 @model List<CheckboxTreeItem> 2 3 4 @using (Html.BeginForm("test", "Home", new { }, FormMethod.Post, new { id = "form", name = "form" })) 5 { 6 @Html.AntiForgeryToken(); 7 8 <link type="text/css" rel="stylesheet" href="@Url.Content("~/Models/CheckboxTree.css")" /> 9 @Html.Raw(Html.CheckboxTree("CheckboxTree", Model)) @*这里是输出显示树形菜单*@ 10 <script src="@Url.Content("~/Models/CheckboxTree.js")" type="text/javascript"></script> 11 }
无论是ajax还是直接post表单,最终的目的都是接收View中的数据,要想传递比较复杂的数据类型,我们需要对ASP.NET MVC Model Binding 有一定了解,之前也讲解过MyTreeViewItem的结果,有普通的数据类型,比如 string,bool,也是对象类型,比如MyTreeViewItem类型的Parent,也是基于集合的属性IList<MyTreeViewItem>,要想让表单中的数据直接传递给Controller,我们需要对表单元素的name进行特殊处理才行。
Controller:这是最重要的就是接收参数,它是一个List类型,无论菜单项有多少层,都会按树形数据结构层次组织好,方便我们查询。
1 public ActionResult test(int id = 0) 2 { 3 ViewBag.IsEdit = id == 0 ? false : true; 4 5 List<CheckboxTreeItem> cb = new List<CheckboxTreeItem>() { 6 new CheckboxTreeItem() { Value="A", Text="一级A", Index="0" }, 7 new CheckboxTreeItem() { Value="B", Text="一级B", Index="1" }, 8 new CheckboxTreeItem() { Value="C", Text="一级C", Index="2", Checked =true, 9 Items=new List<CheckboxTreeItem>() { 10 new CheckboxTreeItem() { Value="CA", Text="二级CA", Index="0", Checked =true }, 11 new CheckboxTreeItem() { Value="CB", Text="二级CB", Index="1", Checked =true, 12 Items=new List<CheckboxTreeItem>() { 13 new CheckboxTreeItem() { Value="CBA", Text="三级CBA", Index="0", Checked =true }, 14 new CheckboxTreeItem() { Value="CBB", Text="三级CBB", Index="1", Checked =true }, 15 } 16 }, 17 } 18 }, 19 }; 20 21 return View(cb); 22 } 23 24 [HttpPost] 25 [ValidateAntiForgeryToken] 26 [ValidateInput(false)] 27 public ActionResult test(List<CheckboxTreeItem> CheckboxTree, int id = 0) 28 { 29 ViewBag.IsEdit = id == 0 ? false : true; 30 31 return View(CheckboxTree); 32 }
下图是Controller接收到的参数:(运行调试,断点查看第27-31行代码)
另外还有自定义样式文件 CheckboxTree.css
1 /* 2 author:熊学浩 3 time:2016年6月5日 4 description:树形菜单控件(有问题请致信:xiongzaiqiren@163.com),或者请参考【http://www.cnblogs.com/xiongzaiqiren/】 5 */ 6 /* CheckboxTree */ 7 .CheckboxTree { 8 width: 100%; 9 height: 100%; 10 text-align: center; 11 margin: 0 auto; 12 } 13 14 .CBT-ul { 15 text-align: left; 16 list-style-type: disc; 17 } 18 19 .CBT-ul .CBT-li { 20 display: list-item; 21 text-align: left; 22 margin-left: 10px; 23 font-size: 16px; 24 line-height: 20px; 25 word-wrap: break-word; 26 word-break: nomal; 27 list-style: inherit; 28 } 29 .CBT-li label { 30 cursor: pointer; 31 } 32 .CBT-li label input[type="checkbox"] { 33 width: 16px; 34 height: 16px; 35 padding: 0 5px 0 0; 36 } 37 38 .CBT-li span { 39 40 } 41 42 .CBT-li a:link, .CBT-li a:visited { 43 } 44 45 .CBT-li a:hover, .CBT-li a:active { 46 color: #ffffff; 47 background-color: #657386; 48 } 49 50 .CBT-li a.active { 51 color: #ffffff; 52 background-color: #657386; 53 } 54 55 .CBT-li a span { 56 57 }
用于初始化和设置选择状态改变的自定义javascript文件 CheckboxTree.js
1 /* 2 author:熊学浩 3 time:2016年6月5日 4 description:树形菜单控件(有问题请致信:xiongzaiqiren@163.com),或者请参考【http://www.cnblogs.com/xiongzaiqiren/】 5 */ 6 /* CheckboxTree */ 7 function setCheckboxTreeItemValue(dom) { 8 try { 9 if (!!dom) 10 dom.value = (!!dom.checked) 11 } 12 catch (e) { 13 console.log(e.message); 14 } 15 }; 16 function iniCheckboxTree() { 17 try { 18 var cbList = document.getElementsByTagName("input"); 19 if (!!cbList && cbList.length > 0) { 20 for (var i = 0; i < cbList.length; i++) { 21 if (!!cbList[i] && (cbList[i].type == "checkbox")) { 22 if (!!cbList[i].value) { 23 cbList[i].checked = (cbList[i].value.toLowerCase() == "true") || false; 24 } 25 } 26 } 27 } 28 } 29 catch (e) { 30 console.log(e.message); 31 } 32 }; 33 34 if (!!window) 35 window.onload = iniCheckboxTree(); 36 else 37 iniCheckboxTree();
最后,UI效果图:
【完】