最近在一个项目中需要用到无限分类的平铺多选,单选这些功能,查了一些资料,结果大都是一些用IFrame这样的东西做的,虽然用起来直观,但本人更喜欢集成控件形式的,于是抽了一些时间做了一个.思路是利用控件+JS+不同的无限分类表,支持一页多控件,支持不同的无限分类表.效果图如下:
当这些父类被选择时,子类都被选择.当这些父类取消选择时,其下所有子类都被取消选择.
代码如下:
控件behind代码CS:
MultiSelectItems.ascx.cs
1 using System;
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 + "' onclick=\"" + 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代码很简单:
MultiSelectItems.ascx
<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="MultiSelectItems.ascx.cs" Inherits="MultiSelectItems" %>
<div id="MultiItems<%=tableName%>"><%=multiItemsInnerHtml%></div>
<input type="hidden" name="<%=controlName%>" id="<%=controlName%>" value="" />
<script language="javascript" type="text/javascript">
<%=scriptStr%>
</script>
接下来是相关的JS,注意,不论一个页面调用几次这个控件,此JS只加载一次
MultiSelectItems.js
function checkAllSubProperty(tableName,groupid,checkedsControlName){
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人要