最近在一个项目中需要用到无限分类的平铺多选,单选这些功能,查了一些资料,结果大都是一些用IFrame这样的东西做的,虽然用起来直观,但本人更喜欢集成控件形式的,于是抽了一些时间做了一个.思路是利用控件+JS+不同的无限分类表,支持一页多控件,支持不同的无限分类表.效果图如下:
当这些父类被选择时,子类都被选择.当这些父类取消选择时,其下所有子类都被取消选择.
代码如下:
控件behind代码CS:
2 using System.Collections;
3 using System.Collections.Generic;
4 using System.Configuration;
5 using System.Data;
6 using System.Linq;
7 using System.Web;
8 using System.Web.Security;
9 using System.Web.UI;
10 using System.Web.UI.HtmlControls;
11 using System.Web.UI.WebControls;
12 using System.Web.UI.WebControls.WebParts;
13 using System.Xml.Linq;
14 using Models;
15
16 public partial class MultiSelectItems : System.Web.UI.UserControl
17 {
18 public List < Groups > groupList { get ; set ; }
19
20 public string hasSel
21 {
22 set
23 {
24 if ( ! string .IsNullOrEmpty(value))
25 {
26 // 绑定已选数据
27 value = value.Trim( ' , ' );
28 string [] hasSels = value.Split( ' , ' );
29 foreach ( string s in hasSels)
30 {
31 scriptStr += " document.getElementById('checkItems " + tableName + s + " ').checked=true;\r\n " ;
32 scriptStr += " checkProperty(' " + tableName + " ', " + s + " ,' " + controlName + " ');\r\n " ;
33 }
34 scriptStr += " document.getElementById('checkeds " + tableName + " ').value=' " + value + " ,';\r\n " ;
35 }
36 }
37 }
38 public string tableName { get ; set ; }
39 public string controlName { get ; set ; }
40 public string scriptStr = string .Empty;
41 public string multiItemsInnerHtml = string .Empty;
42 protected void Page_Load( object sender, EventArgs e)
43 {
44 // 此控件配合JS使用
45 // 使用方法:
46 // 在页面顶部加载此控件:<%@ Register Src="~/controls/MultiSelectItems.ascx" TagName="mi" TagPrefix="MultiSelectItem" %>
47 // 在head中加载相应JS:<script language="javascript" src="/controls/MultiSelectItems.js" type="text/javascript"></script>
48 // 在页面相关位置放置此控件:<MultiSelectItem:mi runat="server" ID="miArea" />ID不受限制
49 // 配置一些属性
50 // miArea.st = SysTable.AreaGroups; // 加载哪个表
51 // miArea.controlName = "checkedsAreaGroups"; // 要获取值的text控件名
52 // miArea.BindData(); // 绑定初始数据
53 // 如果是修改选项,可以设置已有选项:miArea.hasSel = sm.Area_Items;
54 // 获取该控件的值,这个名称就是刚刚配置的名称:Request.Form["checkedsAreaGroups"]
55 }
56 /// <summary>
57 /// 绑定初始的多选表单数据,这些数据都是未被选择的,如果要选择数据,请设置该对象相关实例的hasSel属性
58 /// </summary>
59 public void BindData()
60 {
61 string brStr = string .Empty;
62 string clickStr = string .Empty;
63 string tpGroupName = string .Empty;
64 string tpBox = string .Empty;
65 if (groupList != null )
66 {
67 foreach (Groups gp in groupList)
68 {
69 brStr = string .Empty;
70 clickStr = " checkAllSubProperty(' " + tableName + " ',' " + gp.GroupId + " ',' " + controlName + " ') " ;
71 tpBox = " <input type='checkbox' name='checkItems " + tableName + " ' id='checkItems " + tableName + gp.GroupId + " ' value=' " + gp.ParentStr + gp.GroupId + " ' οnclick=\ "" + clickStr + " \ " />\r\n " ;
72 if (gp.Route == 1 ) {
73 tpGroupName = gp.GroupName.Substring( 1 );
74 }
75 else if (gp.Route == 2 )
76 {
77 tpGroupName = gp.GroupName.Substring( 1 );
78 }
79 else {
80 tpGroupName = gp.GroupName.Substring(gp.Route - 1 );
81 }
82
83
84 if (gp.Route == 1 )
85 {
86 brStr = " <br /> " ;
87 multiItemsInnerHtml += brStr + tpBox + " <span style='color:#FF7500;font-weight:bold;'> " + tpGroupName + " </span> " ;
88 }
89 else
90 {
91 if (groupList.Where(c => c.ParentId == gp.GroupId).Count() > 0 )
92 {
93 brStr = " <br /> " ;
94 multiItemsInnerHtml += brStr + tpBox + " <span style='font-weight:bold;'> " + tpGroupName + " </span> " ;
95 }
96 else
97 {
98 multiItemsInnerHtml += tpBox + tpGroupName;
99 }
100 }
101 multiItemsInnerHtml += " " ;
102 multiItemsInnerHtml += brStr;
103 }
104 }
105 }
106 }
html代码很简单:
< div id ="MultiItems<%=tableName%>" > <% = multiItemsInnerHtml %> </ div >
< input type ="hidden" name ="<%=controlName%>" id ="<%=controlName%>" value ="" />
< script language ="javascript" type ="text/javascript" >
<%= scriptStr %>
</ script >
接下来是相关的JS,注意,不论一个页面调用几次这个控件,此JS只加载一次
var checkedControl = document.getElementById( " checkItems " + tableName + groupid);
var items = document.getElementsByName( " checkItems " + tableName);
var checkeds = document.getElementById(checkedsControlName);
if (checkedControl.checked){
checkeds.value += groupid + ' , ' ;
} else {
checkeds.value = checkeds.value.replace(groupid + " , " , "" );
}
for ( var i = 0 ;i < items.length;i ++ ){
if (items.item(i).value.indexOf( ' , ' + groupid + ' , ' ) > - 1 ){
var insertStr = items.item(i).value.substring(items.item(i).value.lastIndexOf( ' , ' ) + 1 ,items.item(i).value.length) + ' , ' ;
if (checkedControl.checked){
items.item(i).checked = true ;
if (checkeds.value.indexOf(insertStr) < 0 ){
checkeds.value += insertStr;
}
} else {
items.item(i).checked = false ;
checkeds.value = checkeds.value.replace(insertStr, "" );
}
}
}
}
因为是基于.net 3.5的Linq做的控件,所以,此控件必须运行在装有3.5类库的机器上,而且,因为无限分类的数据库结构大家都清楚.是这样的:
稍微解释一下各字段含义:
GroupId:这是分类的主ID,自动增加,主键.
GroupName:这是分类的名字.
ParentId:这是父类的ID,
ParentStr:这是从根类--->父类---->父类....--->本类的父类的路径,以0开始,以,结束,例如0,2,10,22,这从算法上讲叫静态冗余字段,用来快速查找某个类的所有子类.例如要查找GroupId为2的所有子类,可以这样写语句:select top xx * from 表名 where ParentStr like '%,2,%',是不是比一般的遍历要快很多?
Route:这是指示该类的路径深度,如果是根类,则为1,如果是1级子类,则为2,依此类推,此字段主要用于快速查找某一级别的所有子类.
至于这个无限分类的维护,大家可以各显其能去优化.目前我的维护是采用缓存+List<>+ORM
我写的东西大都是日常工作中用得比较多的,而且,相对来说,我会比较偷懒,例如,减少计算,减少数据库访问等.而且,本人不喜欢一个大括号里有很多条语句的代码方式.这样的代码比较难维护.如果我看到一个if或者while后接了一个大括号,然后整屏都看不到它的结束符.我会BS这段代码的作者.
f人要