Windows Phone From Scratch #6 – Data Binding (Really)

Whoa! Insurrection on this series! Fair enough, it is based on the quite legitimateiStock_connectTwoWiresXSmall complaint that the mini-tutorials were too mini and not enough tutorial.  To make up for this deficiency, this mini-tutorial will cover steps 2 and 3 described in the previous mini-tutorial: creating the form and basic data binding.

Let’s start by creating the form.  Based on the work you did earlier in the series, you should feel reasonably comfortable opening Blend and creating 6 rows and two columns in the content grid, and dragging on the appropriate input controls.  Here’s the Xaml for those of you who would just as soon work in Visual Studio,

<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"
      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>

Notice that there are a few simplifications made for this tutorial. The slider has been removed from Height, and the Birthdate is not being shown. Both of these omissions will be addressed in the next mini-tutorial.

Binding

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

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

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

We don’t yet know what object that property (Name) will be on.  The object that contains the bindable property is known as the DataContext.  It can be just about anything, but in our case, we’re going to create a person object, and then we’re going to set that person object to be the data context.

We can instantiate the person in the Loaded event handler of the code behind page. The Loaded even is called once the page is loaded and the controls are initialized.

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
                   };
}

We can now set the data context for ever control in the ContentPanel to be the _currentPerson object we just instantiated

ContentPanel.DataContext = _currentPerson;

Once it knows its datacontext, the TextBox can resolve the Name property and obtain the value (“Jesse”) to display.

We do the same with each of the other controls, setting their contents based on properties in the _currentPerson object.

Binding to Different Objects

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 we’ll modify MainPage.xaml.cs to create a list of (randomly created) Persons and then we’ll iterate through the list with a new “Next” button on the UI.

A few issues arise right away that will force us to simplify for this mini-tutorial and to pick up the complexity in the next.   Both the person’s BirthDate and sex require a bit of manipulation to work with this simple approach (e.g., how do you translate a boolean into which CheckBox is checked?)  It isn’t hard to handle this, and data binding provides a mechanism, but let’s save that for the next mini-tutorial.

We can now make all the changes in the code behind.  First, we’ll stop hardwiring the current person, and instead we’ll set it by calling the GeneratePerson method (that we’ll review in a moment).

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 that both the page loaded event and the click event handler for the Next button must both set the data context, and so I’ve factored that out into a separate method: SetDataContext.  That method, in turn calls our GeneratePerson method, whose job is to create a person at random.

Generating A Random Person

Here is the entire GenerateNewPerson method; you’ll see that I’ve factored out the task of choosing true vs. 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 just uses the random number generator to return true 50% of the time:

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

Finally, to pick a name, I created a list of half a dozen names that can be assigned to men or women, and we 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",
                    };

The net effect is that each time the Next button is pressed a new Person is generated and immediately bound to the fields.

About Jesse Liberty

Jesse Liberty has three decades of experience writing and delivering software projects and is the author of 2 dozen books and a couple dozen online courses. His latest book, Building APIs with .NET will be released early in 2025. Liberty is a Senior SW Engineer for CNH and he was a Senior Technical Evangelist for Microsoft, a Distinguished Software Engineer for AT&T, a VP for Information Services for Citibank and a Software Architect for PBS. He is a Microsoft MVP.
This entry was posted in Patterns & Skills and tagged . Bookmark the permalink.

14 Responses to Windows Phone From Scratch #6 – Data Binding (Really)

Comments are closed.