WindowsPhoneGeek

WPAppInfo

Login | Join (Why?)

rss rss rss
logo

Implementing Windows Phone 7 DataTemplateSelector and CustomDataTemplateSelector

published on: 2/11/2011 | Views: N/A | Tags: Silverlight

by WindowsPhoneGeek

In this article I am going to explain how to create a DataTemplateSelector abstract class  and custom DataTemplateSelector in Silverlight for Windows Phone 7. Basically a DataTemplateSelector will provide a way to choose a DataTemplate based on the data object and the data-bound element. Typically, you need some kind of DataTemplateSelector when you have more than one DataTemplate for the same type of objects and you want to supply your own logic to choose a DataTemplate to apply based on the properties of each data object.

61-0In short: DataTemplateSelector enables you to write some logic that chooses what DataTemplate to use for a particular item. You could even create an entirely new data template if needed to.

NOTE: DataTemplateSelector is a well known class in WPF but it is still not available for Silverlight.

Here is a popular question that I found while browsing through the dev forums:

"I have a list of different types of elements. I would like to display a different data template for different list elements, based on what type it is. "

The answer to this question is to use a kind of DataTemplateSelector. So in this article  I will first explain how to implement a DataTemplateSelector abstract class and after that I will demonstrate how to implement your own CustomDataTemplateSelector . The final result is shown on the right image.

 

 

Implementing DataTemplateSelector abstract class

There are lots of different ways in which you can create a dynamic DataTemplateSelector. You can grab a piece of code from the default WPF implementation or try a different approach with ContentPtesenter of ValueConverter etc. In this article I will demonstrate how to create a DataTemplateSelector which derives from ContentControl (I will use ContentControl as a base class).

The first thing I need to mention is that I will create an abstract class with a virtual method SelectTemplate which provides logic to return the appropriate template based on the value of the Priority property (when overridden in any particular class that derives from DataTemplateSelector). I will also override the OnContentChanhed which comes from the base class. The source code is as follows:

public abstract class DataTemplateSelector : ContentControl
{
    public virtual DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        return null;
    }

    protected override void OnContentChanged(object oldContent, object newContent)
    {
        base.OnContentChanged(oldContent, newContent);

        ContentTemplate = SelectTemplate(newContent, this);
    }
}

How to create a CustomDataTemplateSelector

To create a custom data template selector first create a class that inherits from DataTemplateSelector and after that override the SelectTemplate method. Once your class is defined you can assign an instance of the class to the template selector property of your element.

I will create a FoodTemplateSelector class that will contain three different DataTemplates: Healthy, UnHealthy and NotDetermined. In the SelectTemplate method I will add some conditions that will select the appropriate DataTemplate based on a data context property value. Basically we will choose  the right template based on the Type property value of our data source.

The FoodTemplateSelector class source code is as follows:

public class FoodTemplateSelector : DataTemplateSelector
{
    public DataTemplate Healthy
    {
        get;
        set;
    }

    public DataTemplate UnHealthy
    {
        get;
        set;
    }

    public DataTemplate NotDetermined
    {
        get;
        set;
    }

    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        Data foodItem = item as Data;
        if (foodItem != null)
        {
            if (foodItem.Type == "Healthy")
            {
                return Healthy;
            }
            else if (foodItem.Type == "NotDetermined")
            {
                return NotDetermined;
            }
            else
            {
                return UnHealthy;
            }
        }

        return base.SelectTemplate(item, container);
    }
}

And here is how our data class should looks like:

public class Data
{
    public string Name
    {
        get;
        set;
    }

    public string Description
    {
        get;
        set;
    }

    public string IconUri
    {
        get;
        set;
    }

    public string Type
    {
        get;
        set;
    }
}

 

I will use a data bound ListBox in order to demonstrate the usage of the FoodTemplateSelector.  Here is the source code:

public MainPage()
        {
            InitializeComponent();

            List<Data> list = new List<Data>();
            Data item0 = new Data() { Name = "Tomato", IconUri = "Images/Tomato.png", Type = "Healthy" };
            Data item1 = new Data() { Name = "Beer", IconUri = "Images/Beer.png", Type = "NotDetermined" };
            Data item2 = new Data() { Name = "Fries", IconUri = "Images/fries.png", Type = "Unhealthy" };
            Data item3 = new Data() { Name = "Sandwich", IconUri = "Images/Hamburger.png", Type = "Unhealthy" };
            Data item4 = new Data() { Name = "Ice-cream", IconUri = "Images/icecream.png", Type = "Healthy" };
            Data item5 = new Data() { Name = "Pizza", IconUri = "Images/Pizza.png", Type = "Unhealthy" };
            Data item6 = new Data() { Name = "Pepper", IconUri = "Images/Pepper.png", Type = "Healthy" };
            list.Add(item0);
            list.Add(item1);
            list.Add(item2);
            list.Add(item3);
            list.Add(item4);
            list.Add(item5);
            list.Add(item6);

            this.listBox.ItemsSource = list;
                
        }

61-1

Next I will create three different DataTemplates and will set them as an ItemTemplate of the ListBox. Note that each template is independent and not connected with the rest ones. This means that you are free to add whatever element you want in each of the templates. So you have one data source in three different Views.

The final result can be seen in the image on the left. And here is how each of the templates looks like:

61-461-261-3

Here is how the source code should looks like:

 
<ListBox x:Name="listBox" HorizontalContentAlignment="Stretch">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <local:FoodTemplateSelector Content="{Binding}">
                <local:FoodTemplateSelector.Healthy>
                    <DataTemplate>
                        <StackPanel Orientation="Horizontal" Background="YellowGreen" Width="400" Margin="10">
                            <Image Source="{Binding IconUri}" Stretch="None"/>
                            <TextBlock Text="{Binding Name}" FontSize="40" Foreground="Black" Width="280"/>
                            <TextBlock Text="healty" />
                        </StackPanel>
                    </DataTemplate>
                    </local:FoodTemplateSelector.Healthy>
                <local:FoodTemplateSelector.UnHealthy>
                    <DataTemplate>
                        <Border BorderBrush="Red" BorderThickness="2"  Width="400" Margin="10">
                        <StackPanel Orientation="Horizontal">
                            <Image Source="{Binding IconUri}" Stretch="None"/>
                                <TextBlock Text="{Binding Name}" FontSize="40" Width="280"/>
                            <Image Source="Images/attention.png" Stretch="None" Margin="10,0,0,0"/>
                        </StackPanel>
                        </Border>
                    </DataTemplate>
                </local:FoodTemplateSelector.UnHealthy>
                <local:FoodTemplateSelector.NotDetermined>
                    <DataTemplate>
                        <StackPanel Orientation="Horizontal" Background="Gray" Width="400" Margin="10">
                            <Image Source="{Binding IconUri}" Stretch="None"/>
                            <TextBlock Text="{Binding Name}" FontSize="40" Width="280"/>
                            <Image Source="Images/question.png" Stretch="None" Margin="10,0,0,0"/>
                        </StackPanel>
                    </DataTemplate>
                </local:FoodTemplateSelector.NotDetermined>
            </local:FoodTemplateSelector>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

An the final result : each item uses different template depending on the Type property value:

61-0

Here are some helpful links with another approach of DataTemplateSelector  implementation:

That was all about how to implement a DataTemplateSelector abstract class and how to use it in Silverlight for Windows Phone 7. You can find the full source here:

I hope that the article was helpful.

You can also follow us on Twitter @winphonegeek

Comments

.

posted by: TH on 2/11/2011 7:50:19 PM

Thank you for writing this article.

How many templates can I use?

posted by: Thimoty on 2/11/2011 7:51:40 PM

Thanks for another great post. I am new to WP7 so my question is how many templates can I use?

cool stuff

posted by: Kinsoa on 2/11/2011 7:52:42 PM

Just found this post. Pretty cool stuff!

Incorrect example

posted by: Jake on 4/1/2011 3:42:08 PM

You have a CountryTemplateSelector in your xaml code. It should be FoodTemplateSelector

RE:Incorrect example

posted by: winphonegeek on 4/26/2011 10:41:34 PM

Thank you for pointing that out. Fortunately this was only a problem with the code spinet tool.The typo is now fixed.

Getting it to work with Sample Data

posted by: Eric on 5/10/2011 5:46:40 PM

Nice example! Thanks!

I had to do a little extra coding to get this to work with sample data in the designer, because the designer creates fake types. Here's what my template selector looks like (Notice that I use reflection to get the data I need for template selection):

    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        ItemViewModel model = item as ItemViewModel;
        ItemType itemType;
        // Sample data in the designer uses a faked ItemViewModel
        if(model == null)
        {
            Type modelType = item.GetType();
            PropertyInfo info = modelType.GetProperty("ItemType");
            itemType = (ItemType)info.GetValue(item, null);
        }
        else
        {
            itemType = model.ItemType;
        }

        switch(itemType)
        {
            case ItemType.SetSeparator:
                return SetSeparator;
            case ItemType.ColorIndicator:
                return ColorIndicator;
            default:
                return SetSeparator;
        }
        return base.SelectTemplate(model, container);
    }

posted by: Dave Fancher on 7/3/2011 7:18:08 AM

Thanks for this! I needed to select a template based on the actual type of object in a collection. This was the best solution I came across for WP7 and was easily adapted to my situation.

Design Mode Sample Data

posted by: dutzend on 8/1/2011 6:25:10 PM

Hi,

your post are very helpful!

How can i use Sample data in Design Mode, Erics Example works not for me i cant find the "ItemType".

Any Help?

thx dutzend

Templating on events

posted by: Cesar on 8/3/2011 6:05:58 PM

Is it possible to change the template of all ListBoxItems based on a click in the page that contains your ListBox?

Mango problem

posted by: Ido on 8/26/2011 9:01:11 AM

Hi, I used this example in the NODO release and it worked great. Once I upgraded my code to MANGO it stoped working. I get "Unspecified error". I tested your code and it is working on MANGO.

Any ideas?

Problem with local in xaml

posted by: yash on 10/21/2011 2:47:38 PM

I have problem with the xaml code .

Error 2 The type 'local:FieldTemplateSelector' was not found. Verify that you are not missing an assembly reference and that all referenced assemblies have been built.

I am new to this windows development .

please help me .

Best Regards, Yash

posted by: Denis on 10/29/2011 10:45:00 PM

Thank you - very helpful!

Horizontal orientation

posted by: sunco on 11/26/2011 8:03:07 PM

Thanks for this great article.

Lets see i have this:


Data item0 = new Data() { Name = "Tomato", Type = "Healthy" };
Data item1 = new Data() { Name = "Ice-cream", Type = "Healthy" };
Data item2 = new Data() { Name = "Pepper", Type = "Healthy" };
Data item3 = new Data() { Name = "Beer", Type = "Healthy" };
Data item4 = new Data() { Name = "Fries", Type = "Unhealthy" };
Data item5 = new Data() { Name = "Sandwich", Type = "Unhealthy" };
Data item6 = new Data() { Name = "Pizza", Type = "Unhealthy" };

As you can see, the first 4 items are Healthy. I want to show 2 items in 1 row. Then, show the Unhealthy one by row

How can i do that ? I was playing with StackPanels and Orientation with no luck

RE: Horizontal orientation

posted by: winphonegeek on 11/29/2011 2:02:51 PM

You can use the WrapPanel if you want an overflow layout (horizontal or vertical). You can change the layout panel that is used by the list box control to order items as described in this article: http://www.windowsphonegeek.com/tips/working-with-itemspanel-in-wp7

RE: problem with local in xaml

posted by: winphonegeek on 11/29/2011 2:50:57 PM

You are probably missing a namespace declaration for your FieldTemplateSelector. You will need to add (at the beginning of the xaml file) something like below:

xmlns:local="clr-namespace:your-namespace-here"

Where instead of "your-namespace-here" you have to use the namespace where your FieldTemplateSelector class is.

Cool

posted by: Relevant on 12/30/2011 12:36:24 AM

Awesome, it works, thank you!

Very Useful

posted by: Vinod on 1/30/2012 4:03:18 PM

This sample has been very useful to support different listbox views - with checkbox and without checkbox). To support multiple selection for delete mode view.

posted by: F2006 on 2/14/2012 4:25:24 PM

Works great, thank you!

Add comment to 'Implementing Windows Phone 7 DataTemplateSelector and CustomDataTemplateSelector'

Comment

Our Top Articles & Free books

Our Top Tips & Samples