I am trying to find a specific TreeViewItem whose Tag property is set to a specific value. The below FindNode only works for the first level items or in other levels if the parent TreeViewItem is expanded. In the below example, if “FFF” is expanded, then FindNode works as expected. I am assuming that ContainerFromItem is returning null because the items have not been created. Is there a way to force the creation of all the TreeViewItems?
<TreeView x:Name="__items">
<TreeViewItem Header="AAA"
Tag="{x:Static my:Node.A}" />
<TreeViewItem Header="BBB"
Tag="{x:Static my:Node.B}">
<!-- Items will be added later. -->
</TreeViewItem>
<TreeViewItem Header="CCC"
Tag="{x:Static my:Node.C}" />
<TreeViewItem Header="DDD"
Tag="{x:Static my:Node.D}" />
<TreeViewItem Header="EEE"
Tag="{x:Static my:Node.E}" />
<TreeViewItem Header="FFF"
Tag="{x:Static my:Node.F}">
<TreeViewItem Header="GGG"
Tag="{x:Static my:Node.G}" />
<TreeViewItem Header="HHH"
Tag="{x:Static my:Node.H}" />
</TreeViewItem>
<TreeViewItem Header="III"
Tag="{x:Static my:Node.I}" />
</TreeView>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(MainWindow_Loaded);
}
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
TreeViewItem a = FindNode(__items.ItemContainerGenerator, __items.Items, Node.H);
}
private TreeViewItem FindNode(ItemContainerGenerator gen, ItemCollection items, Node value)
{
TreeViewItem oResult = null;
foreach (var oItem in items)
{
TreeViewItem oTreeViewItem = (TreeViewItem)gen.ContainerFromItem(oItem);
if (oTreeViewItem == null) { continue; }
if ((Node)oTreeViewItem.Tag == value) { oResult = oTreeViewItem; break; }
if (oTreeViewItem.Items.Count > 0)
{
oResult = FindNode(oTreeViewItem.ItemContainerGenerator, oTreeViewItem.Items, value);
if (oResult != null) { break; }
}
}
return oResult;
}
}
public enum Node { A, B, C, D, E, F, G, H, I, J, }
Based on hbarck’s answer the correct FindNode implementation is:
private TreeViewItem FindNode(ItemCollection items, Node value)
{
TreeViewItem oResult = null;
foreach (var oItem in items)
{
TreeViewItem oTreeViewItem = (TreeViewItem)oItem;
if ((Node)oTreeViewItem.Tag == value) { oResult = oTreeViewItem; break; }
if (oTreeViewItem.Items.Count > 0)
{
oResult = FindNode(oTreeViewItem.Items, value);
if (oResult != null) { break; }
}
}
return oResult;
}
the easiest way to make sure that your TreeViewItems exist is probably to set IsExpanded=”True” on each of them, and to set IsVirtualizing to False on the TreeView. Just out of curiosity: what happens if you don’t use the ItemGenerator, but just iterate the Items collection directly instead? Since you’re not using a DataTemplate, I’d imagine that the Items collection should contain the hardcoded items from your XAML file.
I’ve done the following test:
and code behind:
The output in the debug window is
which means that the items are instantiated and can be iterated, even in the child levels. Probably what gets into your way is the ItemContainerGenerator, which is actually only needed when you are using DataTemplates. Also, you probably have to wait until after the Loaded event of the window in order to have all items instantiated.