Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

WPF Data Binding - Part 1

0.00/5 (No votes)
3 Sep 2008 16  
An introduction to data binding with WPF.

Introduction

Conventionally, in Windows Forms and ASP.NET applications, data binding was primarily used for populating elements on the screen with information; there was simple data binding for displaying single values, and complex data binding for displaying and formatting a collection of data. The beauty of data binding is that you can populate the interface while writing little to no code. With data binding in WPF you can take data from almost any property of any object and bind it to almost any other dependency property of another object.

In the first part of this two part series, I am going to cover the basics of data binding in WPF. In the second part, more advanced data binding concepts will be covered such as templates and varying the way information is displayed based on characteristics of the data. For both of these parts, you will need to have a basic understanding of WPF.

Prerequisites

This article is written with the assumption that the reader has already achieved a basic understanding of the following WPF concepts:

  • Understanding of the basics of WPF
  • Dependency properties
  • Dependency property inheritance
  • Resources
  • Markup extensions

Basics

In its most basic form, data binding copies a value from a source object to a property on a destination object. The source property can be anything. The destination will be a dependency property. A key difference in binding to a WPF element property as the source and binding to another object type is that the WPF object has change notification; once binding has occurred with a WPF element property as a source, when the source changes, the destination property will automatically be updated. Let’s look at a couple of very simple WPF data binding expressions.

<Window x:Class="Example00.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300">
    <Grid  >   
        <Grid.RowDefinitions >
            <RowDefinition Height="*" />
            <RowDefinition Height="*" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <TextBox Name="mySourceElement" Grid.Row="0" >Hello World</TextBox>
        <TextBlock Grid.Row="1">            
            <TextBlock.Text>
                <Binding ElementName="mySourceElement" Path="Text"  />
            </TextBlock.Text>
        </TextBlock> 
        
        <TextBlock Text="{Binding ElementName=mySourceElement,Path=Text }" Grid.Row="2" />
    </Grid>
</Window>
Example 00

As you might have guessed, when you run this example, it will display “Hello Word.” The text is displayed three times. The first instance of the text is displayed because it is the literal value assigned to the text box. The next two instances are in textboxes, and are the result of data binding. Both of the text boxes create a binding with the textbox named “mySourceElement” as the source and their Text attribute as the destination. While the two text blocks bind to the same source and yield the same results, the syntax used is different. The first text block uses an expanded syntax, while the second uses a markup extension to express the same binding. Within this document, I will use the extension markup, but keep in mind that in most cases, either syntax can be used. Here is a more interesting data binding example:

Property

Description

Converter

Sets the converter to be used.

ElementName

The name of the element to which the binding is to be made.

FallbackValue

Sets the value to use when binding cannot return a value.

Mode

Sets the direction of the binding.

Path

The path to the element property being used as the data source.

RelativeSource

Sets the binding source by specifying an element relative to the current element.

Source

The source object to use for binding.

StringFormat

Specifies the format of the string representation of a value if the element is bound to a string property.

UpdateSourceTrigger

Sets the events on which binding will occur.

ValidationRules

A collection of the validation rules applied against a binding.

<Window x:Class="Example01.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Example 01" Height="300" Width="300">
    <Grid>
        <Grid.RowDefinitions >
            <RowDefinition Height="40px" />
            <RowDefinition Height="40px" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <Slider Name="fontSizeSlider" Minimum="5" 
           Maximum="100" Value="10" Grid.Row="0" />
        <TextBox Name="SizeTextBox" 
           Text="{Binding ElementName=fontSizeSlider, Path=Value}" Grid.Row="1"/>
        <TextBlock Text="Example 01" 
           FontSize="{Binding ElementName=SizeTextBox,  Path=Text}"  Grid.Row="2"/>
    </Grid>
</Window>
Example 01

In this example, there are three elements bound together. A TextBox’s Text property is bound to a Slider’s Value property, and a TextBlock's FontSize property is bound to the TextBox’s Text property. If you run the program, you will see the effects of this chained relationship. Changing the slider results in the number displayed in the textbox and the size of the font being adjusted appropriately. WPF is able to apply changes as the slider is changed because the element properties implement the INotifyPropertyChanged interface. As its name suggests, when the property value changes, WPF is made aware and knows to reapply the values to the destination properties.

Also, note that if you click in the text box and manually change its value, the slider and the text block will both update as soon as you tab out of the text box. The data appears to flow in both directions in this example (from the slider to the textbox, and from the textbox to the slider). This behavior of data binding is controlled through the binding mode.

Binding Modes

The binding mode sets in which direction data will flow. Data usually flows from the source to the destination. There are five data binding modes.

Mode Description
Default
OneWayToSource A reverse version of OneWay.
OneWay From source to destination.
OneTime The property is initially set, but updates to the source are not copied to the destination.
TwoWay Changes to source and destination are copied to each other.

