Navigate back home
GalaSoft Laurent Bugnion
WPF: Example of ItemsControl bound to an ObservableCollection
Introduction:

One of the most exciting feature I learned about in the WPF course two weeks ago was that you can bind an ObservableCollection's content to just any type of Panel, by using an ItemsControl and the ItemsPanelTemplate property.

In order to check if I had understood the technique properly, I made a small working example. It actually worked out pretty well, so well in fact that I decided to publish it, hoping that it can help someone out there.

The business object: DependencyObject

There are various ways to add a DependencyProperty to a CLR object. In this example, I simply derive my business object from DependencyObject. This way adding a DependencyProperty is very easy. Note that the property Title is here just for convenience. The actual value is not stored in the object, but in the WPF framework, because the DependencyProperty is registered with it. Note the use of GetValue and SetValue.

public class MyDataItem : DependencyObject { // Note how the TitleProperty // is a DependencyProperty, // and the Title CLR property is // here just for convenience. The string itself // is not stored in the object, it's // stored in the WPF framework. public string Title { get { return (string) GetValue( TitleProperty ); } set { SetValue( TitleProperty, value ); } } // Using a DependencyProperty as the backing store for Title. // This enables animation, styling, binding, etc... public static readonly DependencyProperty TitleProperty = DependencyProperty.Register( "Title", typeof( string ), typeof( MyDataItem ), new UIPropertyMetadata( "" ) ); public MyDataItem( string title ) { Title = title; } }
MyDataItem implementation
ObservableCollection in the UI layer

In this example, the examples are stored in an ObservableCollection. This built-in generic collection is great for databinding, because it raises events automatically when an item is added or removed. If you databind such a collection to the ItemsSource property of an ItemsControl, you don't need to worry about adding or removing UI elements from the panel, the WPF framework takes care of that for you.

private ObservableCollection<MyDataItem> dataItems; public ObservableCollection<MyDataItem> DataItems { get { return dataItems; } }
In the page: ObservableCollection

The rest of the Page code-behind is pretty straight forward: Initializing the ObservableCollection, adding a few items for the example, and handling the buttons' events.

There are four buttons: One adds an item to the collection. One removes an item from the collection. One increases the index on the second item in the collection (if available). The last button decreases that same index.

Note that there are strictly no interaction between these buttons and the UI display. The interaction happens on the ObservableCollection and the MyDataItem instances contained in that collection. The rest occurs strictly through data binding.

WPF magic: the data binding

In order to update the UI automatically when the collection gets updated it needs a little XAML work.

First, we specify how the data items must be represented in the UI. This is the step I love, because it's when you take abstract data and give them a shape. To do this, we use a DataTemplate and add it in the Page's resources. The DataTemplate here defines a Border with rounded corners, with a gradient as background, and containing a TextBlock. The Content of the TextBlock is bound directly to the Title property of the data item.

Note that this works because the data context of the DataTemplate will be automatically set when it is actually bound to the data item. For the moment, we only work on resources, so there is no binding yet.

<DataTemplate x:Key="itemsTemplate"> <Border Width="120" Height="50" CornerRadius="10,10,10,10" Margin="4,0,4,0" Padding="4,4,4,4"> <Border.Background> <LinearGradientBrush EndPoint="1,0.5" StartPoint="0,0.5"> <GradientStop Color="#FF767676" Offset="0"/> <GradientStop Color="#FFFF0000" Offset="1"/> </LinearGradientBrush> </Border.Background> <Grid Width="Auto" Height="Auto"> <Grid.ColumnDefinitions> <ColumnDefinition Width="39.942"/> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <TextBlock HorizontalAlignment="Center" Margin="0,0,0,0" VerticalAlignment="Center" Grid.Column="1" FontSize="14" FontWeight="Bold" Foreground="#FFFFFFFF" TextWrapping="Wrap" Text="{Binding Title}"/> </Grid> </Border> </DataTemplate>
DataTemplate, giving a shape to a data item
Finding a parent for the items: ItemsControl

After the items have been shaped, we need to place them on a panel of some sort. Think of the way a ListBox works: You bind its ItemsSource property to a collection, and you specify how the items should be displayed. Well, ListBox derives from ItemsControl, and you can shape that "invisible" container to to look like another panel. To do this, we use the ItemsPanelTemplate property, and we define how the ItemsControl must be represented. In our case, we use a simple horizontal StackPanel.

Note how the ItemsSource property is bound to the ObservableCollection contained in the page. This is where the link between the Business Logic Layer and the UI Layer is done. To simplify the syntax, we simply gave a x:Name to the Window, and then we use the ElementName/Path syntax in the binding.

Note also how we tell the ItemsControl that the items must be represented using the DataTemplate we defined earlier. The ItemTemplate property is used for this. This is where the DataTemplate's data context is set to be the data item itself.

<ItemsControl Grid.Column="1" Grid.Row="0" Width="Auto" Height="Auto" ItemsSource="{Binding ElementName=mainWindow, Path=DataItems}" ItemTemplate="{StaticResource itemsTemplate}" Grid.ColumnSpan="2"> <ItemsControl.ItemsPanel> <ItemsPanelTemplate> <StackPanel Orientation="Horizontal" /> </ItemsPanelTemplate> </ItemsControl.ItemsPanel> </ItemsControl>
ItemsControl, defining a panel for the UI items

There's nothing more to it! The rest of the code is only there to simulate actions on the Business Logic Layer. I hope that this simple example helps you understand better how data binding works, and how ItemsControl and ObservableCollection can be used to display a various number of items without having to write a lot of code behind.

Resources

Click to see the code.
Right-click and "Save as..." to save locally.

History

Date Version Description
12.04.2007 V1.0.0 First published version