2

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;
    }
AMissico
  • 21,470
  • 7
  • 78
  • 106
  • Is there any particular reason why you do not use MVVM pattern? WPFs TreeView works very well when used with MVVM. Using it in another way can be very ugly and give you a feeling 'this is so hard, I am probably doing something wrong'. What do you plan to do in your code when you find a TreeViewItem with specific Tag? – Stipo May 07 '12 at 15:16
  • Legacy code that I inherited. I am trying to straighten out the spagetti in order to get the code-base in shape in order to accept a view-model. (Currently, there is a mixture of multiple view-models and code-behind.) I need to find items because they are currently hard-coded in the quote/un-quote view-model. – AMissico May 07 '12 at 15:23

2 Answers2

1

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.

Community
  • 1
  • 1
Martin Moser
  • 6,219
  • 1
  • 27
  • 41
0

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.

hbarck
  • 2,934
  • 13
  • 16
  • Regardless of the ItemGenerator, I can iterate over the first level items. Right now I am using "IsExpanded=True" to get to the other levels. – AMissico May 07 '12 at 22:43
  • IsVirtualizing=False made no difference. – AMissico May 07 '12 at 22:44
  • I've updated my answer to show that it is possible to iterate over the items and their children, because the items are hardcoded. – hbarck May 08 '12 at 05:41