Peter McGrattan’s Weblog

Silverlight, WCF, ASP.NET, AJAX, Graphics, RIA

Customizing the Silverlight 2 ItemsControl with Templates

Posted by petermcg on February 15, 2009

(Code download)

The ItemsControl in Silverlight 2 is the base type for a number of list based UI controls:

ItemsControl Derived Types

It’s at a place in it’s class hierarchy that means it provides just enough to get you going with list output. Controls further down the inheritance hierarchy from ItemsControl tend to specialise it’s functionality by providing the ability to select or edit items in the list in slightly distinct ways. The items in an ItemsControl can be defined in Xaml or generated dynamically from the ItemsSource property in code.

Every aspect of the ItemsControl’s appearance from the exterior of the control, the panel that surrounds it’s items and the items themselves can be customized based on a pre-defined pattern or ‘template’ that you specify. Three properties on the ItemsControl allow you to specify three different types of template to describe three different aspects of the appearance of the final list:

Property Name

Type

Default Content

Template System.Windows.Controls.ControlTemplate ItemsPresenter containing StackPanel
ItemsPanel System.Windows.Controls.ItemsPanelTemplate StackPanel containing ContentPresenter per item
ItemTemplate System.Windows.DataTemplate ContentPresenter containing Grid

 
These defaults can be visualised in the following control hierarchy:

Default ItemsControl Template Output

The ‘Template’ property is inherited from Control and appropriately allows you to specify a ControlTemplate to define the appearance of everything about the ItemsControl outside it’s ItemsPresenter. The other two properties ‘ItemsPanel’ and ‘ItemTemplate’ are defined by the ItemsControl class itself and are concerned with the appearance of items inside the ItemsControl. More specifically the ‘ItemsPanel’ property controls the appearance of the panel that surrounds all the item’s ContentPresenters, the ‘ItemTemplate’ property customizes the appearance of the contents of each ContentPresenter.

Customizing the Default Templates

The following example shows how to customize the value of each of these three properties to produce a different control hierarchy. The sample application attached to this post makes use of the ItemsControl to position images not vertically in a StackPanel but at specific coordinates within a Canvas. The Xaml from the sample application is reproduced below along with the subsequent control hierarchy:

<UserControl x:Class="ItemsControlImages.Page"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:app="clr-namespace:ItemsControlImages">

    <UserControl.Resources>
        <ControlTemplate x:Key="ControlTemplateKey">
            <Border x:Name="ControlTemplate_ValueOf_Template">
                <ItemsPresenter />
            </Border>
        </ControlTemplate>

        <ItemsPanelTemplate x:Key="ItemsPanelKey">
            <Grid x:Name="ItemsPanelTemplate_ValueOf_ItemsPanel" />
        </ItemsPanelTemplate>

        <DataTemplate x:Key="ItemTemplateKey">
            <Canvas x:Name="DataTemplate_ValueOf_ItemTemplate" >
                <Image Canvas.Top="{Binding Location.Y}"
                       Canvas.Left="{Binding Location.X}"
                       Source="{Binding FlagImage}" />

                <StackPanel Canvas.Top="{Binding Location.Y}"
                            Canvas.Left="{Binding Location.X}">
                    <TextBlock Text="{Binding Title}" />
                    <TextBlock Text="{Binding Location}" />

                    <StackPanel.RenderTransform>
                        <TranslateTransform Y="-32.0" />
                    </StackPanel.RenderTransform>
                </StackPanel>
            </Canvas>
        </DataTemplate>
    </UserControl.Resources>

    <Canvas x:Name="LayoutRoot">
        <Canvas.Background>
            <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                <GradientStop Color="#FFB2C6D5"/>
                <GradientStop Color="#FF1483D9" Offset="1"/>
            </LinearGradientBrush>
        </Canvas.Background>

        <ItemsControl Template="{StaticResource ControlTemplateKey}"
                      ItemsPanel="{StaticResource ItemsPanelKey}"
                      ItemTemplate="{StaticResource ItemTemplateKey}">

            <app:Country Title="Argentina" Location="56,218" FlagImage="Images/ARG.png" />
            <app:Country Title="China" Location="368,66" FlagImage="Images/CHN.png" />
            <app:Country Title="Ireland" Location="192,90" FlagImage="Images/IRE.png" />
            <app:Country Title="New Zealand" Location="404,225" FlagImage="Images/NZ.png" />
            <app:Country Title="United States" Location="40,80" FlagImage="Images/USA.png" />

        </ItemsControl>
    </Canvas>
