[英]Populate a TreeView from an object
我的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。 第一級和下一級是合適的父母。
當我單步執行代碼時,我的數據會准確返回。
List<TreeItem> items = BLLMatrix.GetTreeViewData(HUD.PersonId);
PopulateTree(tvwCaseNotes,items);
我只是不知道該怎么做,並用它准確地填充我的TreeView
。 這是我開始的事情,但現在我被困住了。
public static void PopulateTree(TreeView tree, ICollection<TreeItem> items)
我只是似乎無法包扎。 我是否需要拆分數據調用,並首先返回ParentNoteID = null
所有ParentNoteID = null
,然后再獲取其余ParentNoteID = null
並以某種方式將兩者ParentNoteID = null
?
@霍根:對於這個問題的急劇變化,我深表歉意。 從您的回應中可以明顯看出,我一開始並沒有從一個好的角度來解決這個問題。 其次,原始方法仍然無效。
也許我完全誤解了您,但是您擁有一個扁平的層次結構,其中每個元素都知道其父元素。 現在,您必須創建每個元素,然后建立層次結構。 這是這種實現的第一步(缺少循環檢查,錯誤處理等):
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);
}
}
只是為了回答評論中的問題:
它是一個包含其他兩個對象的簡單容器對象。 而已。
我用了它,因為在我的Dictionary中是一個引用在兩個對象( TreeNode
和Item
)上的不確定標識符( string CaseID
)。 另一種方法是制作兩個Dictionary為Dictionary<string, TreeNode>
和Dictionary<string, Item>
,但是由於存在1:1關系,因此我采用了元組方法。
也許將其重命名為ItemTreeNodeContainer
,對於您在具體情況下意味着什么,它會變得更加清晰。
使用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,並將Item類替換為由實體框架生成的Category類。
遞歸的基本思想是將堆棧用作每次調用中變量的臨時存儲。 但是,您在遞歸調用中引用了全局變量。 當您更改它(通過過濾器功能)時,它將使遞歸中的所有先前調用無效。 您需要刪除遞歸或在堆棧上推送控制變量(行)的新副本(而不是像您所做的那樣引用)。
根據評論進行編輯
我討厭把代碼在那里,而不能對其進行測試,但我相信這樣的事情應該工作,以解決我所描述的問題。
這是問題區域:
// 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);
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.