我在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:我为这个问题的急剧变化而道歉。从你的回答中可以明显看出,我从一开始就没有从一个好的角度来看待这个问题。第二,原始方法仍然不起作用。
答案 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
),它引用了两个对象(TreeNode
和Item
)。另一种方法是将两个字典设为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上的元组帮助:
在我的情况下,我从实体框架传递数据源:Entities.Categories 并用实体框架生成的Category类替换了Item类。