Skip Navigation LinksHome > View Post

Grabbing the Visual Tree for an item displayed by an ItemsControl

Q. How can I gain programmatic access to the elements created by an ItemsControl?

Consider this scenario where I have a ListBox databound to a bunch of strings:

<Grid>
    <Grid.Resources>
        <x:Array x:Key="strings" Type="{x:Type sys:String}">
            <sys:String>Item A</sys:String>
            <sys:String>Item B</sys:String>
            <sys:String>Item C</sys:String>
        </x:Array>
    </Grid.Resources>
    <ListBox Name="listBox" ItemsSource="{Binding Source={StaticResource strings}}" SelectionChanged="listBox_SelectionChanged">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <Border BorderBrush="Red" BorderThickness="1" CornerRadius="3">
                    <TextBlock Text="{Binding}" Margin="3" FontSize="30" Foreground="Blue"/>
                </Border>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
</Grid>

Note also that we specify a new ItemTemplate for the ListBox in the form of a DataTemplate (more on these here).

It's easy enough to programmatically access a specific 'item' within the ListBox right?

string selectedItem = (string) listBox.SelectedItem;

But what if I want to gain access to the VisualTree of elements created by the ItemsControl (ListBox) to display my items? This is where the ItemContainerGenerator comes in. Let's use this inside our listBox_SelectionChanged event handler in our code-behind:

private void listBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    ListBox source = (ListBox)sender;
    DependencyObject d = source.ItemContainerGenerator.ContainerFromIndex(source.SelectedIndex);
    // Display the VisualTree using our helper method and the actual value of the item
    // as the title of the messagebox
    MessageBox.Show(DisplayVisualTree(d, 0), (string) listBox.SelectedItem);
}

Looks easy enough right? We can just use the ItemContainerGenerator property of the ItemsControl (ListBox in this case) and use its ContainerFromIndex method (or indeed its ContainerFromItem method) to grab the top of the VisualTree for this item. I've created a helper method to walk the VisualTree and create an indented string that looks like this:

private string DisplayVisualTree(DependencyObject depObj, int depth)
{
    string response = string.Empty.PadRight(depth) + depObj.ToString();

    for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
    {
        response += Environment.NewLine + DisplayVisualTree(VisualTreeHelper.GetChild(depObj, i), depth + 1);
    }

    return response;
}

Note that whenever you want to explore the Visual Tree in WPF you need to call in the VisualTreeHelper, just call 1-800-VIS-TREE-HELP. Just kidding, The VisualTreeHelper is a static class that lives in the System.Windows.Media namespace (inside the PresentationCore assembly).

Easy peasy!

The VisualTree for our selected item

Tags: WPF

 
Josh Post By Josh Twist
9:58 AM
28 Feb 2008

» Next Post: Aston Martin at MIX 08
« Previous Post: Defense in depth: UI Security is never enough

Comments are closed for this post.

Posted by Billy @ 15 Nov 2010 8:06 AM
This is extremely helpful, thank you :)

Posted by Matt @ 01 Mar 2011 10:13 PM
I love you

© 2005 - 2014 Josh Twist - All Rights Reserved.