I am using the following code to add nodes in a treeview. Is there a better or more compact way of doing this by using LINQ?
foreach (Plan plan in this.IncomingPlan.Plans)
{
foreach (Document doc in plan.Documents.Where(d => d.Name.Equals(this.DocumentName, StringComparison.OrdinalIgnoreCase)))
{
foreach (Author author in doc.Authors)
{
TreeNode treeNode = new TreeNode()
{
Text = author.Name,
Type = NodeType.ParentNode,
Tag = author
};
foreach (Book book in author.Books)
{
treeNode.Nodes.Add(new TreeNode()
{
Text = book.Name,
Type = NodeType.ChildNode,
Tag = book
});
}
this.treeView.Nodes.Add(treeNode);
}
}
}
2 Answers 2
You can make this more maintainable and more compact by utilizing LINQ here. I'm not sure what TreeNode
is in your code, I'm guessing you derived from a WinForms TreeNode
.
I'd argue that the node's Type
is unnecessary. You can easily determine that if you look at its Level
. Level 0
indicates it is at the root of the tree, otherwise it is greater than 0.
Unfortunately there's no nice way to add a range of nodes to another. You could only add arrays of the nodes. Using a loop would be the best option.
Here, I would flatten the nested loops as far as I can then loop through to add them to the tree. To compact it even more, create a factory method to create the nodes. Even more useful if you have a lot of properties to set.
// create the node for the item
static TreeNode CreateNode<T>(T item, Func<T, string> textSelector)
{
return new TreeNode { Text = textSelector(item), Tag = item };
}
var authors =
from plan in this.IncomingPlan.Plans
from doc in plan.Documents
where doc.Name.Equals(this.DocumentName, StringComparison.OrdinalIgnoreCase)
from author in doc.Authors
select author;
foreach (var author in authors)
{
var authorNode = CreateNode(author, a => a.Name);
foreach (var book in author.Books)
{
authorNode.Nodes.Add(CreateNode(book, b => b.Name));
}
treeView.Nodes.Add(authorNode);
}
-
\$\begingroup\$ Might be handy to write an extension method on TreeNodeCollection which allows you to add an IEnumerable<TreeNode>. It would still loop under the hood but that detail would be abstracted from you :) \$\endgroup\$MattDavey– MattDavey2011年10月14日 08:27:11 +00:00Commented Oct 14, 2011 at 8:27
-
\$\begingroup\$ I'd also a method BookToTreeNode(Book b) which could be used in a linq projection -- authorNode.Nodes.Add(author.Books.Select(BookToTreeNode)) \$\endgroup\$MattDavey– MattDavey2011年10月14日 08:32:02 +00:00Commented Oct 14, 2011 at 8:32
-
\$\begingroup\$ I thought about that at first but decided it is better to not create extension methods for that purpose. It's too bad not all collections support adding ranges (
IEnumerable<T>
in particular) of items but I don't see the compelling need to write an extension method to do so either as it's not something that is needed too often. \$\endgroup\$Jeff Mercado– Jeff Mercado2011年10月14日 09:45:24 +00:00Commented Oct 14, 2011 at 9:45
Looks perfectly fine to me. That's the first time I've looked at your code and I was able to see quickly what it does.
There is no need to overly-compact things if it is going to make it a pain for someone else to understand in future.
-
\$\begingroup\$ four nested foreach loops doesn't look that great! This is a good example of the arrowhead anti-pattern \$\endgroup\$MattDavey– MattDavey2011年10月14日 08:28:32 +00:00Commented Oct 14, 2011 at 8:28
-
\$\begingroup\$ @MattDavey, I was also concerned about the nested loops that's why I asked the question :) \$\endgroup\$awaisj– awaisj2011年10月17日 05:45:19 +00:00Commented Oct 17, 2011 at 5:45