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
Member
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[^].

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 
I spent a whole day trying to figure out data binding in WPF by reading MSDN. Wish I had found this article first. Thanks a million!
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
Web01 | 2.6.130516.1 | Last Updated 19 May 2008
Article Copyright 2008 by Josh Smith
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid