Reputation: 14243
I'm trying to extend the winforms TreeView control to allow incremental filtering and searching similar to the Solution Explorer in VS2012/VS2013.
Ideally, I would like it to be capable of replacing the existing TreeView with minimal code change - as far as the consumer is concerned, the only difference would be a method void Filter(string)
. Because of this, I think it would make sense for the Nodes
property to return the TreeNodeCollection
with ALL nodes, even ones not showing because of an applied filter.
I have the code written to handle the filtering, and it actually works quite well except when I access base.Nodes
, it returns my filtered nodes and not the full list.
The problem I have is, I'm unable to clone or create a new instance of TreeNodeCollection
, because the constructor is marked as internal. So my ideal code would look something like this:
public class TreeViewEx : TreeView
{
// results in a compiler error:
private TreeNodeCollection _allNodes = new TreeNodeCollection();
public new TreeNodeCollection Nodes { get { return _allNodes; } }
public TreeNodeCollection FilteredNodes { get { return base.Nodes; } }
public void Filter(string searchString)
{
base.BeginUpdate();
base.Nodes.Clear();
foreach (TreeNode node in FilterInternal(_allNodes, searchString))
{
base.Nodes.Add(node);
}
base.EndUpdate();
}
}
So as you can see, I'm trying to decouple the nodes that are shown in the UI from the nodes that the consumer would access. Of course with TreeNodeCollection
having an internal constructor only, I'm unable to create a new instance or clone it.
I considered these two options, but neither sound like good solutions:
TreeNodeCollection
object (due to the internal constructor) for the second list. This option seems like it would be more efficient than #2, but of course I'm creating an instance of an object I'm not supposed to.I want the end result to still be a TreeNodeCollection
so the TreeView can be used to replace our existing controls with minimal code and we do have several places using the Find
method, which doesn't exist in List<TreeNode>
.
Does anyone have any recommendations on how to handle this? What about performance/resource-wise with my two considerations?
Thank you
Update 1:
Per Pat's recommendation, I decided to take a step back and avoid messing with Nodes
altogether. So now I've added a List<TreeNode> AllNodes
property and have the Nodes
just display the nodes that appear in the TreeView (the filtered list), so now it's a bit simpler.
My problem now is, how do I know when AllNodes
has an item added to it so I can keep Nodes
in sync? I've considered using a BindingList
so I have the ListChanged
event, but then I would need to have my TreeNode and node's children/grand-children/etc (AllNodes[0].Nodes
) use a custom class that inherits from TreeNode
and change the Nodes
property, and TreeNode.Nodes
isn't overridable. Is there another way? I could make a new property called NodeExs or something, but that seems very unintuitive and I could see another dev coming along later and pulling his hair out because the Nodes property is there but doesn't work.
Upvotes: 2
Views: 617
Reputation: 2106
I've found out that the TreeNodeCollection
should only be used to read the listed nodes. Instead, I've used List<TreeNode>
to list nodes. In my project, I created a List<TreeNode>
for each level on the TreeView
. I filled the lists at the same time when I filled the TreeView
, at the startup. In the end, I used AddRange()
to make and combine a list of the all nodes. This way I had all the nodes listed and categorized.
It's easy and fast to create this kinds of lists. I also created a List<string>
version of the all nodes list, which I set up as an AutoCompleteCustomSource
for my TextBox
. This way I was able to use TextBox
with AutoComplete
for searching the nodes.
I'd make different lists for the consumers and other categories. Then I'd only add the items to the TreeView
which meet the given criteria. You can also use treeView.Nodes.Remove()
to remove any nodes. You'd still have the actual node stored on the lists, and could add it back again later.
These are just some ideas.
Upvotes: 1
Reputation: 1384
With regard to your proposed solutions, #2 is out because a TreeNode
cannot belong more than one control. And while it might be possible to create an instance of TreeNodeCollection
via reflection, it won't be very useful because its designed to be coupled to a TreeView
or another TreeNode
. You won't be able to add/remove nodes from the collection.
Because of this, I think it would make sense for the Nodes property to return the TreeNodeCollection with ALL nodes, even ones not showing because of an applied filter.
I disagree, the TreeNodeCollection
returned by the Nodes
property is used by the framework and OS to render the control. You really don't want to hide this property or alter its functionality.
If a consumer needs to have access to _allNodes
, create a List<TreeNode> AllNodes
property or use a custom collection.
Upvotes: 1