今天晚上用了两种方法,两种方法都是一次读取完数据的,测试使用的表结果是这样的:
deptid int、dna varchar(100)、parentid int、parentPath varchar(300)
分别记录自增字段的值、描述、父级编号、父级的路径(根节点的路径是“,0,”,每个路径都以“,”分隔,以“,”结尾)
第一种方法是使用js形成tree,是以前用asp的时候使用的方法,我就是把asp语句转成了C#来写
这种方法的源码是这个:UploadFiles/2007-3/324998101.rar
调用很简单:
<uc1:nnllTreeView ID="NnllTreeView1" runat="server" IDField="deptid" NeedCheckBox="false"
NeedRadioBox="true" ParentField="parentid" TextField="dna" JSUrl="../myuo" ImgPath="../images/TreeImages/" TreeSelectCommand="select deptid,parentid,dna from T_sys_dept order by orders" />
这个方法的好处真的是无限级的树的,因为在这里我们不需要用到父级所有路径,有父级的编号就可以了
还有一个好处是容易用,因为要设置的东西很少,没有TreeView的属性这么多,一堆的东西,如果我不喜欢那些图片,我把UploadFiles/2007-3/324537444.rar里的所有图片替换,就可以换一堆图片了
这个方法的坏处是每次提交了数据回服务器,都要执行一次js里面初始化树的函数,就是我设置了!IsPostBack可以做到不会每次读取数据,就是还是每次都重新初始化整个树,因为这里的js分两个,一个是读取数据库数据,写一个脚本,另一个是执行脚本里的函数,初始化树
我试着在js里设置一个全局开关变量,当树已经第一次初始化了,就不要再重新初始化,结果是当我点了一个服务器端的Button时,整个页面变成空白了,因为返回服务器的时候,是整个页面返回的,运行过的那个读数据的js,被.net重新写回,就是没有重新调用
这个还要再想办法
第二个方法是用TreeView控件,asp.net 2.0里有这个控件,我一开始想啊想,老想着不要碰递归,所以搞了好久,后来L叔叔告诉我有FindNode方法,就变成好简单的东西了
主要是用了三个方法:
tv.Nodes.Add(new TreeNode(textValue, idValue)); //加一个节点
tn.ChildNodes.Add(new TreeNode(textValue, idValue)); //加一个子节点
tn = tv.FindNode(pathValue); 查找父级路径是pathValue的节点
我写成一个方法了:
public bool setTreeViewNode(TreeView tv, string idField, string textField, string pathField, string queryString)
{
bool ok = false;
string idValue, textValue, pathValue;
//获取连接字符串
string connectionString = ConfigurationManager.ConnectionStrings["TTOAConnectionString"].ConnectionString;
//创建并设置SqlConnection
SqlConnection dbConnection = new SqlConnection(connectionString);
try
{
//取数据
dbConnection.Open();
SqlCommand MyCommand = new SqlCommand(queryString, dbConnection);
SqlDataReader dr = MyCommand.ExecuteReader();
TreeNode tn = new TreeNode();
//循环写数据
while (dr.Read())
{
idValue = dr[idField].ToString();
textValue = dr[textField].ToString();
pathValue = dr[pathField].ToString();
//把根节点的路径
pathValue = pathValue.Replace(",0,", "");
//Response.Write("<br>" + idValue.ToString() + ":" + pathValue);
if (pathValue.Length == 0)
{
tv.Nodes.Add(new TreeNode(textValue, idValue));
}
else
{
//去掉最后的,
pathValue = pathValue.Remove(pathValue.Length - 1);
tn = tv.FindNode(pathValue);
if (tn != null)
{
tn.ChildNodes.Add(new TreeNode(textValue, idValue));
}
}
}
ok = true;
}
catch (Exception)// e)
{
//Response.Write(e.Message + "<br>" + e.Source + "<br>" + e.TargetSite.Name);
ok = false;
}
finally
{
dbConnection.Close();
}
return ok;
}
这个方法的好处是他是asp.net 2.0自带的控件,所以他自己解决了回发的问题
坏处就是这种方法要使用父级的所有路径,所以不是无限的树,他的级数是被父级路径parentPath varchar(300)控制的,我这里设置了varchar(300),如果很深的树,路径的字符串超过了300,那这里就取不出了
想了一下,如果是不使用parentPath,那就不是不使用FindNode方法,就要用递归了,对不对?如果我不想用递归,我就要用一个DataTable,把每一个记录的parentid和deptid还有生成的节点的索引记录下来,然后呢,每生成一个TreeNode,我就要把这个节点的索引写入那个DataTable里,每准备生成一个TreeNode,我就要先在那个DataTable里检索他的父级对应的索引,我知道我可以对DataTable和生成树的dbCommand使用一样的字段排序,这样可以记录下当前生成到第几行,可以在回填索引值的时候不循环,就是在查找索引值的时候呢?怎么才可以做到不循环DataTable呢?
是使用递归快,还是使用循环DataTable取父级的索引快?在pb里面,我们是使用递归的,要在内存里多记录一个DataTable,资源也占用更多吧?
我现在还缺的是想要设置TreeView的文字不可点击,TreeView的文字默认是连接来的,想设置每个节点前面都有CheckBox允许多选,这个可以使用ShowCheckBoxes属性,想设置每个节点前面都有RadioBox允许单选,这个还没找到方法,嗯,想过可以利用js控制CheckBox,就是还想试一下别的方法
还有就是这里要有一个属性,PathSeparator,他是记录父级路径的分隔字符的,默认是“\”,我对应自己的数据,改成“,”了
睡觉,先记下这些东东,我要睡觉了
记下这段刚刚写好的js,可以修改底色,形成点击一个input,就把包含这个input在内的td整个底色换成灰
就是我现在不知道怎么把这个js加到每一个checkbox里,我可以用TreeView1.CheckedNodes访问已经通过checkbox选择的项,就是没有办法取到每一个checkbox
<html>
<head>
<title>test</title>
</head>
<body>
<script language="JavaScript">
function checkColor(obj)
{
obj=obj.parentElement;
if(obj.style.backgroundColor=="Gainsboro")
{
obj.style.backgroundColor="White";
}
else
{
obj.style.backgroundColor="Gainsboro";
}
}
</script>
<base target="_blank" onclick="JavaScript:checkColor()">
<table border="1" width="500" align="center">
<tr>
<td id="td1">
<input type="checkbox" name="c1" value="1" onclick="checkColor(this);">aaa
</td>
</tr>
<tr>
<td id="td2">
<input type="checkbox" name="c2" value="1" onclick="checkColor(this);">bbb
</td>
</tr>
<tr>
<td id="td3">
<input type="checkbox" name="c3" value="1" onclick="checkColor(this);">ccc
</td>
</tr>
</table>
</body>
</html>
使用event.srcElement就可以不用给每一个checkbox添加客户端脚本了,就像以前做的要点击了广告才可以下载东西一样
<script type="text/javascript">
function checkColor()
{
var obj = window.event.srcElement;
if(obj.tagName=="INPUT" && obj.type=="checkbox")
{
var cb=obj;
var td=cb.parentElement;
if(cb.checked)
{
td.style.backgroundColor="Gainsboro";
td.style.color="blue";
}
else
{
td.style.backgroundColor="White";
td.style.color="black";
}
}
}
</script>
设置<asp:TreeView onclick="checkColor();">就可以生成<div onclick="checkColor();">
现在还差的问题是,TreeView的文字,怎么去掉连接?因为点击文字不可以同时选择那个checkbox,我不想要这个文字连接
解决点击文字和checkbox不同步的问题:
protected void TreeView1_SelectedNodeChanged(object sender, EventArgs e)
{
if (showCheckbox)
{
TreeView1.SelectedNode.Checked = true;
}
}
节点的SelectAction属性有四个值:
TreeNodeSelectAction.Select:点击节点就会选中这个节点
TreeNodeSelectAction.Expand:点击节点会展开这个节点
TreeNodeSelectAction.SelectExpand:点击节点会选中这个节点,还会展开这个节点的
TreeNodeSelectAction.None:没有操作,所以节点会就以文本的方式显示,不会显示成连接,点击时不会执行任何js