Click here to Skip to main content
Click here to Skip to main content

Moving Toward WPF Data Binding One Step at a Time

By , 19 May 2008
 

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:

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:

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

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:

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:

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:

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:

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

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

About the Author

Josh Smith
Software Developer (Senior) Cynergy Systems
United States United States
Josh creates software, for iOS and Windows.
 
He works at Cynergy Systems as a Senior Experience 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[^].
Follow on   Twitter

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
Hint: For improved responsiveness ensure Javascript is enabled and choose 'Normal' from the Layout dropdown and hit 'Update'.
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralMy vote of 5membermasheleza6-May-13 23:01 
GeneralMy vote of 5memberMember 999165418-Apr-13 5:28 
GeneralMy vote of 4memberJarno Burger14-Apr-13 6:52 
QuestionSimple Data Binding Example in this videomemberabk1029-Mar-13 2:45 
GeneralMy vote of 5memberrxc5555527-Mar-13 5:47 
GeneralMy vote of 5memberiJam_j25-Mar-13 16:51 
GeneralThanks for making it simple :)membervarun kum5-Mar-13 8:06 
GeneralFantastic!memberjperlinski3-Nov-12 8:38 
GeneralThank youmemberraj23karthik10-Oct-12 21:30 
GeneralMy vote of 5memberSanguinSE26-Aug-12 6:38 
QuestionOnPropertyChangedmemberMember 875867010-Jun-12 3:25 
GeneralMy vote of 5memberdotnetgreen20-Feb-12 22:49 
GeneralMy vote of 5memberyannduran27-Jan-12 16:07 
GeneralMy vote of 5memberSteve Aube 122-Dec-11 14:32 
GeneralMy vote of 5membermungflesh29-Jun-11 23:33 
GeneralYou have explained well a topic few others have explained well on the internet. Thank you.memberDamian Flynn20-Jan-11 14:51 
GeneralRe: You have explained well a topic few others have explained well on the internet. Thank you.mvpJosh Smith21-Jan-11 6:28 
GeneralRe: You have explained well a topic few others have explained well on the internet. Thank you.memberDamian Flynn21-Jan-11 18:49 
GeneralRe: You have explained well a topic few others have explained well on the internet. Thank you.memberDamian Flynn21-Jan-11 18:51 
GeneralRe: You have explained well a topic few others have explained well on the internet. Thank you.mvpJosh Smith21-Jan-11 20:18 
GeneralRe: You have explained well a topic few others have explained well on the internet. Thank you.memberDamian Flynn21-Jan-11 20:53 
GeneralMy vote of 5memberWhirledPeas4-Nov-10 10:23 
GeneralMy vote of 5membero1010wanabe1-Aug-10 14:38 
GeneralExcellent article ! [modified]memberMike Gledhill7-Jun-10 0:38 
GeneralThanksmemberslelong2-May-10 7:51 
QuestionCan’t seem to set the DataContext in XAMLmemberNick Alexeev22-Nov-09 21:00 
GeneralBinding GridView columnmemberKizlyk14-Jul-09 5:34 
GeneralThanksmemberGary Wheeler9-Jun-09 9:25 
GeneralRe: ThanksmvpJosh Smith9-Jun-09 10:00 
QuestionHow can we apply this in different windows?memberasdsdasda21-Apr-09 19:38 
AnswerRe: How can we apply this in different windows?memberalleyes20-Aug-10 4:36 
GeneralExcellent article, but...memberRichard MacCutchan9-Feb-09 12:37 
GeneralRe: Excellent article, but...mvpJosh Smith11-Feb-09 2:38 
GeneralRe: Excellent article, but...memberRichard MacCutchan11-Feb-09 12:03 
GeneralRe: Excellent article, but...mvpJosh Smith11-Feb-09 12:07 
GeneralRe: Excellent article, but... [modified]memberRichard MacCutchan11-Feb-09 23:39 
GeneralQuick QuestionmemberOC_Ryan22-Jan-09 12:00 
GeneralThanks for the articlememberalexander_l29-Dec-08 23:41 
GeneralGreat Article, one suggestionmemberzoldello21-Oct-08 7:10 
GeneralQuestion about DataContextmembermariocatch19-Jun-08 15:00 
GeneralRe: Question about DataContextmvpJosh Smith20-Jun-08 1:07 
GeneralRe: Question about DataContextmembermariocatch20-Jun-08 6:48 
Generaldemo software questionmemberMember 460721328-May-08 8:50 
GeneralRe: demo software questionmvpJosh Smith28-May-08 9:27 
GeneralRe: demo software questionmemberMember 460721328-May-08 15:15 
GeneralNice walkthroughprotectorMarc Clifton21-May-08 6:22 
GeneralRe: Nice walkthroughmvpJosh Smith21-May-08 6:29 
GeneralVery nicemvpPete O'Hanlon20-May-08 2:26 
GeneralRe: Very nicemvpJosh Smith20-May-08 2:47 
GeneralRe: Very nicemvpPete O'Hanlon20-May-08 11:05 

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

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130617.1 | Last Updated 19 May 2008
Article Copyright 2008 by Josh Smith
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid