March 2012

Volume 27 Number 03

Windows Phone - Windows Phone Data Binding

By Jesse Liberty | March 2012

Virtually every meaningful Windows Phone application has some kind of data, and the ability to connect that data to elements in the UI (the view) is absolutely essential. There are various ways to do this programmatically (assigning values as you go), but one of the more powerful and essential features of XAML programming is the ability to bind data to controls. 

Here’s the good news:

  • It isn’t difficult to understand.
  • It isn’t difficult to implement.

To illustrate data binding, you’ll create the page shown in Figure1. When it loads, it will be populated from a Person object that you’ll create. Each value in the UI will be bound to a property in the Person object, and the actual binding of the data to the controls will be automatic—no C# code needed.

The Data Binding View in Windows Phone
Figure 1 The Data Binding View in Windows Phone

Getting Started

To get started, create a new Windows Phone application in Visual Studio and name it DataBinding. Begin by creating the class that will serve as the data to which you’ll be binding (also known as the DataContext). Right-click on the project and select Add | New | Class and name the class Person.cs.

Person will contain (at least) all the properties that you’ll want to display in the view. The class consists of an enumeration and a set of automatic properties, as shown in Figure 2.

Figure 2 The Person Class

public class Person
{
  public enum Sex
  {
    Male,
    Female,
  }
  public string Name { get; set; }
  public bool Moustache { get; set; }
  public bool Goatee { get; set; }
  public bool Beard { get; set; }
  public Sex WhichSex { get; set; }
  public double Height { get; set; }
  public DateTime BirthDate { get; set; }
  public bool Favorite { get; set; }
}

You can see pretty quickly how these properties will map to the various input controls shown in Figure 1. The Booleans can be either CheckBoxes or RadioButtons (depending on whether they’re mutually exclusive).

Creating the Form

The next task is to create the form you’ll use to bind the data. Right-click on the project and select “Open in Expression Blend.” As a rule, I tend to create my UI in Expression Blend and write my code in Visual Studio.

Create six rows and two columns in the content grid, and drag on the appropriate input controls. Figure 3 shows the XAML you’ll want to produce.

Figure 3 The XAML to Create the Form

<Grid
  x:Name="ContentPanel"
  Grid.Row="1"
  Margin="24,0,0,0">
  <Grid.ColumnDefinitions>
    <ColumnDefinition
      Width="0.384*" />
    <ColumnDefinition
      Width="0.616*" />
  </Grid.ColumnDefinitions>
  <Grid.RowDefinitions>
    <RowDefinition
      Height="0.1*" />
    <RowDefinition
      Height="0.1*" />
    <RowDefinition
      Height="0.1*" />
    <RowDefinition
      Height="0.1*" />
    <RowDefinition
      Height="0.1*" />
    <RowDefinition
      Height="0.1*" />
    <RowDefinition
      Height="0.2*" />
  </Grid.RowDefinitions>
  <TextBlock
    x:Name="NamePrompt"
    TextWrapping="Wrap"
    Text="Name"
    Grid.Row="0"
    HorizontalAlignment="Left"
    VerticalAlignment="Center" />
  <TextBlock
    x:Name="SexPrompt"
    Grid.Row="2"
    TextWrapping="Wrap"
    HorizontalAlignment="Left"
    VerticalAlignment="Center"
    Text="Sex" />
  <TextBlock
    x:Name="HeightPrompt"
    TextWrapping="Wrap"
    Text="Height, StringFormat=F3"
    HorizontalAlignment="Left"
    Grid.Row="3"
    d:LayoutOverrides="Height"
    VerticalAlignment="Center" />
  <TextBlock
    x:Name="FavoritePrompt"
    TextWrapping="Wrap"
    Text="Favorite"
    HorizontalAlignment="Left"
    Grid.Row="4"
    d:LayoutOverrides="Height"
    VerticalAlignment="Center" />
  <TextBox
    x:Name="Name"
    TextWrapping="Wrap"
    d:LayoutOverrides="Height"
    Grid.Column="1"
    HorizontalAlignment="Left"
    Width="200"
    VerticalAlignment="Center"
    Text="{Binding Name}" />
  <StackPanel
    x:Name="BeardStackPanel"
    Grid.ColumnSpan="2"
    Grid.Row="1"
    Orientation="Horizontal">
    <CheckBox
      x:Name="Moustache"
      Content="Moustache"
      HorizontalAlignment="Left"
      VerticalAlignment="Center"
      IsChecked="{Binding Moustache}" />
    <CheckBox
      x:Name="Goatee"
      Content="Goatee"
      IsChecked="{Binding Goatee}" />
    <CheckBox
      Content="Beard"
      IsChecked="{Binding Beard}"/>
  </StackPanel>
  <StackPanel
    x:Name="SexStackPanel"
    Grid.Column="1"
    Grid.Row="2"
    Orientation="Horizontal">
    <RadioButton
      x:Name="Male"
      Content="Male"
      IsChecked="True"
      GroupName="Sex" />
    <RadioButton
      x:Name="Female"
      Content="Female"
      GroupName="Sex" />
  </StackPanel>
  <StackPanel
    x:Name="HeightStackPanel"
    Grid.Column="1"
    Grid.Row="3"
    Orientation="Horizontal">
    <TextBlock
      TextWrapping="Wrap"
      Text="{Binding Height}"
      VerticalAlignment="Center"
      HorizontalAlignment="Left"
      Margin="0,0,0,0" />
    <TextBlock
      VerticalAlignment="Center"
      HorizontalAlignment="Left"
      Margin="5,0,0,0"
      Text="meters" />
  </StackPanel>
  <ToggleButton
    x:Name="Favorite"
    Content="Favorite"
    Grid.Column="1"
    Grid.Row="4"
    d:LayoutOverrides="Width, Height"
    HorizontalAlignment="Left"
    VerticalAlignment="Center"
    IsChecked="{Binding Favorite}" />
</Grid>

Binding

Each of the text-entry fields now has its value set using the Binding syntax. For example, to tell the TextBox to bind, identify which of its attributes will require the data—in this case, the Text attribute—and use the binding syntax, as shown earlier.

Bindings are within curly braces, and they use the keyword Binding, typically followed by the name of the property to which you’re binding the attribute. For example, this XAML states that the Text for the TextBox will be obtained from a public property named Name:

<TextBox
  x:Name="Name"
  TextWrapping="Wrap"
  d:LayoutOverrides="Height"
  Grid.Column="1"
  HorizontalAlignment="Left"
  Width="200"
  VerticalAlignment="Center"
  Text="{Binding Name}" />

Similarly, with the checkboxes for facial hair, the IsChecked property is bound to the appropriate property:

<CheckBox
  x:Name="Moustache"
  Content="Moustache"
  HorizontalAlignment="Left"
  VerticalAlignment="Center"
  IsChecked="{Binding Moustache}" />

You don’t yet know what object will have these properties (Name and Moustache). As noted earlier, the object that contains the bindable property is known as the DataContext. It can be just about anything, but in this case you’re going to create an instance of the Person class, and then you’re going to set that Person object to be the DataContext for the entire view.

Note that you can set a DataContext for a container, in this case the page, and all the view controls inside that container will share that DataContext—though you’re free to assign other Data­Contexts to one or more individual controls.

You can instantiate the Person in the Loaded event handler of the codebehind page. The Loaded event is called once the page is loaded and the controls are initialized, as shown in Figure 4.

Figure 4 The Loaded Event Is Called Upon the Page Loading

private Person _currentPerson;
private Random _rand = new Random();
public MainPage()
{
  InitializeComponent();
  Loaded += MainPage_Loaded;
}
void MainPage_Loaded( object sender, RoutedEventArgs e )
{
  _currentPerson = new Person
  {
    Beard = false,
    Favorite = true,
    Goatee = false,
    Height = 1.86,
    Moustache = true,
    Name = "Jesse",
    WhichSex = Person.Sex.Male
  };
}

Now you can set the DataContext for every control in the ContentPanel to be the _currentPerson object you just instantiated (in the Loaded event handler):

ContentPanel.DataContext = _currentPerson;

Once it knows its DataContext, the TextBox can resolve the Name property and obtain the value (“Jesse”) to display. The same is true for all the other controls, each bound to a property in the new Person object.

Run the application and you should see all the fields bound appropriately.

Changing the DataContext

To drive home the relationship between the binding and the display, let’s create a number of Person objects and display them one by one. To do this, modify MainPage.xaml.cs to create a list of (randomly created) Persons and then iterate through the list with a new “Next” button on the UI, which you should add to the bottom row: 

<Button
  Name="Next"
  Content="Next"
  Grid.Row="5"
  HorizontalAlignment="Center"
  VerticalAlignment="Center" />

Here’s the modified code to interact with the Next button:

void MainPage_Loaded( object sender, RoutedEventArgs e )
{
  SetDataContext();
  Next.Click += Next_Click;
}
private void SetDataContext()
{
  ContentPanel.DataContext = GeneratePerson();
}
void Next_Click( object sender, RoutedEventArgs e )
{
  SetDataContext();
}

Notice both the page loaded event and the click event handler for the Next button must set the DataContext, so I’ve factored that out into a separate method: SetDataContext. That method, in turn, calls the GeneratePerson method, whose job is to create a Person at random.

You can now make all the changes in the codebehind. First, stop hardwiring the current Person, and instead set it by calling the GeneratePerson method.

Generating a Random Person

Here’s the entire GeneratePerson method; you’ll see that I’ve factored out the task of choosing true versus false into a method called FlipCoin:

private Person GeneratePerson()
{
  var newPerson = new Person
  {
    Beard = FlipCoin(),
    Favorite = FlipCoin(),
    Goatee = FlipCoin(),
    Height = _rand.NextDouble() + 1,
    Moustache = FlipCoin(),
    Name = names[_rand.Next(0, names.Count - 1)]
  };
  return newPerson;
}

FlipCoin uses the random number generator to return true 50 percent of the time:

private  bool FlipCoin()
{
  return _rand.Next( 1, 3 ) % 2 == 0;
}

Finally, to pick a name, create a list of half a dozen names that can be assigned to men or women, and use the random number generator to pick an offset into the list:

private readonly List<string> names = new List<string>()
{
  "Stacey",
  "Robbie",
  "Jess",
  "Robin",
  "Syd",
  "J.J.",
  "Terri",
  "Moonunit",
};

Run the application and click the Next button. As each Person object is created it’s set as the DataContext and its properties are bound to the UI controls.

INotifyPropertyChanged

What happens if one of the properties on your Person object changes? This could easily happen if the object is contained in a database and other users have access to the same object. You would want your UI to be updated.

For this to work, your class (the DataContext) must implement INotifyPropertyChanged—a simple interface that allows each property to notify the UI when its value changes. It’s common to create a helper method that checks to make sure the event has at least one method registered to it. If so, the helper method raises the event, passing in the name of the property that updated.

To see this at work, add a new button to the UI named Change. When you click the Change button, change the Name property to “Jacob”:

void Change_Click( object sender, RoutedEventArgs e )
{
  _currentPerson.Name = "Jacob";
}

This will have no effect unless Person implements INotifyPropertyChanged and the Name property raises the PropertyChanged event, as shown in Figure 5.

Figure 5 The INotifyPropertyChanged Interface

public class Person : INotifyPropertyChanged
{
  public string _name;
  public string Name
  {
    get { return _name; }
    set
    {     
      _name = value;
      PropChanged( "Name" );
    }
  }
// Other properties
  public event PropertyChangedEventHandler PropertyChanged;
  private void PropChanged(string propName)
  {
    if (PropertyChanged != null)
    {
      PropertyChanged( this, new PropertyChangedEventArgs( propName ) );
    }
  }
}

With this in place, when you click the Change button the name shown in the UI will change to the new name in the object (Jacob).

Two-Way Binding

What if the user interacts with the UI and changes a value directly (for example, types a new name into the Name TextBox)? You’ll (usually) want that change pushed back to the underlying data (the DataContext object). To do that, you’ll use two-way binding.

To modify the program to use two-way binding on the Name property, find the Name binding and modify it to this:

<TextBox
  x:Name="Name"
  TextWrapping="Wrap"
  d:LayoutOverrides="Height"
  Grid.Column="1"
  HorizontalAlignment="Left"
  Width="200"
  VerticalAlignment="Center"
  Text="{Binding Name, Mode=TwoWay}" />

There are three modes for binding:

  1. One-time binding means that the data is bound but then never updated, even if the data is updated by the user.
  2. One-way binding is the default; data is pulled from the source to the UI but not pushed back to the source.
  3. Two-way binding allows data to be pulled from the source and pushed back to the source if modified in the UI.

Element Binding

Change the row for the Next button to row six and drag a Slider control onto row five. There are a number of settings that are important in a slider, including:

  • Minimum
  • Maximum
  • Value
  • LargeChange
  • SmallChange

You can see these reflected in the code in Figure 6.

Figure 6 Adding a Slider

<Slider
  x:Name="Likability"
  Grid.Row="5"
  Grid.Column="0"
  BorderBrush="White"
  BorderThickness="1"
  Background="White"
  Foreground="Blue"
  LargeChange="10"
  SmallChange="1"
  Minimum="0"
  Width="199"
  Maximum="100"
  Value="50"
  Height="90" />

Minimum and Maximum set the range of the slider. In this case, because I’m using percentages, I’ve set them to 0 and 100, respectively.

Value is the current value of the slider, and will be: Minimum <= value <= Maximum

LargeChange and SmallChange are used much as they are in scrollbars; they indicate what clicking into the slider will do and what using another control (perhaps arrows) to incrementally move the slider will do, respectively.

