填充的WinForms TreeView控件从数据表 [英] Populate WinForms TreeView from DataTable

查看:213
本文介绍了填充的WinForms TreeView控件从数据表的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个WinForm TreeView控件显示CaseNotes的父子关系(我知道这意味着什么,以你最,但它帮助我想象的答案)。

I have a WinForm TreeView Control that displays the Parent Child relationship of CaseNotes(I know that means nothing to most of you but it helps me visualize the answers).

我有我需要显示CaseNotes的数据表。父/子的定义是:如果该行有ParentNoteID则是音符的childNode否则就是rootNode中。它也可能是家长说明(而不是rootNode中),如果其他行有它的ID,因为它是ParentNoteID。

I have a DataTable of the CaseNotes that I need to display. The Parent/Child is defined as: If the row has a ParentNoteID then it is a childNode of that note otherwise it is a rootNode. It could also be a parent note(but not a rootNode) if another row has it's ID as it's ParentNoteID.

要复杂(也许简单化)的事情我有下面的工作(主要)code表示颜色的节点交替。我手动创建一个静态集合树视图,它的颜色他们相当正确。现在我需要动态地从我的DataTable填充节点。

To complicate(maybe simplify) things I have the below working(mostly) code that colors the nodes alternatingly. I manually created a static collection for the treeview and it colors them fairly correctly. Now I need to dynamically populate the Nodes from my DataTable.

既然我已经打算直通树视图节点一个节点不应该我能够将数据追加到这个过程中不知何故?也许我需要,然后再建立节点颜色作为一个独立的程序,但递归方法仍然适​​用,是否正确?

Since I already am going thru the treeview node by node shouldn't I be able to append the data into this process somehow? Maybe I need to build the nodes first and then color as a separate routine but the Recursion Method would still apply, correct?

可以说,我想显示CaseNoteID每个节点。那就是在数据表回来了,是独一无二的。

Lets say I want to display CaseNoteID for each Node. That is returned in the DataTable and is unique.

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);
            }
        }
    }

修改

我的想法/企图至今:

EDIT

My thoughts/attempts so far:

        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();
    }

这显然是有缺陷的。其中,它只是显示的ContactDate一遍又一遍。当然这也显示出了正确的次数,但我想ContactDate(这是在数据库中的列,并在数据表中返回的值。其次,我需要添加ChildNode逻辑。A 如果(node.parentNode = node.CaseNoteID)等等...

所以,我才发现这个链接,<一个href="http://msdn.microsoft.com/en-us/library/system.windows.forms.treeview.beginupdate%28VS.85%29.aspx">here,它使得它看起来像我需要让我的数据表到一个ArrayList。这是否正确?

So I found this link, here, and it makes it seem like I need to get my DataTable into an ArrayList. Is that correct?

好了,感谢Cerebus这主要是工作。我只是有一个问题。我该如何利用这个 - >

Okay, thanks to Cerebus this is mostly working. I just have one more question. How do I take this-->

DataTable cNotesForTree = CurrentCaseNote.GetAllCNotes(Program._CurrentPerson.PersonID);

和使用我返回的DataTable在这?难道我只是更换本 - >

and use my returned DataTable in this? Do I just replace this -->

    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" });

我的困惑,我想,是我仍然需要做的Column.Add和Row.Adds?又怎么会DataColumn还转化为我的真实数据结构?很抱歉的很无知的问题,好消息是我从来没有问两次。

My confusion, I think, is do I still need to do the Column.Add and Row.Adds? Also how would the DataColumn translate to my real data structure? Sorry for the very ignorant questions, the good news is I never have to ask twice.

以下是提供一个运行时错误。

The following is providing a runtime error.

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);
  }

错误如下 - > 找不到列[ea8428e4] 这是正确的NoteID的第8位(我必须使用GUID)。它应该是寻找该名称的列?因为我使用的是GUID是有别的东西,我需要做什么?我改变了我的,你的code所有引用的Guid ...

The error is as follows --> Cannot find column [ea8428e4] Which is the first 8 digits of the correct NoteID(I have to use a Guid). Should it be looking for a column of that name?? Because I am using a Guid is there something else I need to do? I changed all the references in mine and your code to Guid...

推荐答案

要试图解决这个问题,我创建了一个样本窗口形式,写了下面的code。我设想的数据表设计如下:

To attempt to solve this problem, I created a sample windows form and wrote the following code. I envisioned the datatable design as follows:

 NoteID  NoteName  ParentNoteID
   "1"    "One"        null
   "2"    "Two"        "1"
   "3"    "Three"      "2"
   "4"    "Four"       null
...

这应该创建一个树为(对不起,我不是很好用ASCII艺术的!):

This should create a Tree as (sorry, I'm not very good with ASCII art!):

One
 |
 ——Two
 |
 ————Three
 |
Four

伪code是这样的:

Pseudocode goes like this:

  1. 在遍历数据表中的所有行。
  2. 对于每一行,创建一个树节点并设置其属性。递归重复这个过程具有ParentNodeID匹配此行的ID的所有行。
  3. 在每个完整的循环会返回一个包含所有匹配的子节点具有无限嵌套的节点。
  4. 添加完成的节点列表到TreeView。

在方案,问题就出现从外键指的是在同一个表中的列的事实。这意味着,当我们通过行迭代,我们必须要跟踪哪些行已经被解析。例如,在上表中,匹配所述第二和第三行中的节点已经添加在第一个完整迭代。因此,我们不能再添加。有两种方法来跟踪这样的:

The problem in your scenario arises from the fact the "foreign key" refers to a column in the same table. This means that when we iterate through the rows, we have to keep track of which rows have already been parsed. For example, in the table above, the node matching the second and third rows are already added in the first complete iteration. Therefore, we must not add them again. There are two ways to keep track of this:

  1. 在维​​护ID的已完成的列表( doneNotes )。在添加每一个新的节点,检查noteID存在于该列表。这是更快的方法,通常应该preferred。 (该方法被注释掉了下面的code 的)
  2. 在每次迭代中,使用predicate泛型委托( FindNode )搜索添加节点(占嵌套的节​​点)列表,如果须─见加入节点存在于该列表。这是比较慢的解决方案,但我还挺喜欢复杂code! :P
  1. Maintain a list of ID's that have been done (doneNotes). Before adding each new node, check if the noteID exists in that list. This is the faster method and should normally be preferred. (this method is commented out in the code below)
  2. For each iteration, use a predicate generic delegate (FindNode) to search the list of added nodes (accounting for nested nodes) to see if the to-be added node exists in that list. This is the slower solution, but I kinda like complicated code! :P

确定,这里是经得起考验的code(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);
      }
    }
  }
}



这篇关于填充的WinForms TreeView控件从数据表的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