[This topic is pre-release documentation and is subject to change in future releases of Microsoft Silverlight.]
Working with Data Collections in Silverlight
Introduction
Most Silverlight-based applications will be receiving and displaying data. In many cases, this will be a collection of business objects, such as stock quotes, a list of headlines or predefined objects such as a set of images. In addition to displaying a list of data, often you want to enable the user to select an item in the list and then display details about that item. This topic will show you how to display the contents of a collection, customize the display, enable item selection, and display details based on that selection
Prerequisites (available from the Silverlight download site):
-
Silverlight version 2 Beta 2.
-
Microsoft Visual Studio 2008.
-
Silverlight Tools Beta 2 for Visual Studio 2008.
Display a Collection of Business Objects
The following example shows a simple Book class with a few public properties and the ToString() method implemented. The books are stored in a generic ObservableCollection. The ObservableCollection is a good choice for data binding because it implements INotifyPropertyChanged and INotifyCollectionChanged. These interfaces provide change notification to bound targets when an item in the list changes or a property of the list itself changes. If you want your bound controls to update with changes to properties of objects in the collection, the business object should also implement INotifyPropertyChanged. For more information see Data Binding. To use this collection type, you must add a using statement for the System.Collections.ObjectModel namespace to your code-behind file.
To display the books in the ListBox, the control's ItemsSource property is set to a Binding; in this case the mode is set to OneWay, meaning that data is retrieved from the data source, but changes are not propagated back to the source. The Binding Markup Extension (Silverlight 2) overview explains the details of the binding syntax.
The Source property for the binding is not set directly, instead the DataContext for the ListBox control is set to the collection of books which provides the source for the binding. A ListBoxItem is created for each item in the collection. ToString() is automatically called on each book object to display it in the list box item. When you run the example, you will see a list of books displayed in a list box control.
CS
public partial class Page : UserControl
{
public Page()
{
InitializeComponent();
// Set the data context for the list of books
MyBooks.DataContext = AllBooks;
// Add books to the collection.
AllBooks.Add(new Book("4458907683", "Training Your Dog",
new DateTime(2000, 2, 8), 44.25));
AllBooks.Add(new Book("0446675385", "Good Owners, Great Dogs",
new DateTime(1999, 9, 1), 15.99));
}
// Create a collection to store data items.
private ObservableCollection<Book> allBooksValue;
public ObservableCollection<Book> AllBooks
{
get
{
if (allBooksValue == null)
{
allBooksValue = new ObservableCollection<Book>();
allBooksValue.Add(new Book("3390092284", "All About Dogs",
new DateTime(2004, 3, 4), 12.99));
}
return allBooksValue;
}
}
}
public class Book
{
//Define the public properties.
public string ISBN { get; set; }
public string Title { get; set; }
public DateTime PublishDate { get; set; }
public double Price { get; set; }
// Constructors.
public Book() { }
public Book(string isbn, string title,
DateTime publishdate, double price)
{
this.ISBN = isbn;
this.Title = title;
this.PublishDate = publishdate;
this.Price = price;
}
// Override ToString to display the book.
public override string ToString()
{
return Title + " ISBN: " + ISBN;
}
}
CS
<ListBox x:Name="MyBooks" Margin="5"
ItemsSource="{Binding Mode=OneWay}" />
Display Items using a Data Template
You can display items in a list using the items ToString() method, but a more common scenario is to provide a customized display of data bound items using a DataTemplate. The DataTemplate tells Silverlight how to display each item in the list. Many Silverlight controls enable you to set a data template.
The following example shows the same list of books bound to a list box. The list box is an ItemsControl which means you can establish a data template for each item it contains by setting its ItemTemplate property to a data template. In this example, the data template contains a StackPanel that in turn contains two TextBox controls; bound to the book title and ISBN number. This example uses property element syntax. To learn more about XAML syntax, go to the XAML Overview (Silverlight 2).
This XAML will display the list of books as ISBN numbers and book titles. The stack panel has horizontal orientation which will put the two text block controls side by side. For more information about control layout, see Object Positioning and Layout (Silverlight 2).
The data template establishes bindings between the text property of the text blocks and a property of the book object. This binding relies on the data context previously set to the list of books. For each bound item, the binding provides the path to the property on the data object it will be bound to—in this case, IBSN and Title.
CS
<ListBox x:Name="MyBooks" Margin="5"
ItemsSource="{Binding Mode=OneWay}" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" >
<TextBlock Text="{Binding ISBN}" Margin="0,0,50,0" />
<TextBlock Text="{Binding Title}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Adding a Details View
To display the details of an item when it is selected from a collection, you need to create the appropriate UI to display the details and bind the UI to the property of the object you want it to display. In order to do this, you set the data context for the bindings to an item in the collection, instead of the entire collection. In addition, you must update the data context as the selected item changes.
In this example, a stack panel is added to the user control that contains the existing list box. A Rectangle serves as a dividing line under the ListBox. Next is a stack panel that contains four text boxes to display the book details. The Text property of each text box is bound to a property on the Book object. To update the details view, the SelectionChanged event for the list box is handled, and in the event handler, the DataContext is set to the selected item. To ensure an item is always selected, the selected item is set to the first item in the list box when the page is initialized. When you run the sample, you will see a list of books displayed in a custom format. In addition, you can select an item in the list and see the details portion of the display update with the selected book information.
CS
<StackPanel>
...
<StackPanel>
<!--Visual division between the list and the details-->
<Rectangle HorizontalAlignment="Left" Width="400" Height="2"
Fill="Red" Margin="0,10,0,10"/>
<!--The UI for the details view-->
<StackPanel x:Name="BookDetails">
<TextBlock Text="{Binding ISBN, Mode=OneWay}" />
<TextBlock Text="{Binding Title, Mode=OneWay}" />
<TextBlock Text="{Binding PublishDate, Mode=OneWay}" />
<TextBlock Text="{Binding Price, Mode=OneWay}" />
</StackPanel>
</StackPanel>
...
</StackPanel>
CS
public Page()
{
InitializeComponent();
// You can add items to your collection.
AllBooks.Add(new Book("4458907683", "Training Your Dog",
new DateTime(2000, 2, 8), 44.25));
AllBooks.Add(new Book("0446675385", "Good Owners, Great Dogs",
new DateTime(1999, 9, 1), 15.99));
// Set the data context for the list of books
MyBooks.DataContext = AllBooks;
this.MyBooks.SelectionChanged +=
new SelectionChangedEventHandler(MyBooks_SelectionChanged);
MyBooks.SelectedIndex = 0;
}
private void MyBooks_SelectionChanged(object sender,
SelectionChangedEventArgs e)
{
ListBox myBooks = sender as ListBox;
BookDetails.DataContext = AllBooks[myBooks.SelectedIndex];
}
Providing Data Conversion
If you want to format the strings displayed in the details view, you can use a converter. For example, both the published date and the price could be displayed in a custom format. The converter is a class that derives from IValueConverter. IValueConverter has two methods; Convert(Object, Type, Object, CultureInfo) and ConvertBack(Object, Type, Object, CultureInfo). For a one-way binding from the data source to the binding target, you only need to implement the Convert(Object, Type, Object, CultureInfo) method. If it were TwoWay binding, then you would need to implement ConvertBack(Object, Type, Object, CultureInfo) as well.
Once you've created converters, you create instances of the converter classes and tell the bindings to use these instances. In this example, this is done in XAML; instances are created as static resources and assigned a key. The key is used when the converter property is set on the binding.
Since the classes are defined in the local namespace, the local namespace must be defined on the UserControl so the compiler knows where to look for the converter implementations.
The following code shows the converter implementations and how to declare and assign them to the bindings in XAML using keys. When you run the sample, you will see a list of books displayed in a custom format. In addition, the book detail displays the selected book's publish date and price in a custom format.
CS
//Create a converter to display the price as currency
public class ToCurrency : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
double amount = (double)value;
string s = culture.NumberFormat.CurrencySymbol +
amount.ToString();
return s;
}
public object ConvertBack(object value, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
//Custom class implements the IValueConverter interface
//Create a converter to display the date in a different format
public class CustomDateToString : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
DateTime date = (DateTime)value;
string s = date.Month.ToString() + "/" + date.Year.ToString();
return s;
}
public object ConvertBack(object value, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
CS
<UserControl x:Class="BookStoreWithConverters.Page"
xmlns="http://schemas.microsoft.com/client/2007"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:BookStoreWithConverters"
Width="400" Height="300">
<UserControl.Resources>
<local:CustomDateToString x:Key="DateConverter"/>
<local:ToCurrency x:Key="PriceConverter"/>
</UserControl.Resources>
...
<!--The UI for the details view-->
<StackPanel x:Name="BookDetails">
<TextBlock Text="{Binding ISBN, Mode=OneWay}" />
<TextBlock Text="{Binding Title, Mode=OneWay}" />
<TextBlock Text="{Binding PublishDate, Mode=OneWay,
Converter={StaticResource DateConverter}}" />
<TextBlock Text="{Binding Price, Mode=OneWay,
Converter={StaticResource PriceConverter}}" />
</StackPanel>