[Universal Apps] How to: Find DataTemplate-Generated Elements RRS feed

  • Question

  • There is already a Microsoft tutorial for doing this in WPF and older versions of the .NET Framework. This tutorial can be found here: http://msdn.microsoft.com/en-us/library/bb613579(v=vs.110).aspx

    As I was working on a universal app for Windows 8.1 and Windows Phone 8.1, I noticed that this tutorial doesn't quite work because the System.Windows library is used for DataTemplates, not the Windows.UI.Xaml library that is standard in universal apps. Adding a using statement for the System.Windows library didn't seem to work, so I was stuck. Luckily, I've managed to figure out the solution. Let's take this data template as an example:

    <ListBox Name="lstBox">
                    <Grid Name="grd">
                            <ColumnDefinition Width="*"/>
                            <ColumnDefinition Width="Auto"/>
                        <TextBox Name="txtBox" Grid.Column="0" Text="{Binding Path=text}" />
                        <Button Name="btn" Grid.Column="1" Content="test" />

    Using Microsoft's tutorial, you might retrieve the Button using C# like so:

    // Get the ListBoxItem at the specified index in the ListBox
    ListBoxItem ListBoxItem = (ListBoxItem)lstBox.ItemContainerGenerator.ContainerFromIndex(0);
    // Get the ContentPresenter of the ListBoxItem
    ContentPresenter ContentPresenter = FindVisualChild<ContentPresenter>(ListBoxItem);
    // Get the DataTemplate of the ContentPresenter
    DataTemplate ListBoxItemTemplate = ContentPresenter.ContentTemplate;
    // Find the Button by name from the DataTemplate set on the ContentPresenter
    Button btn = (Button)ListBoxItemTemplate.FindName("btn", ContentPresenter);

    Most of the same code will work in universal apps, but if you notice, the DataTemplate here is a System.Windows DataTemplate. In universal apps, you will be working with a Windows.UI.Xaml DataTemplate. The latter differs in the methods and properties that it lets you access, most notably the FindName method: it's gone! There are no methods or properties that The Windows.UI.Xaml DataTemplate provides us with to access an exact reference to that Button of ours (if you're wondering, the LoadContent method just creates a copy of the data template for use on another control).

    Fortunately, Microsoft has introduced a new property to the ContentControl object: ContentTemplateRoot. This property "Gets the root element of the date (should be data?) template specified by the ContentTemplate property." This means that this property will return the first item in our DataTemplate, or our Grid. After we retrieve our Grid, we can just search its children for the Button, either by name or by index. The result? Shorter code! Here's an example:

    // Get the first ListBoxItem in the ListBox
    ListBoxItem ListBoxItem = (ListBoxItem)lstBox.ContainerFromIndex(0);
    // Get the DataTemplate's Grid with its child elements
    Grid TheGrid = (Grid)ListBoxItem.ContentTemplateRoot;
    // Get the child Button by index
    Button TheButton = (Button)TheGrid.Children[1];
    // Get the child Button by name
    Button SameButton = (Button)TheGrid.FindName("btn");

    Of course, I showed off both the index way of retrieving the Button and the name way of retrieving the Button for demonstration purposes only. You would only need to use one of the methods. I prefer the index way because I believe it is better for performance.

    Well, that's about it! I couldn't find any resources online that addressed this topic, so I thought I'd make a quick post about it. Please let me know if you have any questions or know of a better way to do this!

    • Edited by Alexander Deeb Wednesday, May 21, 2014 10:00 AM Fixed code again
    Wednesday, May 21, 2014 9:54 AM