Finding an Object TreeViewItem

The question often comes up, "how do I get to a certain TreeViewItem?"The question arises because a TreeView is bound to a data source, TreeViewItems are implicitly created and wrap the data object.However, the TreeView.SelectedItem property returns the data object, not the TreeViewItem.This is good when you need to access the data, but what if you need to manipulate the TreeViewItem?It has been suggested that if you design your data model correctly, you can avoid needing to manipulate TreeViewItems directly.While that is good general advice, there might be cases where manipulating the TreeViewItem is unavoidable.

Finding the TreeViewItem in a TreeView is more involved than finding item containers in other ItemControls (such as a ListBoxItem in a ListBox) because TreeViewItems are, naturally, nested.For example, to return the ListBoxItem of an object in a ListBox, you can call ListBox.ItemContainerGenerator.GetContainerFromItem. But if you call GetContainerFromItem on a TreeView, the ItemContainerGenerator searches only the direct child objects of the TreeView.So you need to recursively traverse the TreeView and child TreeViewItem objects.A further complication is that if the TreeView virtualizes its items (you enable virtualization by setting the VirtualizingStackPanel.IsVirtualizing property to true), the child items need to be created before you can check its data object.

Given the complexity of the problem, I (with help from the development team) created a method that traverses the TreeView and realizes any virtualized items.

/// <summary>

/// Recursively search for an item in this subtree.

/// </summary>

/// <param name="container">

/// The parent ItemsControl. This can be a TreeView or a TreeViewItem.

Now that you have the code, you can search for any object in any TreeView, regardless of its depth in the tree.

I attached a sample that uses this technique to find any item in the TreeView. The sample asks for a number, finds the corresponding data object in the data model, and then selects the TreeViewItem that contains the object.Note that the way I find the data object in the model is specific to the organization of my data.I could have kept track of the data object's location within the data hierarchy, and then find the corresponding TreeViewItem in the TreeView.

For example, the item that corresponds to 41 is, starting from the root of the TreeView, under the second item, then under the first item, then under the second item, and finally the first item.I could have kept track of its location with a list of indices, 2,1,2,1, and then used those to navigate the TreeView.However, this approach is dependent on the organization of the underlying TreeView.The implementation I show above works on any TreeView and it doesn't require knowledge of the data model.

Hi Carole : Thanks very much for writing this. It was helpful, especially not having to reference private BindingFlags in accessing the internal Scroll control.

I found that, after utilizing your FindTreeViewItem, with a very large, flattened list (which still requires a TreeView rather than a ListView), such as 15k items, the ApplyTemplate() followed by the BringIndexIntoView() takes a very significant amount of time.

I was able to get around much of it, for my use case in the following way: I added a delegate to GetTreeViewItem(…)'s representing whether the object to test/ApplyTemplate/BringIntoView is worth expanding (i.e. has child items) (containerItem) => ((MyInterface) conainterItem).HasItems(); If the item doesn't match the queried item, and if the item has no children, it is skipped and no ApplyTemplate or BringIntoView is applied, thereby dramatically speeding up my use case anyway.

This, of course, implies a few things. (1) The MVVM model supports holding all item's children in each element (or other way via the delegate) and (2) the huge list of items are not parents themselves. (So, 15k items all with one child would still be slow as each would be expanded). The way I would mitigate that would be to capture the hierarchy and test for one parent at a time, on down, knowing what level they are in, before expanding.

Thanks again – I just wanted to share that in case others were or would run into similar challenges.

The fact that this much code is necessary for the *simple* task of finding an item in a TreeView is WPF's achilles heel. This is ridiculous. And how in the world does Microsoft expect beginners to come up with this solution? Clearly WPF is meant for gurus who have weeks to pour over documentation and analyzer Reflector results to figure out convoluted ways to do SIMPLE things!!