Simple data binding in WPF: 1 – Binding controls to objects

We’ll start having a look at data binding in WPF. Briefly, data binding is the ability to bind some property of a control (such as the text in a TextBox) to the value of a data field in an object. (More generally, you can bind controls to other data sources such as databases, but that’s more advanced.) The idea, and the need for it, are best illustrated with an example.

Suppose we want a little program that allows you to step through the integers and test each one to see if it’s a prime number or a perfect number. In case you’ve forgotten, a prime number is one whose only factors are 1 and the number itself, so 2, 3, 5, 7, 11, 13, 17 and so on are the first few prime numbers. A perfect number is one where the sum of all its factors (including 1 but excluding the number itself) equals the number itself. The smallest perfect number is 6, since its factors are 1, 2 and 3, and 1+2+3=6. The next perfect number is 28, and after that they get quite large, and very rare.

The interface for the program might look like this:

The number being considered is shown at the top. The Factors box shows the number of factors (excluding 1 and the number itself) that number has; thus a number with zero factors is prime. The ‘Sum of factors’ box shows the sum of the number’s factors (including 1, but excluding the number). A number whose ‘Sum of factors’ equals the number itself is therefore perfect.

The program should paint the background of the ‘Factors’ box LightSalmon if the number is prime, and the background of the ‘Sum of factors’ box LightSeaGreen if the number is perfect.

The Reset button resets Number to 2. The Dec button decrements Number by 1; Inc increments Number by 1, and Exit quits the program. Note that the Dec button also becomes disabled when Number is 2, since we don’t want to consider any numbers less than 2.

If the only programming technique you knew about was event handling, you can see that you would need to add handlers for each button’s Click event, and these handlers would need to update Number and the values displayed in the various TextBoxes. You would also need to handle the TextChanged event in the Number TextBox so if the user typed in a new number it would update the other boxes. Quite a bit of back-and-forth coding would be needed to keep everything synchronized.

Data binding allows the value displayed by a TextBox, or in fact pretty well any property of any control, to be bound to a data value, so that changing the data value automatically updates the property, and changing the property can also update the data value.

Let’s start with the simplest case: binding the value displayed by a TextBox to a data field in an object. To work on this project, we’ll use Expression Blend (EB) for the design and binding work, and Visual Studio (VS) for writing the classes we need to support the data binding. I’ve built the user interface using EB and won’t go into the details here; you can look here for an introduction to using EB to build interfaces. If you want the XAML code, you can download the project files; see the end of this post for the link.

We’ll need a class for storing the details of the number being considered, and for calculating the factors of that number. We’ll call that class PrimePerfect, and create it in VS. It’s a pretty standard class, except for one thing: it must be able to notify the controls when any of the relevant data fields are changed so they can be updated. In data binding, the way this is done is by writing a class that implements the INotifyPropertyChanged interface. The beginning of PrimePerfect therefore looks like this:

  using System.ComponentModel;
  ......
  class PrimePerfect : INotifyPropertyChanged  // in System.ComponentModel
  {
    public event PropertyChangedEventHandler PropertyChanged;
    protected void Notify(string propName)
    {
      if (this.PropertyChanged != null)
      {
        PropertyChanged(this, new PropertyChangedEventArgs(propName));
      }
    }
  ......

Note the using statement at the top; it is required for notification code.

Implementing INotifyPropertyChanged requires that we declare an event handler for PropertyChanged, as we’ve done on line 5. The Notify() method on line 6 takes a string parameter which is used to identify which property has changed, and the event handler is then called, passing along this parameter.

The rest of the class consists of standard code, defining the properties and calculating the factors of the number. A typical property looks like this:

    int numFactors;
    public int NumFactors
    {
      get { return numFactors; }
      set
      {
        numFactors = value;
        Notify("NumFactors");
      }
    }

The property contains the usual ‘get’ and ‘set’ clauses, but note that in the ‘set’, a call to Notify() is made, passing along a string identifying which property has been set. This property string is used to bind the text field of a TextBox to the value of NumFactors.

To see how this is done, we return to EB (make sure you save and build the project at this point, so the updates to the PrimePerfect class are available to EB). We’ll add the binding between the factorsTextBox (that displays the current value of NumFactors) and the NumFactors data field in PrimePerfect. In EB, select factorsTextBox. In ‘Common Properties’ in the right panel, find the Text entry. Click on the little square to the right of the entry, and select ‘Data binding’ from the context menu. In the dialog box that appears, click “Data field” at the top, then “+CLR Object”. This brings up another dialog that shows the available data sources for the binding. Find the namespace containing the PrimePerfect class (in the project here, I’ve called it PrimePerfectBinding), and select the PrimePerfect class, then OK. You should now see PrimePerfectDataSource appear in the Data sources column. Click on it, and PrimePerfect should appear in the Fields list on the right. Open it and select NumFactors from the list (if you can’t see it, try setting ‘Show’ to ‘All properties’ in the combo box at the bottom). Click OK and the binding is complete.

If you now look at the XAML code for factorsTextBox, you should see Text=”{Binding NumFactors}” as one of the attributes.

If you’ve been following faithfully, you’ll probably have noticed that although we’ve defined the PrimePerfect class and added notification code to it, and also added a binding to factorsTextBox, we haven’t actually created an instance of PrimePerfect yet. To remedy this, go to the C# code-behind file for MainWindow, and add a couple of lines to it as follows.

    PrimePerfect primePerfect;
    public MainWindow()
    {
      InitializeComponent();
      primePerfect = new PrimePerfect();
      baseGrid.DataContext = primePerfect;
    }

You’ll see we create an instance of PrimePerfect (its default constructor sets Number = 2). However, we’ve also added a cryptic line in which a DataContext is set to this object. What’s a DataContext?

The data context is an object that serves as the source of the data in a binding. When a control, such as factorsTextBox, was assigned a binding, all we did was specify the name of the data field (“NumFactors”) to which it is bound. We didn’t give the TextBox any information about where it should look for this data. The WPF data binding mechanism takes care of this by having a control with a data binding look for a data context. It will look first in the control itself. If a data context isn’t found there, it will look in the parent container of that control, and so on until it reaches the root container (usually the Window). What we’ve done here is attach a DataContext to the Grid that contains all the controls in the interface (‘baseGrid’ is the name of that Grid object). This means that all controls in that Grid that have data bindings will look in the same PrimePerfect object for their data. This completes the data binding process for factorsTextBox.

To get the program to run at this point, we need to complete the code in PrimePerfect so that it calculates NumFactors and SumFactors for the current Number. This code doesn’t have any bearing on the data binding so we won’t list it here, but if you want to see how it’s done, just download the source code and have a look at it.

If you run the program at this point, you should see the Factors TextBox display the current value of NumFactors, which is 0 (since 2 is the starting Number, and it’s prime). The Number and Sum of factors TextBoxes won’t show anything unless you go through the same process and bind them to the corresponding properties in PrimePerfect. There’s nothing new involved in doing this, so you can either try it yourself or just look at the source code to see how it’s done.

So far, we haven’t added any event handlers to the buttons, so let’s finish with that. We can add handlers for the buttons by double clicking on the Click event for each button in EB. This generates event handlers in MainWindow.xaml.cs, and we can enter some code in them as follows.

    private void incButton_Click(object sender, RoutedEventArgs e)
    {
      primePerfect.Number++;
    }

    private void decButton_Click(object sender, RoutedEventArgs e)
    {
      primePerfect.Number--;
    }

    private void exitButton_Click(object sender, RoutedEventArgs e)
    {
      Application.Current.Shutdown();
    }

    private void resetButton_Click(object sender, RoutedEventArgs e)
    {
      primePerfect.Number = 2;
    }

There’s nothing surprising here. The three buttons that change the value of Number do so with a single line each. Now if you run the program, you’ll see that clicking on each button does change the value of Number, and also updates Factors and Sum of factors. Why? Because whenever we change the value of Number, we are calling the ‘set’ method of the Number property in PrimePerfect, and this calls code that recalculates NumFactors and SumFactors. All of this fires a Notify event for each property, so any control that is bound to that property gets updated automatically.

At this stage, we have a program that allows us to step through the numbers and display NumFactors and SumFactors for each number. However, we still haven’t added the colour-coding of the textboxes when we find a prime or perfect number, and the Dec button will allow us to set Number lower than 2, which may have disastrous consequences if the number goes negative. We can fix all these problems using data binding as well, but that’s a topic for the next post.

Code for this post is here.

Post a comment or leave a trackback: Trackback URL.

Trackbacks

Leave a comment