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.

Share

About Jesse Liberty

Jesse Liberty is an independent consultant and programmer with three decades of experience writing and delivering software projects. He is the author of 2 dozen books and multiple Pluralsight courses, and has been 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 Xamarin Certified Mobile Developer and a Xamarin MVP, Microsoft MVP and Telerik MVP.
This entry was posted in Patterns & Skills and tagged . Bookmark the permalink.

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

  1. you’re in point of fact a excellent webmaster. The site loading speed is incredible. It seems that you’re doing any distinctive trick. Moreover, The contents are masterpiece. you’ve performed a wonderful activity in this subject!
    Cheap Snapback Hats Online http://cheapsnapbackhats1.jux.com/

  2. Pete says:

    As most of you probably found out, there’s no continuation to this tutorial on Jesse’s site (at least I could not find it). Fortunately, the entire tutorial is posted on msdn – http://msdn.microsoft.com/en-us/magazine/hh852595.aspx. Includes the entire working code including two-way binding and data converters.

    Thank you, Jessy, I have learned a ton from this!

    Happy coding!

  3. Pete says:

    It may be discussed in subsequent tutorials but I wanted to make one thing clear – the binding shown here is a one-way binding; i.e. changes to the ContentPanel.DataContext will show on the page but changes made on the page will not affect the Person object!

  4. Chucky says:

    I’ve perused some of Jesse Liberty’s blog post and bookmarked alot of them for later reference. I’ve got some experience with WP7 but wanted to get a better understanding of data binding.
    This is the first I’ve attempted to work through and I’m disappointed that he leaves out some basic info like where to declare variables and which method various bits of code go in.

    If your audience is beginners, the don’t forgot your audience!!

  5. Triven says:

    complete code :

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Net;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Animation;
    using System.Windows.Shapes;
    using Microsoft.Phone.Controls;

    namespace DataBinding
    {
    public partial class MainPage : PhoneApplicationPage
    {
    // Constructor
    public MainPage()
    {
    InitializeComponent();

    }

    private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
    {
    SetDataContext();
    Next.Click += Next_Click;
    //First();
    }

    private void SetDataContext()
    {
    ContentPanel.DataContext = GeneratePerson();
    }

    private void Next_Click(object sender, RoutedEventArgs e)
    {
    SetDataContext();
    }
    private Person GeneratePerson()
    {
    var newPerson = new Person
    {
    Beard = calculate(),
    Moustache = calculate(),
    Goatee = calculate(),
    WhichSex = Person.Sex.Male,
    Name = names[random.Next(names.Count)],
    Height = random.NextDouble() +1
    };
    return newPerson;
    }
    private Random random = new Random();

    private bool calculate()
    {
    bool output = random.Next(0, 3) % 2 == 0;
    return output;
    }

    List names = new List ()
    {
    “Amit”,
    “Ravi”,
    “Gopal”,
    “Yatin”,
    “Ambaani”
    };

  6. Triven says:

    thanks @Reid

  7. David says:

    I agree with Don. The line

    ContentPanel.DataContext = _currentPerson;

    just sort of hangs out there. Is it XAML code or C# code. From what follows I surmized it to be C# code. It that’s the case, it opens up a Pandora’s Box full of questions: What is a ContentPanel? Where is it defined? instantiated? What object is it related to in the WP Design view? Can there be more than one? etc.

  8. AbdouMoumen says:

    @Justin the issue there is that the variable “rand” has to be defined as a “class member”, meaning that it has to be either a Property or a Field, it won’t work if you define it inside a particular method.
    check here for more details on Class Members:
    http://en.csharp-online.net/Class_Members

    Hope this helps 🙂

  9. Peter says:

    Ooh, ooh, bug! Is there a finder’s fee? 🙂

    The GeneratePerson method never chooses Moonunit as the name (if it was any other name, I probably wouldn’t have noticed). The Random.Next method only chooses integers less than, and not equal to, the maximum value (http://msdn.microsoft.com/en-us/library/2dx6wyd4.aspx). So the snippet:

    Name = names[rand.Next(0, names.Count – 1)]

    should be

    Name = names[rand.Next(0, names.Count)]

    or even just

    Name = names[rand.Next(names.Count)]

  10. Reid says:

    I’ve just gone through this tutorial above, and I believe the above code for the code behind file should be:
    ____________________________
    public MainPage()
    {
    InitializeComponent();
    Loaded += MainPage_Loaded;

    }

    void MainPage_Loaded(object sender, RoutedEventArgs e)
    {

    var _currentPerson = new Person
    {
    Beard = false,
    Favorite = true,
    Goatee = false,
    Height = 1.86,
    Moustache = true,
    Name = “Jesse”,
    WhichSex = Person.Sex.Female
    };

    ContentPanel.DataContext = _currentPerson;
    }
    ______________________________
    Note that the object “ContentPanel” comes from the xaml object for the Grid x:Name=”ContentPanel”. When I created my MainPage.xaml file, the default name for me was “LayoutRoot”, so my last line was

    “LayoutRoot.DataContext = _currentPerson;”

  11. Justin says:

    I’m working through these tutorials, and wanted to point something out to anybody that might have experienced the headache I did.

    My situation is a fresh install of the free tools on a fairly fresh install of Win7. Perhaps there are some dependencies that would be commonly loaded – in my case, I’m working right out of the box.

    I was getting an error “The name ‘rand’ does not exist in the current context” The solution was to add the code:
    Random rand = new Random();

    I had to add this twice; inside each codeblock where rand was used. Hope this helps someone!

  12. Rob says:

    @Don – I’m new to C# as well. From what I can figure out, you’re creating an object (not referenced here) called:

    _currentPerson “public object _currentPerson { get; set; }

    Then after setting the values for it as shown in the details, just call that line of code and it should work.

  13. Don says:

    I don’t understand where
    ContentPanel.DataContext = _currentPerson;

    goes. I don’t have too much C# knowledge…

Leave a Reply

Your email address will not be published.