The OneWayToSource mode appears redundant at first. Since it reverses the direction in which data is copied, at first glance, one might think that the same functionality can be achieved by reversing the element to which the binding expression is attached. Since the destination element must be a dependency property, that’s not always an option. When we need to set a non-dependency property with the value of a dependency property, you will need to use the OneWayToSource mode.

Controlling When Binding Occurs

Binding that occurs in a forward direction (from source to destination) occurs immediately. When you change the slider, the numeric value in the text box, or the combo box value, the changes occur to the text block immediately. But, you may have noticed that when you enter a numeric value into the text box, the slider does not update until the text box loses focus. Binding that occurs in the reverse direction doesn’t occur immediately, by default. The UpdateSourceTrigger property can be used to set this behavior.

Name

Description

Default

For most properties, the behavior is the same as PropertyChanged. For TextBox.Text, this is the same as LostFocus. The default behavior is controlled by metadata on the property.

Explicit

Binding does not occur until UpdateSource() is called.

LostFocus

Source is updated when the target loses focus.

PropertyChanged

Binding occurs immediately.

Binding to Public Properties on Objects that are not Elements

So far, we have looked at binding to WPF elements. When binding to a property that isn’t an element, the ElementName property of the binding expression must be set to one of the following values instead.

Name

Description

DataContext

*WPF will search up the element tree until it encounters a DataContext object if a Source or RelativeSource is not used. Once it finds a non-null DataContext, that object is used for binding. Useful for binding several properties to the same object.

RelativeSource

Used to identify the source object relative to the current element. Useful for control templates and data templates. (Data templates will be covered in part 2 of this article.)

Source

The reference in the binding expression is to the object supplying the data.

* - Actually no runtime searching occurs. The DataContext property inherits its value.

If no source is defined in a binding expression, then the source is assumed to be the data context. When several elements are being bound to the same source object, the implicit use of the data context saves a lot of typing. Instead of specifying the data sources on all of the elements that bind to the same source, we can assign the source object to the data context of some parent object.

<Window.Resources>
     <local:Employee 
        x:Key="MyEmployee" EmployeeNumber="123" FirstName="John" 
       LastName="Doe" Department="Product Development" Title="QA Manager" 
    />
</Window.Resources>
<Grid DataContext="{StaticResource MyEmployee}">
    <TextBox Text="{Binding Path=EmployeeNumber}"></TextBox>
    <TextBox Text="{Binding Path=FirstName}"></TextBox>
    <TextBox Text="{Binding Path=LastName}" />
    <TextBox Text="{Binding Path=Title}"></TextBox>
    <TextBox Text="{Binding Path=Department}" />
</Grid>
Example 03

Binding to Collections

Dependency properties support single-value binding. For binding to a collection of objects, your destination object must derive from ItemsControl. There are three properties in the ItemsControl class that are important for data binding.

Name

Description

DisplayMemberPath

Identifies the property on the source objects to be displayed. If not specified, then WPF will call the ToString method on the class. Unless ToString has been overridden, it will only display the class of the object.

ItemsSource

Identifies the collection that has the objects to be displayed.

ItemsTemplate

Accepts a template that can be used to customize how the objects are displayed. Templates are covered in part 2 of this article.

You can bind to any collection that implements the IEnumerable interface. The .NET Collection classes and Array classes implement this interface. With IEnumerable, you will be able to perform read-only binding. To be able to edit data, other requirements must be met.

For this example, the code is binding to a collection of Employee objects. A List control (which inherits from ItemsControl) displays a list of the employees’ names. Selecting one of the employees will cause their information to be displayed in the details area beside the list. To handle the display of detailed information, we will take advantage of the DataContext object that was mentioned in the previous section. All of the fields in the details area will be bound to the same object (but different property). All of the details fields are within the same grid control. The data context on the grid control is bound to the selected item in the employee list.

<TextBox Text="{Binding Path=FirstName}" />
<TextBox Text="{Binding Path=LastName}" />
<TextBox Text="{Binding Path=Title}" />
<TextBox Text="{Binding Path=Department}" />
Example 04

When the selected item in the list is changed, the data context is automatically updated. When the DataContext is updated, the fields in the details area that are bound to the DataContext are also updated.

If an element were programmatically inserted or removed from the collection after it is bound, the interface won’t update. To make the list automatically respond to insertions and deletions, the interface would need to be bound to a list that implements the INotifyCollectionChanged interface. The WPF ObservableCollection class implements INotifyCollectionChanged. For our example, changing the data collection from a List<Employee> to an ObservableCollection<Employee> is the only change needed to make sure the interface updates when employees are inserted or removed from the list. Example 5 uses an ObservableCollection instead of a generic List, and has a form for entering information for a new employee. When a new employee is added to the list, the ListBox automatically updates to show the new employee.

Binding to DataTables

Binding to tables is similar to binding to a list. In WPF, you cannot bind directly to a DataTable, but instead, must bind to a DataView (this should not be a big deal since all tables have a view in the DefaultView property). This restriction was also present in Windows Forms, but invisible to the developer. Since DataTables implement the IBindingList interface, WPF is able to detect when a new row is added. When deleting an item, be sure not to implement the delete functionality by removing a row from the dataset. Instead, mark a row as deleted by calling Row.Delete() (which will cause it not to be returned by the view).

Value Conversion

Sometimes, you will need to make a change to data that is being bound so that it is more appropriate for being displayed on the screen. For example, if your source data has a property that contains a path to an image, you would probably want to display the image instead of the image’s path. Value converters are responsible for performing this task.

To create a value converter, declare a class that inherits from IValueConverter . The class will need to have the ValueConversion attribute applied to it. The attribute takes two arguments, the type for the source data, and the type to which it is being converted. Implement the Convert and ConvertBack methods to perform the conversion. For the case of the image converter, we would not need to be able to convert an image back to the path from which it was loaded, so instead of implementing ConvertBack, we can throw the NotImplemented exception. WPF will suppress the exception (more on WPF exception suppression later).

The IValueConverters can be used in some rather creative ways. Let’s say you are displaying a list of sales figures, and you want to highlight figures that are above or below certain levels, by changing the color of the text. A value converter could be used here to convert the value to the appropriate color. For the following code example, value converters are used to convert Fahrenheit temperature and to change the color of the temperature text to indicate whether the temperature is hot or cold. The same converter is used on both the Celsius and Fahrenheit temperatures. To make the converters suitable for both numeric ranges, the converters expose properties that allow the limits of hot and cold to be set.

<Window.Resources>
    <local:FarenheitToCelciusConverter x:Key="myTemperatureConverter" />
    <local:TemperatureToColorConverter x:Key="myFRangeIndicator" 
           HotTemperature="80" ColdTemperature="65" />
  <local:TemperatureToColorConverter x:Key="myCRangeIndicator" 
         HotTemperature="26.6" ColdTemperature="18.8" />
</Window.Resources>
<TextBox Name="txtFarenheit" 
         Foreground="{Binding Path=Text, ElementName=txtFarenheit, 
         Converter={StaticResource myFRangeIndicator}}"
/>
<TextBox Name="txtCelcius" 
         Text="{Binding  UpdateSourceTrigger=PropertyChanged, 
                Path=Text,ElementName=txtFarenheit, 
                Converter={StaticResource myTemperatureConverter}}" 
                Foreground="{Binding Path=Text, ElementName=txtCelcius, 
                Converter={StaticResource myCRangeIndicator}}" 
/>
Example 06
[ValueConversion(typeof(double), typeof(double))]
public class FarenheitToCelciusConverter : IValueConverter
{
    #region IValueConverter Members
    public object Convert(object value, Type targetType, 
           object parameter, System.Globalization.CultureInfo culture)
    {
        string sourceValue = value.ToString();
        double decimalValue = 0;
        if (Double.TryParse(sourceValue, out decimalValue))
        {
            return (decimalValue - 32.0) * (5.0 / 9.0);
        }
        return value;
    }
 
    public object ConvertBack(object value, Type targetType, 
           object parameter, System.Globalization.CultureInfo culture)
    {
        string sourceValue = value.ToString();
        double decimalValue = 0;
        if (Double.TryParse(sourceValue, out decimalValue))
        {
            return (decimalValue * (9.0 / 5.0)) + 32.0;
        }
        return value;
    }
    #endregion
}
Example 06 Value converter code

Validation

To prevent a user from entering invalid data, your classes may contain logic to prevent invalid values from being set. But, if an exception is thrown when an invalid value is set, the user will not receive feedback explaining that a value is invalid. WPF will automatically suppress exceptions that occur during binding. To provide user feedback for validation problems, WPF has a facility named Validation Rules. One of the built-in validation rules is the ExceptionValidationRule. The markup for applying this rule is shown below. When applied, the user will be notified of exceptions that occurred during binding.

<Binding Path="EventDate"  UpdateSourceTrigger="PropertyChanged">
   <Binding.ValidationRules>
      <ExceptionValidationRule />
   </Binding.ValidationRules>
</Binding>
Example 07

You can create your own validation rules be inheriting from the ValidationRule class and overriding the Validate method. If a value is valid, then a ValidationResult object should be returned with its IsValid property set to true. Otherwise, this value should be set to false, and it’s ErrorContent property should contain details of the failure. Validation occurs before conversion, so your validation logic must be written against the unconverted value.

If you wanted a list of all of the elements that have encountered validation errors during binding, walk through the form hierarchy, testing each element’s HasError property. If an element has an error, then you can get a collection of the errors it encountered by calling its GetErrors() method.

In the Next Section…

For the next part in this two part article series on data binding, I’ll cover more details of validation and rules, and will introduce data templates (for customizing how information is displayed in ItemControls), data sources (which allow data to be pulled in from other sources), and data views (for sorting, filtering, and grouping data). Before moving on to the forthcoming second part of this article, I would suggest getting more practice with WPF data binding.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here