</UserControl>

This Xaml produces the following control hierarchy:

Customized ItemsControl Template Output

The contents of the three custom templates are defined in the Resources section of the UserControl and bound to the relevant properties of the instance of ItemsControl. Firstly the ControlTemplate specified in the Template property has been used to surround the ItemsPresenter with a blank Border. Secondly the ItemsPanelTemplate specified in the ItemsPanel property means the ItemsPresenter now has a Grid as it’s content rather than a StackPanel. Finally the DataTemplate specified in the ItemTemplate property has completely replaced the default Grid with a Canvas containing an Image and a StackPanel. The contents of the DataTemplate are interspersed with Binding expressions to extract data from each item in the ItemsControl, in this case instances of the simple Country class shown below:

using System.ComponentModel;
using System.Windows;

namespace ItemsControlImages
{
    public class Country : INotifyPropertyChanged
    {
        private string title;
        private string flagImage;
        private Point location;

        public string Title
        {
            get
            {
                return this.title;
            }
            set
            {
                if ((object.ReferenceEquals(this.title, value) != true))
                {
                    this.title = value;
                    this.RaisePropertyChanged("Title");
                }
            }
        }

        public string FlagImage
        {
            get
            {
                return this.flagImage;
            }
            set
            {
                if ((object.ReferenceEquals(this.flagImage, value) != true))
                {
                    this.flagImage = value;
                    this.RaisePropertyChanged("FlagImage");
                }
            }
        }

        public Point Location
        {
            get
            {
                return this.location;
            }
            set
            {
                if ((this.location.Equals(value) != true))
                {
                    this.location = value;
                    this.RaisePropertyChanged("Location");
                }
            }
        }

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        protected void RaisePropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler propertyChanged = this.PropertyChanged;

            if (propertyChanged != null)
            {
                propertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        #endregion
    }
}

This all conspires to produce the following UI:

Positioned ItemsControl Images

Note even though the Images are in an ItemsControl they are positioned at the coordinates shown by setting the value of the Left and Top attached properties of their parent Canvas to the value of the X and Y coordinates from the custom Location property via the Bindings specified in the DataTemplate assigned to the ItemsControl’s ItemTemplate property.

The images used in the sample application are from this article on WikiMedia and the control hierarchy images were produced from screenshots of the excellent Silverlight Spy 2 which I highly recommend.

About these ads

14 Responses to “Customizing the Silverlight 2 ItemsControl with Templates”

  1. […] more here […]

  2. Excellent blog post, thanks for publishing this.

    I loved how you used the Visual Tree from Silverlight Spy.
    It’s an excellent visualization of how the entire ItemsControl thing is just a shortcut to create a really complex repeatable visual tree.

    Great blog post, keep it up.

    — Justin Angel
    Microsoft Silverlight Program Manager

  3. Erno said

    Note even though the Images are in an ItemsControl they are positioned at the coordinates shown by setting the value of the Left and Top attached properties of their parent Canvas to the value of the X and Y coordinates from the custom Location property via the Bindings specified in the DataTemplate assigned to the ItemsControl’s ItemTemplate property.

    Would you mind explaining why that is? I don’t understand why/how is determined that the coordinates are relative to the grandparent.

  4. Hi Justin,

    Thanks for taking the time to leave your comment, you guys have done and no doubt will continue to do great inventive work with the revolutionary Silverlight framework.

    Best Regards,

    Peter

  5. Hi Erno,

    I don’t mind explaining at all; let’s take the co-ordinates for Ireland as an example.

    The coordinates assigned to the Canvas.Top and Canvas.Left attached properties by the Image and StackPanel objects (192,90) are relative to their immediate containing Canvas. As you can see from the second control hierarchy their immediate containing Canvas is a Canvas named “DataTemplate_ValueOf_ItemTemplate”.

