Click here to Skip to main content
15,860,972 members
Articles / Desktop Programming / WPF

Moving Toward WPF Data Binding One Step at a Time

Rate me:
Please Sign up or sign in to vote.
4.92/5 (110 votes)
19 May 2008CPOL6 min read 349K   3.8K   136   74
A gradual introduction to the world of WPF data binding.

Introduction

This article explains the absolute basics of WPF data binding. It shows four different ways to perform the same simple task. Each iteration moves closer to the most compact, XAML-only implementation possible. This article is for people with no experience in WPF data binding.

Background

Programming in WPF involves a lot of data binding. WPF user interfaces typically use much more data binding than most Windows Forms or ASP.NET user interfaces. Most, if not all, data movement in the user interface is accomplished with data binding. This article should help WPF newbies to start thinking in terms of WPF data binding, by showing how to translate a code-only solution into a compact XAML-only solution.

This article does not discuss the binding API much. It only discusses what is relevant to the simple example. If you would like to read more about the technical details of WPF data binding, you can read my article about it here.

The Demo App

Throughout this article, we will examine several ways to implement the same simple functionality. Our goal is to create a WPF program that allows us to edit a person’s first and last name. The application should also display that person’s name, formatted as <LastName>, <FirstName>. The formatted name should immediately update whenever the first or last name changes.

The user interface should look something like this:

screenshot.png

Version 1 – Manually Moving Data

First, we will not use data binding to implement this. Let’s create a simple class to hold the person’s name:

C#
public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }

    public string FullName
    {
        get
        {
            return String.Format("{0}, {1}",
                this.LastName, this.FirstName);
        }
    }
}

Next, we declare a simple user interface in XAML. These controls will display the three properties of our Person class. They exist in our application’s main Window:

XML
<StackPanel>
  <TextBox 
    x:Name="firstNameTextBox" 
    Width="200" 
    Margin="0,4" 
    />

  <TextBox 
    x:Name="lastNameTextBox" 
    Width="200" 
    Margin="0,4" 
    />

  <TextBlock 
    x:Name="fullNameTextBlock" 
    Background="LightBlue" 
    Margin="0,4" 
    />
</StackPanel> 

Finally, we can write some code in the Window’s code-behind file to manually move the data around as necessary:

C#
Person _person;

// This method is invoked by the Window's constructor.
private void ManuallyMoveData()
{
    _person = new Person
    {
        FirstName = "Josh",
        LastName = "Smith"
    };

    this.firstNameTextBox.Text = _person.FirstName;
    this.lastNameTextBox.Text = _person.LastName;
    this.fullNameTextBlock.Text = _person.FullName;

    this.firstNameTextBox.TextChanged += firstNameTextBox_TextChanged;
    this.lastNameTextBox.TextChanged += lastNameTextBox_TextChanged;
}

void lastNameTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
    _person.LastName = this.lastNameTextBox.Text;
    this.fullNameTextBlock.Text = _person.FullName;
}

void firstNameTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
    _person.FirstName = this.firstNameTextBox.Text;
    this.fullNameTextBlock.Text = _person.FullName;
}

Bugs are born in this type of code, like a swamp. This implementation requires the UI code to keep track of what controls need to be updated when certain property values change. This forces us to duplicate knowledge of the problem domain in our UI code, which is never a good thing. If we were dealing with a more complex problem domain, this type of code can get very ugly very fast. There must be a better way…

Version 2 – Binding in Code

Using the exact same XAML in our Window, let’s rewrite the code-behind so that the controls are data bound to the Person object. Instead of having the Window’s constructor call the ManuallyMoveData method, as seen before, now it will call this method instead:

C#
private void BindInCode()
{
    var person = new Person
    {
        FirstName = "Josh",
        LastName = "Smith"
    };

    Binding b = new Binding();
    b.Source = person;
    b.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
    b.Path = new PropertyPath("FirstName");
    this.firstNameTextBox.SetBinding(TextBox.TextProperty, b);

    b = new Binding();
    b.Source = person;
    b.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
    b.Path = new PropertyPath("LastName");
    this.lastNameTextBox.SetBinding(TextBox.TextProperty, b);

    b = new Binding();
    b.Source = person;
    b.Path = new PropertyPath("FullName");
    this.fullNameTextBlock.SetBinding(TextBlock.TextProperty, b);
}

In this version, we are no longer directly assigning values to the Text property of a TextBox or TextBlock. Now we are binding those properties on the controls to a property on the Person object. The Binding class is part of WPF; in fact, it is a core piece of all WPF data binding. Setting a Binding object’s Source property indicates the data source of the binding (i.e., where the data comes from). Setting the Path property indicates how to get the bound value from the data source. Setting the UpdateSourceTrigger property to ‘PropertyChanged’ tells the binding to update as you type, instead of waiting for the TextBox to lose focus before updating the data source.

This seems all well and good, but there is a problem. If you run the program now, the formatted full name will not update when you edit the first or last name. In the previous version, the formatted full name updated because we hooked each TextBox’s TextChanged event and manually pushed the new FullName value into the TextBlock. But now, all of those controls are data bound, so we cannot do that. What’s the deal?

The WPF data binding system is not magical. It has no way to know that our Person object’s FullName property changes when the FirstName or LastName properties are set. We must let the binding system know that FullName has changed. We can do that by implementing the INotifyPropertyChanged interface on the Person class, as seen below:

C#
public class Person : INotifyPropertyChanged
{
    string _firstName;
    string _lastName;

    public string FirstName
    {
        get { return _firstName; }
        set
        {
            _firstName = value;
            this.OnPropertyChanged("FirstName");
            this.OnPropertyChanged("FullName");
        }
    }

    public string LastName
    {
        get { return _lastName; }
        set
        {
            _lastName = value;
            this.OnPropertyChanged("LastName");
            this.OnPropertyChanged("FullName");
        }
    }

    public string FullName
    {
        get
        {
            return String.Format("{0}, {1}",
                this.LastName, this.FirstName);
        }
    }

    #region INotifyPropertyChanged Members

    public event PropertyChangedEventHandler PropertyChanged;

    void OnPropertyChanged(string propName)
    {
        if (this.PropertyChanged != null)
            this.PropertyChanged(
                this, new PropertyChangedEventArgs(propName));
    }

    #endregion
}

Notice that the new implementation of Person does not use automatic properties. Since we need to raise the PropertyChanged event when FirstName or LastName is set to a new value, we must use a normal property and field instead.

If we run the app now, the formatted full name text updates as we edit the first or last name. This shows that the binding system is listening to the Person object’s new PropertyChanged event. At this point, we have gotten rid of that ugly, bug-prone code in the previous version. Our code-behind has no logic in it that determines when to update which fields.

We still have quite a bit of code. It would be better if we could declare the relationships between controls and data in XAML. That would neatly separate the UI layout and configuration away from the application logic. This is especially appealing if you want to use a design tool, such as Microsoft Expression Blend, to create your user interfaces.

Version 3 – Binding in XAML (Verbose)

Now, let’s comment out the second version and see how to move all of this binding code into XAML. In the code-behind, we will have the Window’s constructor call this method:

C#
private void BindInXaml()
{
    base.DataContext = new Person
    {
        FirstName = "Josh",
        LastName = "Smith"
    };
}

The rest of the work is done in XAML. Here is the content of the Window:

XML
<StackPanel>
  <TextBox 
    x:Name="firstNameTextBox" 
    Width="200" 
    Margin="0,4" 
    >
    <TextBox.Text>
      <Binding 
        Path="FirstName" 
        UpdateSourceTrigger="PropertyChanged" 
        />
    </TextBox.Text>
  </TextBox>

  <TextBox 
    x:Name="lastNameTextBox" 
    Width="200" 
    Margin="0,4" 
    >
    <TextBox.Text>
      <Binding 
        Path="LastName" 
        UpdateSourceTrigger="PropertyChanged" 
        />
    </TextBox.Text>
  </TextBox>

  <TextBlock 
      x:Name="fullNameTextBlock" 
      Background="LightBlue" 
      Margin="0,4" 
      >
      <TextBlock.Text>
        <Binding Path="FullName" />
      </TextBlock.Text>
  </TextBlock>
</StackPanel>

That XAML uses the property-element syntax to establish bindings for each control’s Text property. It looks like we are setting the Text property to a Binding object, but we’re not. Under the covers, the WPF XAML parser interprets that as a way to establish a binding for the Text property. The configuration of each Binding object is identical to the previous version, which was all in code.

Running the application at this point shows that the XAML-based bindings work identically to the code-based bindings seen before. Both examples are creating instances of the same class and setting the same properties to the same values. However, this seems like a lot of XAML, especially if you are typing it by hand. It would be nice if there were a less verbose way to create the same bindings…

Version 4 – Binding in XAML

Using the same method in the code-behind as the previous example, and the same Person class, we can drastically reduce the amount of XAML it takes to achieve the same goal. The key here is the fact that the Binding class is actually a markup extension. Markup extensions are like a XAML parlor trick, allowing us to create and configure an object in a very compact way. We can use them to create an object within the value of an XML attribute. The XAML of the final version of this program is shown below:

XML
<StackPanel>
  <TextBox 
    x:Name="firstNameTextBox" 
    Text="{Binding Path=FirstName, UpdateSourceTrigger=PropertyChanged}"
    Width="200" 
    Margin="0,4" 
    />

  <TextBox 
    x:Name="lastNameTextBox" 
    Text="{Binding Path=LastName, UpdateSourceTrigger=PropertyChanged}"
    Width="200" 
    Margin="0,4" 
    />

  <TextBlock 
    x:Name="fullNameTextBlock" 
    Text="{Binding FullName}"
    Background="LightBlue" 
    Margin="0,4" 
    />
</StackPanel>

That XAML is almost as short as in the original version. However, in this example, there is no plumbing code wiring the controls up to the data source. Most data binding scenarios in WPF use this approach. Using the Binding markup extension feature vastly simplifies your XAML, and allows you to spend time working on more important things.

Conclusion

There are many ways to hook a user interface up to data. None of them are wrong, and all of them are useful in certain situations. Most of the time, WPF developers use data binding via the convenient markup extension syntax. In more complicated, dynamic scenarios, it can be useful to create bindings in code. I hope that this article has shed some light on the topic, so that you can make an informed decision about how you want to get the job done.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United States United States
Josh creates software, for iOS and Windows.

He works at Black Pixel as a Senior Developer.

Read his iOS Programming for .NET Developers[^] book to learn how to write iPhone and iPad apps by leveraging your existing .NET skills.

Use his Master WPF[^] app on your iPhone to sharpen your WPF skills on the go.

Check out his Advanced MVVM[^] book.

Visit his WPF blog[^] or stop by his iOS blog[^].

See his website Josh Smith Digital[^].

Comments and Discussions

 
GeneralRe: Question about DataContext Pin
mariocatch20-Jun-08 6:48
mariocatch20-Jun-08 6:48 
Generaldemo software question Pin
Member 460721328-May-08 8:50
Member 460721328-May-08 8:50 
GeneralRe: demo software question Pin
Josh Smith28-May-08 9:27
Josh Smith28-May-08 9:27 
GeneralRe: demo software question Pin
Member 460721328-May-08 15:15
Member 460721328-May-08 15:15 
GeneralNice walkthrough Pin
Marc Clifton21-May-08 6:22
mvaMarc Clifton21-May-08 6:22 
GeneralRe: Nice walkthrough Pin
Josh Smith21-May-08 6:29
Josh Smith21-May-08 6:29 
GeneralVery nice Pin
Pete O'Hanlon20-May-08 2:26
subeditorPete O'Hanlon20-May-08 2:26 
GeneralRe: Very nice Pin
Josh Smith20-May-08 2:47
Josh Smith20-May-08 2:47 
GeneralRe: Very nice Pin
Pete O'Hanlon20-May-08 11:05
subeditorPete O'Hanlon20-May-08 11:05 
GeneralRe: Very nice Pin
Josh Smith20-May-08 11:06
Josh Smith20-May-08 11:06 
GeneralRe: Very nice Pin
Pete O'Hanlon21-May-08 11:09
subeditorPete O'Hanlon21-May-08 11:09 
GeneralRe: Very nice Pin
Josh Smith21-May-08 11:14
Josh Smith21-May-08 11:14 
GeneralRe: Very nice Pin
Pete O'Hanlon21-May-08 11:18
subeditorPete O'Hanlon21-May-08 11:18 
GeneralRe: Very nice Pin
Josh Smith21-May-08 11:23
Josh Smith21-May-08 11:23 
GeneralRe: Very nice Pin
Pete O'Hanlon22-May-08 3:35
subeditorPete O'Hanlon22-May-08 3:35 
GeneralThanks Pin
User 27100920-May-08 0:47
User 27100920-May-08 0:47 
GeneralRe: Thanks Pin
Josh Smith20-May-08 1:25
Josh Smith20-May-08 1:25 
GeneralVery Nice! Pin
Muigai Mwaura19-May-08 23:03
Muigai Mwaura19-May-08 23:03 
GeneralRe: Very Nice! Pin
Josh Smith20-May-08 1:24
Josh Smith20-May-08 1:24 
GeneralRe: Very Nice! Pin
Sacha Barber20-May-08 2:51
Sacha Barber20-May-08 2:51 
GeneralRe: Very Nice! Pin
Muigai Mwaura21-May-08 4:29
Muigai Mwaura21-May-08 4:29 
GeneralRe: Very Nice! Pin
Josh Smith21-May-08 4:30
Josh Smith21-May-08 4:30 
GeneralRe: Very Nice! Pin
Sacha Barber21-May-08 21:07
Sacha Barber21-May-08 21:07 
GeneralRe: Very Nice! Pin
Muigai Mwaura27-May-08 5:29
Muigai Mwaura27-May-08 5:29 
GeneralRe: Very Nice! Pin
Sacha Barber27-May-08 8:33
Sacha Barber27-May-08 8:33 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.