今天在用Microsoft.Web.UI.WebControls.TreeView中发现了这个bug。
递归过程是加载目录树,用递归的方法一层层加载目录。
我创建一个DataTable来递归:
using System;
using Microsoft.Web.UI.WebControls;
using System.Data;
namespace TestWeb
{
/// <summary>
/// Chapters 的摘要说明。
/// </summary>
public class Chapters
{
private DataTable m_chapters;
public Chapters()
{
//
// TODO: 在此处添加构造函数逻辑
//
m_chapters = LoadData();
}
public DataTable LoadData()
{
DataTable dt = new DataTable();
dt.Columns.Add(new DataColumn("ChapterID",typeof(System.Int32)));
dt.Columns.Add(new DataColumn("ChapterName",typeof(System.String)));
dt.Columns.Add(new DataColumn("ParentChapter",typeof(System.Int32)));
DataRow dr = dt.NewRow();
dr["ChapterID"] = "1";
dr["ChapterName"] = "root";
dr["ParentChapter"] = "0";
dt.Rows.Add(dr);
dr = dt.NewRow();
dr["ChapterID"] = "2";
dr["ChapterName"] = "chapter1";
dr["ParentChapter"] = "1";
dt.Rows.Add(dr);
dr = dt.NewRow();
dr["ChapterID"] = "3";
dr["ChapterName"] = "chapter2";
dr["ParentChapter"] = "1";
dt.Rows.Add(dr);
return dt;
}
/// <summary>
/// 获取目录结构
/// </summary>
/// <param name="nodes">TreeView的节点集</param>
public void InitTree(TreeNodeCollection nodes)
{
//获取根结点ID
int rootChapterID = 1;
//获取根接点数据
TreeNode rootNode = new TreeNode();
rootNode.Text = "Root";
rootNode.ID = "1";
nodes.Add(rootNode);
InitTree(nodes[0].Nodes,rootChapterID);
}
/// <summary>
/// 获取目录结构
/// </summary>
/// <param name="nodes">TreeView的节点集</param>
/// <param name="parentNodeId">相对父节点Id</param>
private void InitTree(TreeNodeCollection nodes,int parentNodeId)
{
DataView dvTree = m_chapters.DefaultView;
dvTree.RowFilter = "ParentChapter = " + parentNodeId;
if(dvTree.Count==0)
return;
TreeNode tnChild;
for (int i = 0; i < dvTree.Count; i++)
{
DataRowView drv = dvTree[i];
tnChild = new TreeNode();
tnChild.Text = drv["ChapterName"].ToString();
tnChild.ID = drv["ChapterID"].ToString();
nodes.Add(tnChild);
int iParentId = Convert.ToInt32(drv["ChapterID"]);
InitTree(nodes[i].Nodes, iParentId);
}
}
}
}
using Microsoft.Web.UI.WebControls;
using System.Data;
namespace TestWeb
{
/// <summary>
/// Chapters 的摘要说明。
/// </summary>
public class Chapters
{
private DataTable m_chapters;
public Chapters()
{
//
// TODO: 在此处添加构造函数逻辑
//
m_chapters = LoadData();
}
public DataTable LoadData()
{
DataTable dt = new DataTable();
dt.Columns.Add(new DataColumn("ChapterID",typeof(System.Int32)));
dt.Columns.Add(new DataColumn("ChapterName",typeof(System.String)));
dt.Columns.Add(new DataColumn("ParentChapter",typeof(System.Int32)));
DataRow dr = dt.NewRow();
dr["ChapterID"] = "1";
dr["ChapterName"] = "root";
dr["ParentChapter"] = "0";
dt.Rows.Add(dr);
dr = dt.NewRow();
dr["ChapterID"] = "2";
dr["ChapterName"] = "chapter1";
dr["ParentChapter"] = "1";
dt.Rows.Add(dr);
dr = dt.NewRow();
dr["ChapterID"] = "3";
dr["ChapterName"] = "chapter2";
dr["ParentChapter"] = "1";
dt.Rows.Add(dr);
return dt;
}
/// <summary>
/// 获取目录结构
/// </summary>
/// <param name="nodes">TreeView的节点集</param>
public void InitTree(TreeNodeCollection nodes)
{
//获取根结点ID
int rootChapterID = 1;
//获取根接点数据
TreeNode rootNode = new TreeNode();
rootNode.Text = "Root";
rootNode.ID = "1";
nodes.Add(rootNode);
InitTree(nodes[0].Nodes,rootChapterID);
}
/// <summary>
/// 获取目录结构
/// </summary>
/// <param name="nodes">TreeView的节点集</param>
/// <param name="parentNodeId">相对父节点Id</param>
private void InitTree(TreeNodeCollection nodes,int parentNodeId)
{
DataView dvTree = m_chapters.DefaultView;
dvTree.RowFilter = "ParentChapter = " + parentNodeId;
if(dvTree.Count==0)
return;
TreeNode tnChild;
for (int i = 0; i < dvTree.Count; i++)
{
DataRowView drv = dvTree[i];
tnChild = new TreeNode();
tnChild.Text = drv["ChapterName"].ToString();
tnChild.ID = drv["ChapterID"].ToString();
nodes.Add(tnChild);
int iParentId = Convert.ToInt32(drv["ChapterID"]);
InitTree(nodes[i].Nodes, iParentId);
}
}
}
}
我跟踪调试发现如下过程:
1、首先加载parentNodeID=1的所有结点,包括2个结点,其ChapterID分别为2,3。
2、可见事实上也是Count=2,如下图
3、递归第一次parentNodeID=2。
4、验证parentNodeID=2,也正确。
5、当第一次递归完成了,按道理要递归下一个parentNodeID=3了。
6、这时候确实也回到了上一级,也就是parentNodeId=1的时候。这里i的值是还没有++,++后i为1。
7、这时候bug出现了,dvTree.Count应该和第2步一样为2,可这里怎么就成了0了呢?
我猜,在实现编译器的时候,应该把dvTree保存为临时的DataView,这里把递归第一次的dvTree拿来用了。每一次递归都必须保存前一级别的DataView。事实上,却没有,DataView被丢了。上一级别的DataView和自身一级别的DataView原本就是不同的。
附上代码: TestWeb.rar