Quickstart: Data binding to controls for Windows Phone
[ This article is for Windows Phone 8 developers. If you’re developing for Windows 10, see the latest documentation. ]
Most Windows Phone apps display data in controls. In many cases, the data are a business object or a collection of business objects such as stock quotes, headlines, or images. In addition, you often want to enable the user to select an item from a list and then display details about that item in another control, such as a text box.
This Quickstart shows you how to bind a control to a single item and to bind a list control to a collection of items. In addition, this Quickstart shows you how to customize the display of control items, implement a details view based on a selection, and convert data for display.
This topic contains the following sections.
- Binding a control to a single item
- Binding a control to a collection of objects
- Displaying items in a control by using a data template
- Adding a details view
- Converting data for display in controls
- Related Topics
Binding a control to a single item
A data binding consists of a target and a source. The target of a binding typically is a control property. The target must be a DependencyProperty.
The following shows an example of binding a control to a single item. The target is the Text property of a TextBox control. The source is a simple music Recording class.
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<TextBox VerticalAlignment="Top" IsReadOnly="True" Margin="5"
TextWrapping="Wrap" Height="120" Width="400"
Text="{Binding}" x:Name="textBox1" />
</Grid>
// Constructor
public MainPage()
{
InitializeComponent();
// Set the data context to a new Recording.
textBox1.DataContext = new Recording("Chris Sells", "Chris Sells Live",
new DateTime(2008, 2, 5));
}
// A simple business object
public class Recording
{
public Recording() { }
public Recording(string artistName, string cdName, DateTime release)
{
Artist = artistName;
Name = cdName;
ReleaseDate = release;
}
public string Artist { get; set; }
public string Name { get; set; }
public DateTime ReleaseDate { get; set; }
// Override the ToString method.
public override string ToString()
{
return Name + " by " + Artist + ", Released: " + ReleaseDate.ToShortDateString();
}
}
' Constructor
Public Sub New()
InitializeComponent()
' Set the data context to a new recording.
textBox1.DataContext = New Recording("Chris Sells", "Chris Sells Live", _
New DateTime(2008, 2, 5))
End Sub
Public Class Recording
Public Sub New()
End Sub
Public Sub New(ByVal artistName As String, ByVal cdName As String, _
ByVal release As DateTime)
Artist = artistName
Name = cdName
ReleaseDate = release
End Sub
Public Property Artist As String
Public Property Name As String
Public Property ReleaseDate As DateTime
' Override ToString.
Public Overloads Overrides Function ToString() As String
Return Name + " by " + Artist + ", Released: " + releaseDate.ToShortDateString()
End Function
End Class
When you run the app, it will look something like this:
To display a music recording in a TextBox, the control's Text property is set to a Binding by using a markup extension. In this example, the binding mode is OneWay by default, which means that data are retrieved from the source, but changes are not propagated back to the source.
The Recording class has three public properties and a ToString method override. The properties are Artist, Name, and ReleaseDate. The ToString method is significant, because if no formatting is specified, the ToString method is called on a bound object for display purposes. The Source property for the binding is not set directly; instead, the DataContext property for the TextBox control is set to a new Recording object.
Binding a control to a collection of objects
The previous example demonstrates the syntax you use to bind data to controls, but it isn't very realistic. A more common scenario is to bind to a collection of business objects. The generic ObservableCollection<(Of <(T>)>) class is a good collection choice for data binding, because it implements the INotifyPropertyChanged and INotifyCollectionChanged interfaces. These interfaces provide change notification to bound controls 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.
The following example binds a collection of music Recording objects to a ListBox.
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<ListBox x:Name="ListBox1" ItemsSource="{Binding}"
Height="200" Width="400"/>
</Grid>
public ObservableCollection<Recording> MyMusic = new ObservableCollection<Recording>();
public MainPage()
{
InitializeComponent();
// Add items to the collection.
MyMusic.Add(new Recording("Chris Sells", "Chris Sells Live",
new DateTime(2008, 2, 5)));
MyMusic.Add(new Recording("Luka Abrus",
"The Road to Redmond", new DateTime(2007, 4, 3)));
MyMusic.Add(new Recording("Jim Hance",
"The Best of Jim Hance", new DateTime(2007, 2, 6)));
// Set the data context for the list box.
ListBox1.DataContext = MyMusic;
}
Public MyMusic As New ObservableCollection(Of Recording)()
Public Sub New()
InitializeComponent()
' Add items to the collection.
MyMusic.Add(New Recording("Chris Sells", "Chris Sells Live", _
New DateTime(2008, 2, 5)))
MyMusic.Add(New Recording("Luka Abrus", "The Road to Redmond", _
New DateTime(2007, 4, 3)))
MyMusic.Add(New Recording("Jim Hance", "The Best of Jim Hance", _
New DateTime(2007, 2, 6)))
' Set the data context for the list box.
ListBox1.DataContext = MyMusic
End Sub
To display the music recordings in the ListBox, the control's ItemsSource property is set to a Binding, and the DataContext property for the ListBox control is set to the collection of Recording objects, which provides the source for the binding. A ListBoxItem is created for each item in the collection. ToString is automatically called on each Recording object to display it in the list box item.
Displaying items in a control by using a data template
You can display items in a list by using the item's ToString method. However, a more common scenario is to provide a customized display of data bound items by using a DataTemplate which enables you to customize how list items are displayed in a control. Typically, you set the data template by using the ContentTemplate property of a content control or the ItemTemplate property of an items control.
The following example shows the same list of recordings bound to a list box by using a data template. A list box is an ItemsControl, which means that you establish a data template for each item by setting its ItemTemplate property to a data template.
<ListBox x:Name="ListBox1" Margin="5"
Width="450" Height="200" HorizontalAlignment="Left"
ItemsSource="{Binding}" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="2">
<TextBlock Text="Artist:" Margin="2" />
<TextBlock Text="{Binding Artist}" Margin="2" />
<TextBlock Text="CD:" Margin="10,2,0,2" />
<TextBlock Text="{Binding Name}" Margin="2" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
In the XAML, you can see the data template definition. The data template contains a StackPanel with four TextBlock controls. The stack panel has a horizontal orientation so that the four text block controls appear side by side. Two of the TextBlock controls are bound to the Artist and Name properties of a Recording object. The other two TextBlock controls display static text. For each bound item, the binding provides the path to the property on the Recording object. As in the previous example, this binding relies on the data context to be set to the list of recordings.
Adding a details view
To display the details of an item when it is selected from a collection, you have to create the appropriate UI and bind the UI to the data that you want it to display. Additionally, you must use a CollectionViewSource as the data context to enable the details view to bind to the current item.
The following example shows the same list of recordings, but this time the list is the data source of a CollectionViewSource instance. The data context of the layout root is set to the collection view source, and the list box and details view inherit the data context from the layout root. This enables the list box to display the same list of items while the details view shows information about the current item.
<!--The UI for the details view-->
<StackPanel x:Name="RecordingDetails">
<TextBlock FontWeight="Bold" Text="{Binding Artist}" Margin="5,0,0,0"/>
<TextBlock FontStyle="Italic" Text="{Binding Name}" Margin="5,0,0,0"/>
<TextBlock Text="{Binding ReleaseDate}" Margin="5,0,0,0" />
</StackPanel>
//ListBox1.DataContext = MyMusic;
LayoutRoot.DataContext = new CollectionViewSource { Source = MyMusic };
'ListBox1.DataContext = MyMusic
LayoutRoot.DataContext = New CollectionViewSource With {.Source = MyMusic}
In this example, a StackPanel is added that contains three text blocks to display the recording details. The Text property of each text block is bound to a property on the Recording object.
Converting data for display in controls
If you want to format and display a non-string type in a control, such as a TextBox, you can use a converter. For example, you could display a label and a formatted date instead of displaying just the date. (Note that you can use the StringFormat property if you just want to format the date by itself.) The following example shows a converter implementation for the release date in the list of recordings.
<phone:PhoneApplicationPage
x:Class="PhoneApp1.MainPage"
...
xmlns:local="clr-namespace:PhoneApp1"
...
>
<phone:PhoneApplicationPage.Resources>
<local:StringFormatter x:Key="StringConverter"/>
</phone:PhoneApplicationPage.Resources>
...
<!--ContentPanel - place content here-->
<StackPanel x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<ListBox x:Name="ListBox1" Margin="5"
Width="450" Height="200" HorizontalAlignment="Left"
ItemsSource="{Binding}" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="2">
<TextBlock Text="Artist:" Margin="2" />
<TextBlock Text="{Binding Artist}" Margin="2" />
<TextBlock Text="CD:" Margin="10,2,0,2" />
<TextBlock Text="{Binding Name}" Margin="2" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<!--The UI for the details view-->
<StackPanel x:Name="RecordingDetails">
<TextBlock Text="{Binding Artist}" />
<TextBlock Text="{Binding Name}" />
<TextBlock Text="{Binding ReleaseDate,
Converter={StaticResource StringConverter},
ConverterParameter=Released: \{0:D\}}" />
</StackPanel>
</StackPanel>
public class StringFormatter : IValueConverter
{
// This converts the value object to the string to display.
// This will work with most simple types.
public object Convert(object value, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
// Retrieve the format string and use it to format the value.
string formatString = parameter as string;
if (!string.IsNullOrEmpty(formatString))
{
return string.Format(culture, formatString, value);
}
// If the format string is null or empty, simply
// call ToString() on the value.
return value.ToString();
}
// No need to implement converting back on a one-way binding
public object ConvertBack(object value, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Public Class StringFormatter
Implements IValueConverter
' This converts the DateTime object to the string to display.
Public Function Convert(ByVal value As Object, ByVal targetType As Type, _
ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) _
As Object Implements IValueConverter.Convert
' Retrieve the format string and use it to format the value.
Dim formatString As String = TryCast(parameter, String)
If Not String.IsNullOrEmpty(formatString) Then
Return String.Format(culture, formatString, value)
End If
' If the format string is null or empty, simply call ToString()
' on the value.
Return value.ToString()
End Function
' No need to implement converting back on a one-way binding
Public Function ConvertBack(ByVal value As Object, ByVal targetType As Type, _
ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) _
As Object Implements IValueConverter.ConvertBack
Throw New NotImplementedException()
End Function
End Class
A converter is a class that derives from the IValueConverter interface which has two methods: Convert and ConvertBack. For a one-way binding from the data source to the binding target, you only have to implement the Convert method. The converter in this example is fairly generic. You can pass the desired string format as a parameter, and the converter uses the Format method to perform the conversion. If no format string is passed, the converter returns the result of calling ToString on the object.
Once you implement the converter, you create an instance of the converter class and tell the bindings to use this instance. In this example, this is performed in XAML. An instance of the converter is created as a static resource and assigned a key. The key is used when the converter property is set on the binding. The converter in this example is fairly generic. You can pass the desired string format as a parameter, and the converter uses the Format method to perform the conversion. If no format string is passed, the converter returns the result of calling ToString on the object.