从对象填充TreeView

时间:2010-02-23 13:57:31

标签: c# .net winforms linq-to-sql treeview

我在WinForm应用中遇到treeview问题。我创建了一个保存数据的TreeViewItem类。只有5个字段: CaseNoteID,ContactDate,ParentNoteID,InsertUser,ContactDetails。

public class TreeItem
{
    public Guid CaseNoteID;
    public Guid? ParentNoteID;
    public string ContactDate;
    public string InsertUser;
    public string ContactDetails;

    public TreeItem(Guid caseNoteID, Guid? parentNoteID, string contactDate, string contactDetails, string insertUser)
    {
        CaseNoteID = caseNoteID;
        ParentNoteID = parentNoteID;
        ContactDate = contactDate;
        ContactDetails = contactDetails;
        InsertUser = insertUser;
    }
}

该计划是通过在 ParentNoteID 字段确定的父母下方显示注释来显示注释的关系。真的很简单。不幸的是,到目前为止我所有的尝试都在两个位置都放了一个“子”注释,一个带有ParentNoteID。第一级 AND 位于其下方。

当我单步执行代码时,我的数据会准确地返回。

 List<TreeItem> items = BLLMatrix.GetTreeViewData(HUD.PersonId);
        PopulateTree(tvwCaseNotes,items);

我只是不知道该怎么做,并准确填充我的TreeView。这就是我的开始,但现在我被卡住了。

  public static void PopulateTree(TreeView tree, ICollection<TreeItem> items)

我似乎无法绕过它。我是否需要拆分我的数据调用并首先返回所有与ParentNoteID = null的托管,然后去完成剩下的工作并以某种方式加入这两个?

@Hogan:我为这个问题的急剧变化而道歉。从你的回答中可以明显看出,我从一开始就没有从一个好的角度来看待这个问题。第二,原始方法仍然不起作用。

3 个答案:

答案 0 :(得分:4)

也许我完全误解了你,但你有一个扁平的层次结构,每个元素都知道它的父元素。现在,您必须创建每个元素,然后构建层次结构。以下是此类实现的第一个快速镜头(缺少循环检查,错误处理等):

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();

        PopulateTreeView(treeView, SampleData());
    }

    private IEnumerable<Item> SampleData()
    {
        yield return new Item { CaseID = "1" };
        yield return new Item { CaseID = "2" };
        yield return new Item { CaseID = "3" };
        yield return new Item { CaseID = "4", ParentID = "5" };
        yield return new Item { CaseID = "5", ParentID = "3" };
        yield return new Item { CaseID = "6" };
        yield return new Item { CaseID = "7", ParentID = "1" };
        yield return new Item { CaseID = "8", ParentID = "1" };
    }

    private void PopulateTreeView(TreeView tree, IEnumerable<Item> items)
    {
        Dictionary<string, Tuple<Item, TreeNode>> allNodes = new Dictionary<string, Tuple<Item, TreeNode>>();

        foreach (var item in items)
        {
            var node = CreateTreeNode(item);
            allNodes.Add(item.CaseID, Tuple.New(item, node));
        }

        foreach (var kvp in allNodes)
        {
            if (kvp.Value.First.ParentID != null)
            {
                allNodes[kvp.Value.First.ParentID].Second.Nodes.Add(kvp.Value.Second);
            }
            else
            {
                tree.Nodes.Add(kvp.Value.Second);
            }
        }
    }

    private TreeNode CreateTreeNode(Item item)
    {
        var node = new TreeNode();
        node.Text = item.CaseID;

        return node;
    }
}

public class Item
{
    public string CaseID { get; set; }
    public string ParentID { get; set; }
}

public class Tuple<T>
{
    public Tuple(T first)
    {
        First = first;
    }

    public T First { get; set; }
}

public class Tuple<T, T2> : Tuple<T>
{
    public Tuple(T first, T2 second)
        : base(first)
    {
        Second = second;
    }

    public T2 Second { get; set; }
}

public static class Tuple
{
    //Allows Tuple.New(1, "2") instead of new Tuple<int, string>(1, "2")
    public static Tuple<T1> New<T1>(T1 t1)
    {
        return new Tuple<T1>(t1);
    }

    public static Tuple<T1, T2> New<T1, T2>(T1 t1, T2 t2)
    {
        return new Tuple<T1, T2>(t1, t2);
    }
}

什么是元组?

只是回答评论中的问题:

它是一个容纳另外两个对象的简单容器对象。就是这样。

我使用它,因为在我的词典中是一个unqiue标识符(string CaseID),它引用了两个对象(TreeNodeItem)。另一种方法是将两个字典设为Dictionary<string, TreeNode>Dictionary<string, Item>,但由于存在1:1的关系,我采用了元组方法。

也许将它重命名为ItemTreeNodeContainer,它会更清楚地显示具体情况下的含义。

答案 1 :(得分:1)

递归的基本思想是在每次调用时使用堆栈作为变量的临时存储。但是,您在递归调用中引用了一个全局变量。当您更改它(通过过滤器功能)时,它将使递归中的所有先前调用无效。您需要删除递归或推送堆栈上的控制变量(行)的新副本(而不是像您正在做的那样)。

根据评论进行修改

我讨厌把代码放在那里而不能测试它,但是我相信像这样的这应该可以解决我描述的问题。

以下是问题区域:

// using the Find method uses a Predicate generic delegate.
if (nodeList.Find(FindNode) == null)
{
  var tmpCNoteID = dr["CaseNoteID"].ToString();
  var filter = "ParentNote='" + tmpCNoteID + "'";

  DataRow[] childRows = cNoteDT.Select(filter);

  if (childRows.Length > 0)
  {
    // Recursively call this function for all childRows
    TreeNode[] childNodes = RecurseRows(childRows);

    // Add all childnodes to this node
    node.Nodes.AddRange(childNodes);
  }

  // Mark this noteID as dirty (already added)
  nodeList.Add(node);
}

这样的事情应该能解决我看到的问题(注意:这不是优雅或优秀的代码,它只是我上面描述的问题的修复,我永远不会把我的名字放在这个代码上)。此外,如果无法测试代码,我甚至无法确定这是问题。

// using the Find method uses a Predicate generic delegate.
if (nodeList.Find(FindNode) == null)
{
  var tmpCNoteID = dr["CaseNoteID"].ToString();
  var filter = "ParentNote='" + tmpCNoteID + "'";

  DataTable DTCopy = cNoteDT.Copy()

  DataRow[] childRows = DTCopy.Select(filter);

  if (childRows.Length > 0)
  {
    // Recursively call this function for all childRows
    TreeNode[] childNodes = RecurseRows(childRows);

    // Add all childnodes to this node
    node.Nodes.AddRange(childNodes);
  }

  // Mark this noteID as dirty (already added)
  nodeList.Add(node);
}

答案 2 :(得分:1)

使用Oliver的解决方案解决了我的问题。刚刚使用Tuple重构了它.Net 4.0的一部分

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();

        PopulateTreeView(treeView1, SampleData());
    }

    private IEnumerable<Item> SampleData()
    {
        yield return new Item { CaseID = "1" };
        yield return new Item { CaseID = "2" };
        yield return new Item { CaseID = "3" };
        yield return new Item { CaseID = "4", ParentID = "5" };
        yield return new Item { CaseID = "5", ParentID = "3" };
        yield return new Item { CaseID = "6" };
        yield return new Item { CaseID = "7", ParentID = "1" };
        yield return new Item { CaseID = "8", ParentID = "1" };
    }

    private void PopulateTreeView(TreeView tree, IEnumerable<Item> items)
    {
        Dictionary<string, Tuple<Item, TreeNode>> allNodes = new Dictionary<string, Tuple<Item, TreeNode>>();

        foreach (var item in items)
        {
            var node = CreateTreeNode(item);
            allNodes.Add(item.CaseID, Tuple.Create(item, node));
        }

        foreach (var kvp in allNodes)
        {
            if (kvp.Value.Item1.ParentID != null)
            {
                allNodes[kvp.Value.Item1.ParentID].Item2.Nodes.Add(kvp.Value.Item2);
            }
            else
            {
                tree.Nodes.Add(kvp.Value.Item2);
            }
        }
    }

    private TreeNode CreateTreeNode(Item item)
    {
        var node = new TreeNode();
        node.Text = item.CaseID;

        return node;
    }

}

public class Item
{
    public string CaseID { get; set; }
    public string ParentID { get; set; }
}

MSDN上的元组帮助:

Tuple Class

在我的情况下,我从实体框架传递数据源:Entities.Categories 并用实体框架生成的Category类替换了Item类。