Setting up the TextBlocks

In the right-hand column, you’ll use three TextBlocks; the first and third as fixed labels (with the values “Likeability:” and “%,” respectively). The middle TextBlock displays a numeric representation of the value of the slider.

To accomplish this, bind the Text Value property of the middle TextBlock to the Value property of the slider, identifying which element you’re binding to with the keyword ElementName, as shown in Figure 7.

Figure 7 Binding the Value Property of a TextBlock

<StackPanel
  x:Name="LikeabilityPercentStackPanel"
  Grid.Row="5"
  Grid.Column="1"
  Orientation="Horizontal">
  <TextBlock
    Text="Likeability: "
    HorizontalAlignment="Left"
    VerticalAlignment="Center"
    Margin="20,0,5,0" />
  <TextBlock
    x:Name="SliderValue"
    Text="{Binding Value, ElementName=Likeability, StringFormat=F3}"
    HorizontalAlignment="Left"
    VerticalAlignment="Center"
    Margin="5,0,0,0"/>
  <TextBlock
    Text="%"
    HorizontalAlignment="Left"
    VerticalAlignment="Center" />
</StackPanel>

Run the program. As the Slider is adjusted, the value in the TextBlock is updated instantly.

Data Converters

Some properties won’t bind properly to a given UI control, or you might want greater control over how the value is displayed. As a simple example, let’s display the user’s birth date by moving the buttons down a row and inserting a row with a prompt (“Birth date”) and the value of the Person’s BirthDate.

To do this, you have to modify the GeneratePerson method in MainPage.xaml.cs to generate a valid BirthDate, which you do by adding this line to create a random BirthDate within the past 20 years:

BirthDate = DateTime.Now - TimeSpan.FromDays(_rand.Next(1,365*20)),

If you just bind to the BirthDate property, you’ll see the birth date and time. But you don’t want the time—just the date, in short date format. To accomplish this, you need a DataConverter.

DataConverters are classes that implement IValueConverter. This interface requires two methods, shown in Figure 8.

Figure 8 The IValueConverter Interface

public object Convert(
  object value,
  Type targetType,
  object parameter,
  System.Globalization.CultureInfo culture )
{
  throw new NotImplementedException();
}
public object ConvertBack(
  object value,
  Type targetType,
  object parameter,
  System.Globalization.CultureInfo culture )
{
  throw new NotImplementedException();
}

In this case, you only need the first of the two (the second will never be called). The method is pretty simple to implement; make sure the target type is string and the value type is DateTime. If so, take the value, cast it to a DateTime and then call ToShortDateString on it, as show in Figure 9.

Figure 9 The Convert Method to Shorten a DateTime

public object Convert(
  object value,
  Type targetType,
  object parameter,
  System.Globalization.CultureInfo culture )
{
  if (targetType == typeof( string ) &&
    value.GetType() == typeof( DateTime ))
  {
    return (( DateTime ) value).ToShortDateString();
  }
  else  // Unable to convert
  {
    return value;
   }
}
public object ConvertBack(
  object value,
  Type targetType,
  object parameter,
  System.Globalization.CultureInfo culture )
{
  throw new NotImplementedException();
}

With this in place, you need a way for the XAML to access the value converter; you can accomplish this by making the converter a resource. Open App.xaml and add a namespace for your converter, based on the namespace of your application:

xmlns:mine="clr-namespace:DataBinding">

Next, in the same file, find the <Application.Resource> section and add a resource for your value converter:

<Application.Resources>
  <mine:DateConverter   x:Key="dateConverter" />
</Application.Resources>

You can now use the key in your XAML file. Update the Binding for the BirthDate to use the resource:

<TextBlock
  Grid.Row="6"
  Grid.Column="1"
  VerticalAlignment="Center"
  Text="{Binding BirthDate, Converter={StaticResource dateConverter}}" />

Run the program and you should see the date shown in short date format.

Powerful Applications

Data binding allows you to create powerful Windows Phone applications that reliably manage the relationship between underlying data and the controls and views that display that data. In this article you saw how to create simple data binding and two-way data binding, how to bind to elements and how to use data converters to massage the data into the format you want.


Jesse Liberty is a senior developer-community evangelist on the Windows Phone team. Liberty hosts the popular Yet Another Podcast (jesseliberty.com/podcast), and his blog (jesseliberty.com) is required reading. He’s the author of numerous best-selling books, including “Programming Reactive Extensions and LINQ” (Apress, 2011) and “Migrating to Windows Phone” (Apress, 2011). You can follow Jesse on Twitter at twitter.com/JesseLiberty.

Thanks to the following technical expert for reviewing this article: Jason Shaver