最近在一个项目中需要用到无限分类的平铺多选,单选这些功能,查了一些资料,结果大都是一些用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人要