Ref*_*din 14 .net c# treeview recursion winforms
我有一个WinForm TreeView控件,显示CaseNotes的Parent Child关系(我知道这对大多数人来说没有任何意义,但它可以帮助我查看答案).
我有一个我需要显示的CaseNotes的DataTable.父/子被定义为:如果行具有ParentNoteID,则它是该注释的childNode,否则它是rootNode.它也可以是父笔记(但不是rootNode),如果另一行有它的ID,因为它是ParentNoteID.
为了使事情变得复杂(可能是简化),我有以下工作(大多数)代码,它们交替地为节点着色.我手动为树视图创建了一个静态集合,它可以非常正确地为它们着色.现在我需要从我的DataTable动态填充节点.
既然我已经逐个节点地通过树视图,我不应该以某种方式将数据附加到这个过程中吗?也许我需要先构建节点,然后将颜色作为单独的例程,但递归方法仍然适用,对吗?
假设我想为每个节点显示CaseNoteID.这是在DataTable中返回的,并且是唯一的.
foreach (TreeNode rootNode in tvwCaseNotes.Nodes)
{
ColorNodes(rootNode, Color.MediumVioletRed, Color.DodgerBlue);
}
protected void ColorNodes(TreeNode root, Color firstColor, Color secondColor)
{
root.ForeColor = root.Index % 2 == 0 ? firstColor : secondColor;
foreach (TreeNode childNode in root.Nodes)
{
Color nextColor = childNode.ForeColor = childNode.Index % 2 == 0 ? firstColor : secondColor;
if (childNode.Nodes.Count > 0)
{
// alternate colors for the next node
if (nextColor == firstColor)
ColorNodes(childNode, secondColor, firstColor);
else
ColorNodes(childNode, firstColor, secondColor);
}
}
}
Run Code Online (Sandbox Code Playgroud)
到目前为止我的想法/尝试:
public void BuildSummaryView()
{
tvwCaseNotes.Nodes.Clear();
DataTable cNotesForTree = CurrentCaseNote.GetAllCNotes(Program._CurrentPerson.PersonID);
foreach (var cNote in cNotesForTree.Rows)
{
tvwCaseNotes.Nodes.Add(new TreeNode("ContactDate"));
}
FormPaint();
}
Run Code Online (Sandbox Code Playgroud)
显然这是有缺陷的.一个只是一遍又一遍地显示ContactDate.虽然它显示了正确的次数,但我想要ContactDate的值(它是数据库中的一个列,并在DataTable中返回.其次我需要添加ChildNode逻辑.if (node.parentNode = node.CaseNoteID) blah...
所以我在这里找到了这个链接,这让我觉得我需要把我的DataTable变成一个ArrayList.那是对的吗?
好的,多亏了Cerebus,这大部分都在工作.我还有一个问题.我该怎么做 - >
DataTable cNotesForTree = CurrentCaseNote.GetAllCNotes(Program._CurrentPerson.PersonID);
Run Code Online (Sandbox Code Playgroud)
并在此使用我返回的DataTable?我只是替换它 - >
dt = new DataTable("CaseNotes");
dt.Columns.Add("NoteID", typeof(string));
dt.Columns.Add("NoteName", typeof(string));
DataColumn dc = new DataColumn("ParentNoteID", typeof(string));
dc.AllowDBNull = true;
dt.Columns.Add(dc);
// Add sample data.
dt.Rows.Add(new string[] { "1", "One", null });
dt.Rows.Add(new string[] { "2", "Two", "1" });
dt.Rows.Add(new string[] { "3", "Three", "2" });
dt.Rows.Add(new string[] { "4", "Four", null });
dt.Rows.Add(new string[] { "5", "Five", "4" });
dt.Rows.Add(new string[] { "6", "Six", null });
dt.Rows.Add(new string[] { "7", "Seven", null });
dt.Rows.Add(new string[] { "8", "Eight", "7" });
dt.Rows.Add(new string[] { "9", "Nine", "8" });
Run Code Online (Sandbox Code Playgroud)
我认为,我的困惑是,我还需要做Column.Add和Row.Adds吗?DataColumn如何转换为我的真实数据结构?对于非常无知的问题感到抱歉,好消息是我永远不会问两次.
以下是提供运行时错误.
if (nodeList.Find(FindNode) == null)
{
DataRow[] childRows = dt.Select("ParentNoteID = " + dr["NoteID"]);
if (childRows.Length > 0)
{
// Recursively call this function for all childRowsl
TreeNode[] childNodes = RecurseRows(childRows);
// Add all childnodes to this node.
node.Nodes.AddRange(childNodes);
}
// Mark this noteID as dirty (already added).
//doneNotes.Add(noteID);
nodeList.Add(node);
}
Run Code Online (Sandbox Code Playgroud)
错误如下 - > 找不到列[ea8428e4]这是正确NoteID的前8位数字(我必须使用Guid).它应该寻找那个名称的列?因为我使用的是Guid还有其他我需要做的事情吗?我将我的所有引用和你的代码改为Guid ......
Cer*_*rus 12
为了尝试解决这个问题,我创建了一个示例窗体并编写了以下代码.我设想的数据表设计如下:
NoteID NoteName ParentNoteID
"1" "One" null
"2" "Two" "1"
"3" "Three" "2"
"4" "Four" null
...
Run Code Online (Sandbox Code Playgroud)
这应该创建一个树(抱歉,我对ASCII艺术不是很好!):
One
|
——Two
|
————Three
|
Four
Run Code Online (Sandbox Code Playgroud)
伪代码是这样的:
您的方案中的问题源于"外键"引用同一表中的列的事实.这意味着当我们遍历行时,我们必须跟踪哪些行已经被解析.例如,在上表中,匹配第二行和第三行的节点已在第一次完整迭代中添加.因此,我们不能再添加它们.有两种方法可以跟踪这个:
doneNotes
).在添加每个新节点之前,请检查该列表中是否存在noteID.这是更快的方法,通常应该是首选方法.(此方法在下面的代码中注释掉)FindNode
)来搜索添加的节点列表(计算嵌套节点),以查看该列表中是否存在要添加的节点.这是较慢的解决方案,但我有点像复杂的代码!:P好的,这是久经考验的代码(C#2.0):
public partial class TreeViewColor : Form
{
private DataTable dt;
// Alternate way of maintaining a list of nodes that have already been added.
//private List<int> doneNotes;
private static int noteID;
public TreeViewColor()
{
InitializeComponent();
}
private void TreeViewColor_Load(object sender, EventArgs e)
{
CreateData();
CreateNodes();
foreach (TreeNode rootNode in treeView1.Nodes)
{
ColorNodes(rootNode, Color.MediumVioletRed, Color.DodgerBlue);
}
}
private void CreateData()
{
dt = new DataTable("CaseNotes");
dt.Columns.Add("NoteID", typeof(string));
dt.Columns.Add("NoteName", typeof(string));
DataColumn dc = new DataColumn("ParentNoteID", typeof(string));
dc.AllowDBNull = true;
dt.Columns.Add(dc);
// Add sample data.
dt.Rows.Add(new string[] { "1", "One", null });
dt.Rows.Add(new string[] { "2", "Two", "1" });
dt.Rows.Add(new string[] { "3", "Three", "2" });
dt.Rows.Add(new string[] { "4", "Four", null });
dt.Rows.Add(new string[] { "5", "Five", "4" });
dt.Rows.Add(new string[] { "6", "Six", null });
dt.Rows.Add(new string[] { "7", "Seven", null });
dt.Rows.Add(new string[] { "8", "Eight", "7" });
dt.Rows.Add(new string[] { "9", "Nine", "8" });
}
private void CreateNodes()
{
DataRow[] rows = new DataRow[dt.Rows.Count];
dt.Rows.CopyTo(rows, 0);
//doneNotes = new List<int>(9);
// Get the TreeView ready for node creation.
// This isn't really needed since we're using AddRange (but it's good practice).
treeView1.BeginUpdate();
treeView1.Nodes.Clear();
TreeNode[] nodes = RecurseRows(rows);
treeView1.Nodes.AddRange(nodes);
// Notify the TreeView to resume painting.
treeView1.EndUpdate();
}
private TreeNode[] RecurseRows(DataRow[] rows)
{
List<TreeNode> nodeList = new List<TreeNode>();
TreeNode node = null;
foreach (DataRow dr in rows)
{
node = new TreeNode(dr["NoteName"].ToString());
noteID = Convert.ToInt32(dr["NoteID"]);
node.Name = noteID.ToString();
node.ToolTipText = noteID.ToString();
// This method searches the "dirty node list" for already completed nodes.
//if (!doneNotes.Contains(doneNoteID))
// This alternate method using the Find method uses a Predicate generic delegate.
if (nodeList.Find(FindNode) == null)
{
DataRow[] childRows = dt.Select("ParentNoteID = " + dr["NoteID"]);
if (childRows.Length > 0)
{
// Recursively call this function for all childRowsl
TreeNode[] childNodes = RecurseRows(childRows);
// Add all childnodes to this node.
node.Nodes.AddRange(childNodes);
}
// Mark this noteID as dirty (already added).
//doneNotes.Add(noteID);
nodeList.Add(node);
}
}
// Convert this List<TreeNode> to an array so it can be added to the parent node/TreeView.
TreeNode[] nodeArr = nodeList.ToArray();
return nodeArr;
}
private static bool FindNode(TreeNode n)
{
if (n.Nodes.Count == 0)
return n.Name == noteID.ToString();
else
{
while (n.Nodes.Count > 0)
{
foreach (TreeNode tn in n.Nodes)
{
if (tn.Name == noteID.ToString())
return true;
else
n = tn;
}
}
return false;
}
}
protected void ColorNodes(TreeNode root, Color firstColor, Color secondColor)
{
root.ForeColor = root.Index % 2 == 0 ? firstColor : secondColor;
foreach (TreeNode childNode in root.Nodes)
{
Color nextColor = childNode.ForeColor = childNode.Index % 2 == 0 ? firstColor : secondColor;
if (childNode.Nodes.Count > 0)
{
// alternate colors for the next node
if (nextColor == firstColor)
ColorNodes(childNode, secondColor, firstColor);
else
ColorNodes(childNode, firstColor, secondColor);
}
}
}
}
Run Code Online (Sandbox Code Playgroud)