WPF Databinding Overview

Posted on April 27, 2008. Filed under: WPF | Tags: , , , |

Introduction

Data binding in WPF seems to be much like skinning a cat (although hopefully a lot less painful… for the cat at least), there is usually more than one way of achieving the result you want. 

 The purpose of this blog is to quickly detail some of the many ways you can bind data to a WPF application. If you want to go into more detail about the concepts behind WPF data binding and it’s inner workings here are a couple of great articles; 

What is DataBinding?

The basic philosophy behind data binding is that we have some data that we are able to load into memory (known as the Binding Source) and we have a GUI where we want to display and/or edit the data (known as the Binding Target). 

When we bind we are essentially creating a link between a property from the Binding Source (i.e. the “name” property of our customer object) with a property of our Binding Target (i.e. the “Text” property of our text box). This binding can fall under one of three scenarios; 

  • One Time – Data is written to the control and then the link is broken
  • One Way – Updates to the in memory object will be reflected in the control or vice versa
  • Two Way – Editing the value in our text box will update our in memory object and vice versa

In WPF there are essentially two distinct groups of controls that can be involved in data binding, collection controls such as List Boxes that can display properties from a list of objects and singleton controls such as text boxes that can display a property from a single object. 

For this overview I will use the following custom object in most of the examples;       

public class Person
{      
    public String FirstName { get; set; }     
    public String LastName { get; set; }     
    public String Email { get; set; }    
}

Simple Code Behind Binding

Let’s start with a couple of examples of data binding from code behind, this isn’t good practice in WPF as it is much cleaner to perform our data binding in xaml but it is a good starting point; 

In our xaml file we create a text block that is bound to the “FirstName” property as follows;   

<Window x:Class="WpfApplication1.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"> 

    <TextBlock Name="myTextBlock" Text="{Binding Path=FirstName}" /> 

</Window>

You will notice that we haven’t actually told the text block where it is going to look for this property. When the code runs the text block will look for a DataContext, it will start by checking wether it has a DataContext assigned, if not it will progress up the control tree until it reaches an item that does have a DataContext set, if no data context is found then it simply won’t perform the binding.   

If we add the following code behind to set the DataContext of the window to a new Person object we will see the text block being bound to the first name.   

namespace WpfApplication1
{
     public partial class Window1 : Window
     {
         public Window1()
         {
             InitializeComponent();
             this.DataContext = new Person { FirstName="Rowan" };
         }
     }
       public class Person
     {
         public String FirstName { get; set; }
         public String LastName { get; set; }
         public String Email { get; set; }
     }
 }  

Note that we could also have set the DataContext explicitly on the TextBlock as follows; 

this.myTextBlock.DataContext = new Person { FirstName="Rowan" };

This is what we expect to see displayed when the code executes;

 WPF Window with the text "Rowan" displayed

Simple Binding in XAML

A much cleaner option is to perform all our data binding in xaml. Lets say we have a simple object that we want to call an empty constructor on and bind to one or more controls in our window. In this example I have created a new class Rowan that inherits from our Person class and basically sets all the properties when the constructor is called; 

  
namespace WpfApplication1
{
     public partial class Window1 : Window
     {
         public Window1()
         {
             InitializeComponent();
         }
     }

    public class Rowan : Person
    {
         public Rowan()
         {
             this.FirstName = "Rowan";
             this.LastName = "Miller";
             this.Email = mail@romiller.com;
         }
     }

     public class Person
     {
         public String FirstName { get; set; }
         public String LastName { get; set; }
         public String Email { get; set; }
     }
 }  

Now we need to register the namespace of our code behind in xaml so that we can access this object, here I have registered it with a prefix of local (see line 4 below). Now we can create an instance of Rowan in our Window.Resources tag and then set the DataContext of the window to this instance using a Window.DataContext tag. In this example we are displaying all three proeprties of our object in a stack panel.   

<Window x:Class="WpfApplication1.Window1"
    xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation
    xmlns:x=http://schemas.microsoft.com/winfx/2006/xaml
    xmlns:local="clr-namespace:WpfApplication1"
    Title="Window1" Height="300" Width="300">

    <Window.Resources>
        <local:Rowan x:Key="MyInstanceOfRowan" />
    </Window.Resources>

    <Window.DataContext>
        <Binding Source="{StaticResource MyInstanceOfRowan}" />
    </Window.DataContext>

    <StackPanel>
        <TextBlock Name="myTextBlock1" Text="{Binding Path=FirstName}" />
        <TextBlock Name="myTextBlock2" Text="{Binding Path=LastName}" />
        <TextBlock Name="myTextBlock3" Text="{Binding Path=Email}" />
    </StackPanel> 

</Window>

The resulting output is something like below; 

WPF Window with "Rowan", "Miller" and "mail@romiller.com" displayed on seperate lines

Binding to a Function

It’s probably more likely that we want to call a function on an object to get the data we want to bind to, lets assume we have the following code behind and want to bind the result of the Person.GetAFewPeople() function to a ListBox on our window;  

namespace WpfApplication1
{
     public partial class Window1 : Window
     {
         public Window1()
         {
             InitializeComponent();
         }
     }

     public class Person
     {
         public String FirstName { get; set; }
         public String LastName { get; set; }
         public String Email { get; set; }

        public List<Person> GetAFewPeople()
         {
             List<Person> result = new List<Person>();
             result.Add(new Person { FirstName = "Rowan", LastName = "Miller", Email = "mail@romiller.com" });
             result.Add(new Person { FirstName = "John", LastName = "Citizen", Email = "john@citizen.com" });
             result.Add(new Person { FirstName = "Jane", LastName = "Doe", Email = "jane@ImNotAFemaleDear.com" });
             return result;
         }
     }
 }

 The following xaml will achieve this binding, note that here we are setting the databinding explicitly on the ListBox rather than on the whole form, this is a better option if we are going to bind more than one data source to different controls within our window.   

<Window x:Class="WpfApplication1.Window1" 
    xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation 
    xmlns:x=http://schemas.microsoft.com/winfx/2006/xaml 
    xmlns:local="clr-namespace:WpfApplication1" 
    Title="Window1" Height="300" Width="300"> 

    <Window.Resources> 
        <ObjectDataProvider x:Key="MyFewPeople" ObjectType="{x:Type local:Person}" MethodName="GetAFewPeople" /> 
    </Window.Resources> 

    <ListBox ItemsSource="{Binding Source={StaticResource MyFewPeople}}"> 
        <ListBox.ItemTemplate> 
            <DataTemplate> 
                <Border CornerRadius="5" BorderThickness="2" Width="200" BorderBrush="Black" Margin="3"> 
                    <StackPanel> 
                        <TextBlock Text="{Binding Path=FirstName}" /> 
                        <TextBlock Text="{Binding Path=LastName}" /> 
                        <TextBlock Text="{Binding Path=Email}" /> 
                    </StackPanel> 
                </Border> 
            </DataTemplate> 
        </ListBox.ItemTemplate> 
    </ListBox> 

</Window> 

Binding to a WCF Service

Just one possible usage of the above example is binding to the results of a WCF Service call, in this case we want to create an instance of our ServiceClient and then call a function on that client to get data back. Lets assume we have created a Service Reference called MyService to a WCF Service that has a GetEmployees() function. If we modify the Window.Resources tag from the above example we can call our webservice from xaml;    

<Window.Resources> 
   <ObjectDataProvider x:Key="MyEmployees" ObjectType="{x:Type local:MyService.MyServiceClient}" MethodName="GetEmployees" /> 
</Window.Resources> 

Master Detail Binding

A common requirement is to display a list of items and in a separate area display more detailed data for the currently selected item, again this is something we can do in WPF without the need for any code behind. We are going to use a CollectionViewSource to achieve this, a CollectionViewSource basically allows many controls on our page to share a common view of a set of data. One very handy feature of this component is that when bound to a collection control it will display all items but when bound to a singleton control it will display the currently selected item. Still using the code behind from our Binding to a Function example; 

namespace WpfApplication1
{
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
        }
    }
 
    public class Person
    {
        public String FirstName { get; set; }
        public String LastName { get; set; }
        public String Email { get; set; }
 
        public List<Person> GetAFewPeople()
        {
            List<Person> result = new List<Person>();
            result.Add(new Person { FirstName = "Rowan", LastName = "Miller", Email = "mail@romiller.com" });
            result.Add(new Person { FirstName = "John", LastName = "Citizen", Email = "john@citizen.com" });
            result.Add(new Person { FirstName = "Jane", LastName = "Doe", Email = "jane@ImNotAFemaleDear.com" });
            return result;
        }
    }
}

You will notice in the following xaml that we still use our ObjectDataProvider to access the GetAFewPeople() function but we then put this data into a CollectionViewSorce and use this to bind our ListBox and a ContentControl. When you run the code you will see that the ContentControl will display information for the person currently selected in the ListBox.   

<Window x:Class="WpfApplication1.Window1" 
    xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation 
    xmlns:x=http://schemas.microsoft.com/winfx/2006/xaml 
    xmlns:local="clr-namespace:WpfApplication1" 
    Title="Window1" Height="300" Width="300"> 

    <Window.Resources> 
        <ObjectDataProvider x:Key="MyFewPeople" ObjectType="{x:Type local:Person}" MethodName="GetAFewPeople" /> 
        <CollectionViewSource x:Key="MyCollectionView" Source="{Binding Source={StaticResource MyFewPeople}}" /> 
    </Window.Resources> 

    <StackPanel> 
        <Border BorderBrush="Black" BorderThickness="2"> 
            <ListBox ItemsSource="{Binding Source={StaticResource MyCollectionView}}"> 
                <ListBox.ItemTemplate> 
                    <DataTemplate> 
                        <StackPanel> 
                            <TextBlock Text="{Binding Path=FirstName}" /> 
                        </StackPanel>
                    </DataTemplate> 
                </ListBox.ItemTemplate> 
            </ListBox> 
        </Border> 

        <ContentControl Content="{Binding Source={StaticResource MyCollectionView}}"> 
            <ContentControl.ContentTemplate> 
                <DataTemplate> 
                    <StackPanel Margin="5" Background="LightGray"> 
                        <TextBlock Text="{Binding Path=FirstName}" /> 
                        <TextBlock Text="{Binding Path=LastName}" /> 
                        <TextBlock Text="{Binding Path=Email}" />   
                    </StackPanel> 
                </DataTemplate> 
            </ContentControl.ContentTemplate> 
        </ContentControl> 

    </StackPanel> 
</Window> 

 The master detail window should look like this; 

WPF Window displaying master/detail view for employees

Summary

There are many different ways to achieve data binding in WPF and you should become comfortable with a variety of them so that you can use the method best suited to the task at hand. Unless you are doing something particularly complicated try and avoid writing code behind as it is much cleaner and easier to debug if your binding is all done in XAML. 

Some topics that I haven’t covered here that you should also get an understanding of before tackling your first WPF project are; 

  • Resource Dictionaries
  • Re-usable Data Templates
  • Re-usable Styles
  • Filtering, Grouping & Sorting CollectionViewSources
  • Data Binding Validation
  • BindingExpressions (if you are going to be editing data in your application)
About these ads

Make a Comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

    About

    Rowan works as a Program Manager for the ADO.NET Entity Framework team at Microsoft. He speaks at technical conferences and blogs at romiller.com. Rowan lives in Seattle, Washington with his wife Athalie. Prior to moving to the US he resided in the small state of Tasmania in Australia. Outside of technology Rowan's passions include snowboarding, mountain biking, horse riding, rock climbing and pretty much anything else that involves being active. The primary focus of his life, however, is to follow Jesus.

    RSS

    Subscribe Via RSS

    • Subscribe with Bloglines
    • Add your feed to Newsburst from CNET News.com
    • Subscribe in Google Reader
    • Add to My Yahoo!
    • Subscribe in NewsGator Online
    • The latest comments to all posts in RSS

    Meta

Liked it here?
Why not try sites on the blogroll...

Follow

Get every new post delivered to your Inbox.

Join 167 other followers

%d bloggers like this: