Reputation: 21684
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;
}
Upvotes: 2
Views: 4846
Reputation: 2944
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:
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<TreeView x:Name="TestTreeView">
<TreeViewItem Header="Item 1">
<TreeViewItem Header="Item 1 1"/>
<TreeViewItem Header="Item 1 2"/>
</TreeViewItem>
<TreeViewItem Header="Item 2"/>
</TreeView>
<Button x:Name="TestButton" Click="TestButton_Click">Test</Button>
</StackPanel>
</Window>
and code behind:
Class MainWindow
Private Sub TestButton_Click(sender As System.Object, e As System.Windows.RoutedEventArgs)
Stop
For Each t As TreeViewItem In Me.TestTreeView.Items
Debug.Print("Item: {0}, Child count:{1}", t.Header, t.Items.Count)
Next
End Sub
End Class
The output in the debug window is
Item: Item 1, Child count:2
Item: Item 2, Child count:0
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.
Upvotes: 0
Reputation: 6269
Yes you can force the creation of Items in an ItemsControl. Access the ItemContainerGenerator, then (this is the magic ;)) cast it to IItemContainerGenerator, as the interface is implemented explicit. Using StartAt and GenerateNext allows you to force the items to be created.
see: Why does ItemContainerGenerator return null? You just need to do it for all items.
Upvotes: 1