    To understand where Ireland is eventually positioned the question then becomes where is the Canvas named “DataTemplate_ValueOf_ItemTemplate” positioned. The answer is at the very top left corner of it’s parent container: a ContentPresenter.

    Positioning based on the parent continues recursively up the Visual Tree from a ContentPresenter to the Grid, from the Grid to the ItemsPresenter, from the ItemsPresenter to the Border, from the Border to the ItemsControl and eventually from the ItemsControl to the Canvas named “LayoutRoot” which in turn is positioned relative to it’s parent, the instance of the Page UserControl assigned to the RootVisual property of the single instance of the App class that derives from Application.

    The important thing is that in this case every control in the section of the visual tree from a Canvas named “DataTemplate_ValueOf_ItemTemplate” upwards to the Canvas named “LayoutRoot” inclusive are all positioned at the very top left corner of their parent container. The outcome of this is that the co-ordinates within a Canvas named “DataTemplate_ValueOf_ItemTemplate” are positioned relative to the top left corner because all of it’s parent controls are also positioned from there.

    Altering this positioning can help you understand things further. Set the margin on the “DataTemplate_ValueOf_ItemTemplate” Canvas to Margin=”150,150,0,0″ and you will see the countries move right and down accordingly. Everything is still positioned according to it’s parent and all of the above still applies, this time however the Canvas is positioned leaving a margin between it’s top and it’s parent’s top of 150 pixels and between it’s left and it’s parent’s left of 150 pixels.

    This results in the countries being positioned at the bottom right of a square whose top left is the top left of the UserControl and whose width and height are 150, 150 pixels.

    Silverlight Spy 2 is a great tool for debugging and understanding the Silverlight Layout System.

    Regards,

    Peter

  6. Erno said

    Thank you for your explanation. Could I ask for one more thing that has been quizzing me and caused me to ask my previous question? When I set the Background of the “DataTemplate_ValueOf_ItemTemplate” canvas to Red it doesn’t show. Any idea why?

  7. Hi Erno,

    Your Background does not show because the Width and Height properties of the DataTemplate_ValueOf_ItemTemplate Canvas are zero. It’s Content is still displayed because no clipping is defined by default for the Canvas control, you can customize this of course have a look here for more.

    To get your Background to show, just give the DataTemplate_ValueOf_ItemTemplate Canvas a Width and Height along with a Brush for Background – something like this for example:

    <Canvas Width=”100″ Height=”100″ Background=”Red” x:Name=”DataTemplate_ValueOf_ItemTemplate”>

    Remember, as all the DataTemplate_ValueOf_ItemTemplate Canvas’ are eventually positioned at the very top left corner – you will only see one red square in this case as all the Canvas’ are being displayed on top of each other.

    Regards,

    Peter

  8. […] visualizing data using a listbox and ‘randomly’ place the ListBoxItems (Please read this article for understanding how to do this), you’ll notify that there’s a little friction […]

  9. Kurt said

    Thanks – this got me started on the path of ItemsControl Templates! Great stuff.

  10. Merlinox said

    How may I do it using a generic.xaml theme file? I’m studying how to do it to apply to RadTabControl of telerik.

  11. Great article! Thanks!

  12. Thomas said

    Thank you so much I needed this so bad, you have no idea

  13. Karel said

    Hi Peter,

    Very nice example.
    I have a problem converting it to support loading descendants of Control. (The images must be tab-enabled for this application, hence control descendants)
    I can create an IValueConverter to allow the ItemsControl to contain custom controls, but the problem is that the controls (your images) no longer show up.
    Do you have any idea?

    kind regards,

    Karel

  14. Ladi said

    For more on customizing an ItemsControl including writing a class inherited from ItemsControl and implementing virtualization see: http://www.ladimolnar.com/Home/ViewArticle.aspx?ArticleName=HowToCustomizeAnItemsControl. The article has a link to a sample app and sources. The sample app has a custom ItemsControl that also implements virtualization.

Sorry, the comment form is closed at this time.

 
Follow

Get every new post delivered to your Inbox.

%d bloggers like this: