Mobile Corner

A Data Binding Primer

Data binding is central to Windows Phone Development. Here's a refresher for old pros, and an introduction for newbies.

One of the most significant changes between more traditional style applications (for example, Windows Forms for both desktop and the old Windows Mobile platform) and the new style of XAML-based technologies (WPF, Silverlight, Windows Phone and Windows 8) is the use of data binding.

This isn't to say data binding didn't exist prior to XAML; it's just that it became much easier to use and gained designer support. Windows Phone, with its variant of Silverlight, is no exception; it includes designer support with Expression Blend. I'll cover some of the basics of data binding, which are often overlooked or skipped, since they've become assumed knowledge.

Data Binding Defined
When we refer to data binding in this article, we're referring to the synchronization of a data value between an attribute of an onscreen element -- for example, a TextBlock -- and a property of an associated data object -- for example, an instance of a Person class.

I'll illustrate this with a small snippet of code that shows the XAML for the TextBlock on the screen: a simple Person class and some code that creates an instance of the Person class and sets up the contents of the TextBlock (without data binding).

<TextBlock x:Name="TB_Name" />

public class Person
{
    public string Name { get; set; }   
}

var p = new Person {Name = "Nick"};
TB_Name.Text = p.Name;

While this code seems simple enough, as more code is written that accesses attributes of elements on the screen (whether to set or read their values), the harder the code becomes to manage -- in particular, if you want to keep the values synchronized.

For example, if the Name of the Person changed, you'd then need code which updated the appropriate attributes. Let's change this example over to use data binding:

<TextBlock Text="{Binding Name}"/>

public class Person
{
    public string Name { get; set; }   
}

var p = new Person {Name = "Nick"};
this.DataContext = p;

In the XAML, we've removed the Name attribute, as we no longer need to refer to the element explicitly in code. This will give us a tiny performance improvement, as there's no need for the backing variable to be initialized by locating the element on the visual tree.

The TextBlock element now has a Text attribute, which is assigned a calculated value (indicated by the { } braces), which is a data binding (indicated by the Binding keyword). In this case, the Text attribute is being bound to the Name property. The full syntax is actually {Binding Path=Name}, but the Path prefix is often dropped as it's not required if the first value is the property name to which data is being bound.

Which Object Gets the Binding?
So far, we know that the data binding is between the Text attribute on the TextBlock with the Name property of some other object. The question is, which object? The answer is that it's whatever object is set as the DataContext of the TextBlock. There is no DataContext attribute explicitly set on the TextBlock, which means it inherits the DataContext of its parent. The following XAML indicates that the TextBlock is located within a Grid on a PhoneApplicationPage. If the DataContext is set on either of these elements, it will be inherited by the TextBlock.

<phone:PhoneApplicationPage 
    x:Class="DataBinding101.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone">
        <Grid Margin="12,0,12,0">
            <TextBlock Text="{Binding Name}"/>
        </Grid>
</phone:PhoneApplicationPage>

The other change in the previous code block is that rather than explicitly setting the Text attribute on the TextBlock to the Name property of the Person instance, the entire Person object is assigned to the DataContext of the page (the code is being run in the context of a page in the application, so "this" refers to the page on which the TextBlock is located). As discussed, the DataContext value is inherited by both the Grid and the TextBlock. At the point where the DataContext is set, the Text attribute of the TextBlock would then be assigned the current value of the Name property of the Person instance.

What happens when the Name property changes value? With the current Person class, the answer is simple -- nothing. Currently, there's no way of notifying the TextBlock that it needs to update the Text attribute value. This is easily solved by implementing the INotifyPropertyChanged interface; it exploses a PropertyChanged event, raised whenever a property value changes.

The PropertyChanged event includes a PropertyChangedEventArgs, which indicates the name of the property that has changed. When this event's raised, the data binding engine will query this property, retrieve the new value and update any attributes of elements on the page that are data-bound to that property.

public class Person:INotifyPropertyChanged
{
    private string name;
    public string Name
    {
        get { return name; }
        set
        {
            if (name == value) return;
            name = value;
            OnPropertyChanged("Name");
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

It would seem that we've added a considerable amount of code for very little gain, but the true power of data binding will reveal itself in due course. Let's extend this to add a second property, ReversedName, which is actually a calculated value based on the Name property.

<TextBlock Text="{Binding Name}"/>
<TextBlock Text="{Binding ReversedName}" />

private string name;
public string Name
{
    get { return name; }
    set
    {
        if (name == value) return;
        name = value;
        OnPropertyChanged("Name");
        OnPropertyChanged("ReversedName");
    }
}

public string ReversedName
{
    get { return new string(Name.Reverse().ToArray()); }
}

The important thing about this example is the ReversedName property; because it's a calculated property, it's never explicitly set, so there isn't an opportunity to raise the PropertyChanged event. Instead, this is raised when the underlying data value, i.e. the Name property, is updated. The setter of the Name property raises the PropertyChanged event for both the Name and ReversedName properties.

So far we've seen how attributes and properties can be data bound, so that changes to the data object propagate through to the element on the screen. Of course, there are cases, such as with a TextBox, where data needs to flow the other direction.

Modes
There's an additional data binding attribute that can be applied to the Binding expression to indicate the direction, or Mode, of data binding. By default, this is set to OneWay, but there are two other values: OneTime (attribute value is only set initially), and TwoWay (changes to the attribute flow to the property on the data object). Changing the TextBlock to a TextBox, the binding expression would look like the following:

<TextBox Text="{Binding Name, Mode=TwoWay}"/>

If you set a breakpoint in the setter of the Name property, you may be wondering why it isn't immediately hit when you start typing in the TextBox. The reason is that the property isn't updated until the changes to the Text attribute have been committed; in other words, when the TextBox loses focus (simply tap anywhere outside of the TextBox to see it occur).

If you look back over the code in the setter for the Name property of the Person class, you might be wondering if there's a way to eliminate the string literals. One option would be to simply extract the strings into constants, but that's only a marginal improvement as you'd still need to ensure they match the property names. An alternative is to use lambda expressions to provide a strongly-typed overload for OnPropertyChanged; this is illustrated in the following code.

private string name;
public string Name
{
    get { return name; }
    set
    {
        if (name == value) return;
        name = value;
        OnPropertyChanged(()=>Name);
        OnPropertyChanged(() => ReversedName);
    }
}

protected virtual void OnPropertyChanged<TValue>(Expression<Func<TValue>> propertySelector)
{
    var memberExpression = propertySelector.Body as MemberExpression;
    if (memberExpression != null)
    {
        OnPropertyChanged(memberExpression.Member.Name);
    }
}
MVVM Foundation
In this article, you've seen an overview of the basics of data binding. What's important here is that this forms the basis for the Model-View-ViewModel (MVVM) pattern, where the linkage between the View (attributes of elements on a page) and the ViewModel (properties on a data object) is data binding.

About the Author

Nick Randolph runs Built to Roam, a consulting company that specializes in training, mentoring and assisting other companies build mobile applications. With a heritage in rich client applications for both the desktop and a variety of mobile platforms, Nick currently presents, writes and educates on the Windows Phone platform.

comments powered by Disqus

Featured

  • Compare New GitHub Copilot Free Plan for Visual Studio/VS Code to Paid Plans

    The free plan restricts the number of completions, chat requests and access to AI models, being suitable for occasional users and small projects.

  • Diving Deep into .NET MAUI

    Ever since someone figured out that fiddling bits results in source code, developers have sought one codebase for all types of apps on all platforms, with Microsoft's latest attempt to further that effort being .NET MAUI.

  • Copilot AI Boosts Abound in New VS Code v1.96

    Microsoft improved on its new "Copilot Edit" functionality in the latest release of Visual Studio Code, v1.96, its open-source based code editor that has become the most popular in the world according to many surveys.

  • AdaBoost Regression Using C#

    Dr. James McCaffrey from Microsoft Research presents a complete end-to-end demonstration of the AdaBoost.R2 algorithm for regression problems (where the goal is to predict a single numeric value). The implementation follows the original source research paper closely, so you can use it as a guide for customization for specific scenarios.

  • Versioning and Documenting ASP.NET Core Services

    Building an API with ASP.NET Core is only half the job. If your API is going to live more than one release cycle, you're going to need to version it. If you have other people building clients for it, you're going to need to document it.

Subscribe on YouTube