blue ball of data floating in room with binders and folders

Silverlight Data Binding

Get a handle on binding data to controls in your Silverlight applications

Microsoft's data-binding strategies have evolved a lot over the years. Going back to the 90s, classic ASP provided a simple way for developers to add dynamic data into a page, which was quite revolutionary at the time even though it didn't represent a true data-binding framework. ASP.NET provided developers with a simplified way to bind data to controls that significantly increased productivity and automatically generated much of the HTML needed to display data. The latest strategy can be found in Windows Presentation Foundation (WPF) and Silverlight, which both provide a robust data-binding framework that simplifies the process of binding objects and controls together.

Silverlight 2.0 and later provide a data-binding framework that allows data to be bound to controls using declarative and programmatic techniques. In this article, I'll demonstrate how you can leverage key data-binding features found in Silverlight, discuss best practices for organizing data, and point out an important interface that you need to know about to enhance data binding.

Before we get started, it's important to note that Silverlight allows data to be bound only to objects that derive from FrameworkElement and to object properties that are dependency properties. Although I won't cover the subject of dependency properties here, you can find additional information about that topic at msdn.microsoft.com/en-us/library/cc221408(VS.95).aspx. The built-in Silverlight controls already follow these rules, but knowing the requirements is important when building custom functionality.

Data-Binding Syntax

Silverlight's data-binding syntax mirrors the syntax found in WPF, although it provides only a subset of the overall WPF data-binding framework. Bindings between objects and controls can be defined using a compact, declarative binding syntax or defined more verbosely using the XAML Binding element. For example, if you'd like to bind an object's FirstName property to a TextBox control's Text property, you can use the following syntax:


Notice how compact the syntax is and that it uses { and } brackets to represent the start and end of the binding. This syntax is a shortcut for the following (notice the inclusion of Path):

Alternatively, you can use the Binding element to define the same binding in a more verbose manner:


    
    
    

The object containing the data that will be bound must be assigned to the TextBox control's DataContext property or to a parent of the TextBox for the binding to work properly. Otherwise the control wouldn't know how to access the property defined in the binding. Here's an example of binding an object to a TextBox control's DataContext property:

FirstNameTextBox.DataContext = myCustomer;

If the myCustomer object is used only by the FirstNameTextBox, then this code is fine. However, it's common to bind a data object to multiple controls with each control binding to a specific property, such as FirstName, LastName, Age, or Country. Let's look at an example of doing this and discuss some best practices along the way. We'll use the Customer class shown in Figure 1 to illustrate the process.

public class Customer
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int Age { get; set; }
    public string Country { get; set; }
}

Although an instance of Customer can be bound directly to the DataContext property of Silverlight controls such as TextBoxes, it's a good practice to add it and any other data that may be bound into a ViewModel class, then bind the ViewModel to the target controls. Doing so enables a single data container object to be bound, as opposed to multiple objects.

You may wonder what a ViewModel class is and why you need it. The ViewModel is responsible for getting all data used in a Silverlight page or user control and is commonly used in WPF and Silverlight applications for data binding. Figure 2 shows an example of a simple ViewModel class that retrieves Customer and country data.

public class MainPageViewModel
{
    public MainPageViewModel()
    {
    this.Customer = new Customer { FirstName = "John",
        LastName = "Doe", Age = 55, Country = "USA" };
    this.Countries = new List { "USA", "Canada", "Mexico" };
    }
    public Customer Customer { get; set; }
    public List Countries { get; set; }
}

Although this example contains hard-coded data, it could also retrieve the data by calling a Windows Communication Foundation (WCF) or an ASP.NET web service (ASMX).

To bind the ViewModel object and associated properties to controls, you'll first need to assign the MainPageViewModel object to the DataContext property of the Silverlight class you're working with (MainPage.xaml.cs, in this example). If you want, you can also assign it to the layout root, which is a Grid control by default. Figure 3 shows an example of assigning an instance of MainPageViewModel to the DataContext property.

void MainPage_Loaded(object sender, RoutedEventArgs e)
{
    MainPageViewModel vm = new MainPageViewModel();
    this.DataContext = vm;
}

Any data bound to an object's DataContext property is available to that object's descendants. In this example, the data is being bound to the Silverlight user control's DataContext property, so that any child controls automatically have access to the data as well. Figure 4 shows how different TextBox and ComboBox controls can bind to the data exposed by MainPageViewModel.











Notice that the ComboBox binds the SelectedItem property to the Customer object's Country property, while ItemsSource (similar to DataSource in ASP.NET) binds to the Countries property of the ViewModel.

By placing all the data in a ViewModel class, you provide an effective way to bind different objects to controls and remove the need for the Silverlight page or user control to know how to get the data. I'll revisit the topic of ViewModels in future articles about accessing data from remote services and discuss how these and other objects can also be assigned to the DataContext in a declarative manner.

Data-Binding Modes

ASP.NET 2.0 lets you bind data to controls and data sources in different ways. Data values that need to be bound to a control such as a GridView can be bound using syntax such as <%# Eval() %>, which is a type of OneWay binding used for read-only data. Data can also be bound in a TwoWay manner using the <%# Bind() %> expression, which is useful when data needs to be propagated from a control to a data source.

Silverlight provides three different ways to bind data to controls: OneTime, OneWay, and TwoWay. The OneTime binding allows data in a source object to be bound to a control once. If the data in the object changes, the target control isn't updated. The OneWay binding (which is the default) allows data to be bound to a control as well. However, if data in the source object changes, the control will automatically be updated, too. Finally, the TwoWay binding lets data flow in both directions. Data bound to a control is automatically updated if the source object changes, and if the control value changes (by an end user, for instance), the source object is also updated.

You can define the data-binding mode by using the Mode property, as follows:


This example instructs Silverlight to bind the FirstName property of the Customer type to a TextBox one time. Any changes made to the FirstName property won't automatically be refreshed in the control. To automatically update the TextBox's Text property when the data source object's FirstName property changes, you could use the OneWay binding (which is the default):


If you'd like data to flow in both directions, use a TwoWay binding:

Changes made in the data source object or target control automatically update each other without requiring any coding on your part. Although this is a great feature, you may wonder how Silverlight knows to refresh a control when the data source value changes. After all, how does Silverlight know about property value changes?

The answer to this question lies in the INotifyPropertyChanged interface located in the System.ComponentModel namespace. INotifyPropertyChanged exposes a single event named PropertyChanged that can be used to notify Silverlight when a property changes, so it can automatically update the bindings. If you define a OneWay or TwoWay binding and change a data object property through code, but you don't see the updated value in the target control, then the INotifyPropertyChanged interface hasn't been implemented by the data object.

Figure 5 shows an example of updating the Customer class (shown earlier in Figure 1) to implement INotifyPropertyChanged.

public class Customer : INotifyPropertyChanged
{
    string _FirstName;
    string _LastName;
    int _Age;
    string _Country;

public string FirstName
    {
    get { return _FirstName; }
    set
    {
        if (value != _FirstName)
        {
        _FirstName = value;
        OnPropertyChanged("FirstName");
        }
    }
    }

public string LastName
    {
    get { return _LastName; }
    set
    {
        if (value != _LastName)
        {
        _LastName = value;
        OnPropertyChanged("LastName");
        }
    }
    }

public int Age
    {
    get { return _Age; }
    set
    {
        if (value != _Age)
        {
        _Age = value;
        OnPropertyChanged("Age");
        }
    }
    }

public string Country
    {
    get { return _Country; }
    set
    {
        if (value != _Country)
        {
        _Country = value;
        OnPropertyChanged("Country");
        }
    }
    }

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

#region INotifyPropertyChanged Members

public event PropertyChangedEventHandler PropertyChanged;

#endregion
}

Looking through the code, you'll see that every time a property's set block is called, a method named OnPropertyChanged is also called, which raises the PropertyChanged event.

You'd also want to implement INotifyPropertyChanged on the MainPageViewModel class (shown in Figure 2) and any other ViewModel classes that you might create, since it's bound to the DataContext. Doing so enables any changes made to properties of the ViewModel object to be detected by Silverlight and control values to be refreshed accordingly.

You've Got the Knowledge

Silverlight provides a flexible and robust data-binding framework that can be used to minimize the amount of code you have to write to bind data to controls. In this article you've seen the data-binding syntax, learned about the DataContext property, and seen how data can automatically be synced between data objects and controls by using the INotifyPropertyChanged interface and TwoWay binding mode. You'll definitely want to master these concepts since they're used frequently throughout a typical Silverlight application.

Hide comments

Comments

  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
Publish