WPF FAQ
Get Microsoft Silverlight
Skip Navigation Links

Data templates are excellent. Using a DataTemplateSelector is even more useful, unfortunately, if you ever really want to use one, you face one major problem, finding out how to use them properly because all the MSDN documentation for the DataTemplateSelector and its usage absolutely suck!

The following code shows the method used to find a template selector by finding the main window of the application and searching within its resources:

    public class TaskListDataTemplateSelector : DataTemplateSelector
    {
        public override DataTemplate
            SelectTemplate(object item, DependencyObject container)
        {
            if (item != null && item is Task)
            {
                Task taskitem = item as Task;
                Window window = Application.Current.MainWindow;

                if (taskitem.Priority == 1)
                    return
                        window.FindResource("importantTaskTemplate") as DataTemplate;
                else
                    return
                        window.FindResource("myTaskTemplate") as DataTemplate;
            }

            return null;
        }
    }

This technique however, has a flaw in it inasmuch as the FindResource method actually searches in the wrong direction for this code to be really useful. FindResource walks from a node buried deep within the logical tree and searches backwards towards the root. With the MSDN sample, if the template isn't at the level of the main window the resource will not be found.

Now, the advantage of this behaviour may not be abundantly clear at first glance however, we could imagine that data we bind to in our WPF or Silverlight UI might be represented differently in different objects, say in a TreeView and in a ListView showing two different aspects of the data. Imagine then, several DataTemplates, one for each view, being selected for the UI element using a common name but stored in different resource dictionaries. In this way we can use the simplest data binding scenario and allow the position of the resource in the UI markup to determine the look of the visual.

Where does the promise of reflection fall into all of this you may be asking? Well, when a DataTemplateSelector is needed, a delegate is called to perform the selection. This delegate takes as its parameters the object that is in the binding context and a DependencyObject which is the container for the bound object. This container will be of a different type depending on the type of UI element used. Because we may be using several templates in several views we cannot be certain of which type of object the container will be so, a little bit of reflection will allow us to find the desired resource no matter where the selector is used.

In the example, I have created a very simple object and the template selection will take place according to the type of the object. Of course, in your own implemetations you may be interested in other properties of the data object but for now, this will suffice. The data objects form a tree-like structure which is ideal for display in a TreeView. When we select a node in the tree view, the data is placed into the data context of a list view and a different aspect of the data is seen. The only remotely complicated thing about the data itself is a little routine which creates the initial tree of data objects. Here is the code for those simple data objects:

    public class BasicThing
    {
        public BindingList<BasicThing> Things { get; protected set; }
 
        public BasicThing()
        {
            Things = new BindingList<BasicThing>();
        }
 
        static Random r = null;
 
        public static BindingList<BasicThing> GenerateThings(int level)
        {
            if (r == null)
                r = new Random();
 
            BindingList<BasicThing> blt = new BindingList<BasicThing>();
 
            int nl = level - 1;
 
            if(nl>0)
            {
                int l=(r.Next(10) + 2);
 
                for (int n = 0; n < l; n++)
                {
                    BasicThing bt=null;
                    switch (r.Next(3))
                    {
                        case 0:
                            bt = new Thing1();
                            break;
                        case 1:
                            bt = new Thing2();
                            break;
                        case 2:
                            bt = new Thing3();
                            break;
                        default:
                            throw new Exception();
                    }
                    bt.Things = GenerateThings(nl);
                    blt.Add(bt);
                }
 
            }
             return blt;
        }
    }
 
    public class Thing1 : BasicThing
    {
    }
 
    public class Thing2 : BasicThing
    {
    }
 
    public class Thing3 : BasicThing
    {
    }

Now that the data objects exist, we can design some data templates for the various visual appearances of the objects as they are seen in the tree view. Here's some XAML for that:

            <TreeView.Resources>
                <HierarchicalDataTemplate x:Key="thing1DataTemplate" ItemTemplateSelector="{StaticResource theSelector}" ItemsSource="{Binding Things}">
                    <Grid Margin="4,4,4,4">
                        <Ellipse Width="15" Height="15" Fill="Red"/>
                    </Grid>
                </HierarchicalDataTemplate>
                <HierarchicalDataTemplate x:Key="thing2DataTemplate" ItemTemplateSelector="{StaticResource theSelector}" ItemsSource="{Binding Things}">
                    <Grid Margin="4,4,4,4">
                        <Rectangle Width="15" Height="15" Fill="Blue"/>
                    </Grid>
                </HierarchicalDataTemplate>
                <HierarchicalDataTemplate x:Key="thing3DataTemplate" ItemTemplateSelector="{StaticResource theSelector}" ItemsSource="{Binding Things}">
                    <Grid Margin="4,4,4,4">
                        <Image Width="15" Height="15" Source="Bitmap1.bmp"/>
                    </Grid>
                </HierarchicalDataTemplate>               
            </TreeView.Resources>

You can see that these are hierarchical data templates which enable the tree view to display the full structure of the data. You can also see that the templates all use the same template selector for their sub-items and that they are named with keys as thing1DataTemplate, thing2DataTemplate and thing3DataTemplate. Make a note of this for later.

The TreeView itself is declared in the following code and shows the simplicity of the component setup:

        <TreeView Grid.Column="0" x:Name="treeView1" ItemTemplateSelector="{StaticResource theSelector}" TreeViewItem.Selected="treeView1_Selected">
            <TreeView.Resources>
            …The templates you’ve already seen…
            </TreeView.Resources>
        </TreeView>

So, you see here that the template selector is used by the TreeView to select the template for the root node. The same selector declared in the templates themselves will serve for the sub nodes.

Now we come to the templates used in a ListView. In the next bit of XAML you will see once again that the resources holding the templates are stored by the graphical object itself. The ListView declares the same DataTemplateSelector and the templates themselves all use the same key names as those used in the TreeView. You will realise now that this is the advantage offered by FindResource because the container object for the ListView will be able to search back to the resources that it holds and the container for the TreeView will find its own local resources such that the templates for the different visual styles are applied to the different UI controls. Here's the ListView XAML:

        <ListView Grid.Column="1" x:Name="listView1" ItemsSource="{Binding Things}" ItemTemplateSelector="{StaticResource theSelector}">
            <ListView.Resources>
                <DataTemplate x:Key="thing1DataTemplate">
                    <Grid Margin="6,6,6,6">
                        <StackPanel Orientation="Horizontal">
                            <TextBlock Text="{Binding}"/>
                            <Ellipse Width="30" Height="30" Fill="Red"/>
                        </StackPanel>
                    </Grid>   
                </DataTemplate>   
                <DataTemplate x:Key="thing2DataTemplate">
                    <Grid Margin="6,6,6,6">
                        <StackPanel Orientation="Horizontal">
                            <TextBlock Text="{Binding}"/>
                            <Rectangle Width="30" Height="30" Fill="Blue"/>
                        </StackPanel>
                    </Grid>   
                </DataTemplate>   
                <DataTemplate x:Key="thing3DataTemplate">
                    <Grid Margin="6,6,6,6">
                        <StackPanel Orientation="Horizontal">
                            <TextBlock Text="{Binding}"/>
                            <Image Width="30" Height="30" Source="Bitmap1.bmp"/>
                        </StackPanel>
                    </Grid>   
                </DataTemplate>    
            </ListView.Resources>           
        </ListView>

Now the famous DataTemplateSelector based object itself. The code for this is once again very simple and is shown below:

    public class ThingTemplateSelector : DataTemplateSelector
    {
        public override DataTemplate SelectTemplate(object item, DependencyObject container)
        {
            MethodInfo mi = container.GetType().GetMethod("FindResource") as MethodInfo;
            if (mi != null)
            {
                switch (item.ToString())
                {
                    case "ReflectedTemplateSelection.Thing1":
                        return mi.Invoke(container, new object[] { "thing1DataTemplate" }) as DataTemplate;
                    case "ReflectedTemplateSelection.Thing2":
                        return mi.Invoke(container, new object[] { "thing2DataTemplate" }) as DataTemplate;
                    case "ReflectedTemplateSelection.Thing3":
                        return mi.Invoke(container, new object[] { "thing3DataTemplate" }) as DataTemplate;
                }
            }
            return null;
        }
    }

The application when running shows the different templates in the TreeView and ListView:

DataTemplateSelector demo application

The complete ZIP file containing the demo code for this article can be downloaded from this link.