Export (0) Print
Expand All
Dazzling Graphics: Top Ten UI Development Breakthroughs In Windows Presentation Foundation
Distributed .NET: Learn The ABCs Of Programming Windows Communication Foundation
A First Look at InfoCard
Talking Windows: Exploring New Speech Recognition And Synthesis APIs In Windows Vista
Windows Workflow Foundation
Windows Workflow Foundation, Part 2
WinFX Workflow: Simplify Development With The Declarative Model Of Windows Workflow Foundation
XPS Documents: A First Look at APIs For Creating XML Paper Specification Documents
Expand Minimize

Windows Presentation Foundation Data Binding: Part 1

 

Shawn Wildermuth

May 2006

Applies to:
   Microsoft Windows Presentation Foundation

Summary: Illustrates how to use the XAML-based data binding to perform data manipulation in Microsoft Windows Presentation Foundation projects. (16 printed pages)

Contents

Introduction
XAML Bindings, Simplified
Where Are We?
References

Introduction

Microsoft Windows Presentation Foundation (WPF; formerly Avalon) introduces a profound new way to develop user interfaces for rich clients. For the first time, WPF separates design of the user interface from the code. This separation means that, much like ASP.NET, your markup is typically in one file, while the code is in another. This separation exists only at compile time, though. The markup file is used to generate code that is married to the code file in order to produce your application.

To facilitate the design, Microsoft has developed a rich markup language called XAML. XAML is an XML-based markup language that supports a new model for developing applications that has native support for many different user interface concepts, such as 2-D and 3-D drawing, animations, control containment, control and document flow, as well as a rich data binding model. This article will give you an overview of WPF data binding. This article assumes that you have some cursory knowledge of WPF. If you do not, please see Time Sneath's Hitchhiker's Guide to WPF Beta 1 article for an overview.

Why Use Data Binding?

If you are getting started with WPF, you might wonder whether it is easier to avoid learning data binding, and instead just write code to do most of the data manipulation in your projects. While this may be a valid way to do it, I would suspect that you will grow to use, and perhaps even love, XAML-based data binding. Let's take a small example.

Figure 1 shows the user interface for a simple WPF project. It is an editor for a RSS feed, to allow users to view and edit feeds.

Click here for larger image

Figure 1. Our RSS editor (Click on the image for a larger picture)

The layout of the editor is pretty straightforward, as shown below in the following XAML code.

<Window x:Class="ExampleCS.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="ExampleCS"
    Loaded="Window1_Loaded"
    >
  <StackPanel>
    <TextBlock HorizontalAlignment="Center" 
          FontWeight="Bold">
      BlogEditor
    </TextBlock>
    <StackPanel Orientation="Horizontal" 
           HorizontalAlignment="Center">
      <ListBox Name="entryListBox" 
          Height="300" 
          SelectionChanged="entryListBox_Changed"/>
      <Grid Width="500" Margin="5">
        <Grid.ColumnDefinitions>
          <ColumnDefinition Width="50" />
          <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
          <RowDefinition Height="25" />
          <RowDefinition Height="25" />
          <RowDefinition Height="25" />
          <RowDefinition Height="*" />
          <RowDefinition Height="25" />
        </Grid.RowDefinitions>
        <TextBlock Grid.Row="0" Grid.Column="0">Title:</TextBlock>
        <TextBox Grid.Row="0" Grid.Column="1" Name="titleText" />
        <TextBlock Grid.Row="1" Grid.Column="0">Url:</TextBlock>
        <TextBox Grid.Row="1" Grid.Column="1" Name="urlText" />
        <TextBlock Grid.Row="2" Grid.Column="0">Date:</TextBlock>
        <TextBox Grid.Row="2" Grid.Column="1" Name="dateText" />
        <TextBlock Grid.Row="3" Grid.Column="0">Body:</TextBlock>
        <TextBox Grid.Row="3" Grid.Column="1" 
            Name="bodyText" 
            TextWrapping="Wrap" />
        <Button Grid.Row="4" 
                  Grid.ColumnSpan="2" 
                  Grid.Column="1"
            Click="updateButton_Click">
          Update
        </Button>
      </Grid>
    </StackPanel>
  </StackPanel>
</Window>

Take note of the bolded event handlers. That is where most of our code will happen, to load the RSS feed and fill in our WPF controls.

C#

XmlDocument blog = new XmlDocument();
const string BLOGURL = @"z:\adoguy.RSS";

public Window1()
{
  InitializeComponent();
  blog.Load(BLOGURL);
}

void Window1_Loaded(object sender, RoutedEventArgs e) 
{
  FillListBox();
}

void FillListBox()
{
  entryListBox.Items.Clear();

  XmlNodeList nodes = blog.SelectNodes("//item");
  foreach (XmlNode node in nodes)
  {
    ListBoxItem item = new ListBoxItem();
    item.Tag = node;
    item.Content = node["title"].InnerText;
    entryListBox.Items.Add(item);
  }
}

Visual Basic .NET

  Dim blog As New XmlDocument
  Const BLOGURL As String = "z:\adoguy.RSS"

  Public Sub New()
    InitializeComponent()
    blog.Load(BLOGURL)
  End Sub

  Sub Window1_Loaded(ByVal sender As Object, _
                     ByVal e As RoutedEventArgs) 
    FillListBox()

  End Sub

  Sub FillListBox()

    entryListBox.Items.Clear()

    Dim nodes As XmlNodeList = blog.SelectNodes("//item")

    For Each node As XmlNode In nodes

      Dim item As New ListBoxItem
      item.Tag = node
      item.Content = node("title").InnerText
      entryListBox.Items.Add(item)

    Next

  End Sub

You can see in this code that we are loading up an XML document with the RSS feed, and filling in the ListBox with the titles of all the entries.

Next, we will need to handle what happens when a selection is made in the ListBox.

C#

void entryListBox_Changed(object sender, RoutedEventArgs e)
{
  if (entryListBox.SelectedIndex != -1)
  {
    XmlNode node = ((ListBoxItem)entryListBox.SelectedItem).Tag as XmlNode;
    if (node != null)
    {
      titleText.Text = node["title"].InnerText;
      urlText.Text = node["guid"].InnerText;
      dateText.Text = node["pubDate"].InnerText;
      bodyText.Text = node["description"].InnerText;
    }
  }
}

Visual Basic .NET

  Sub entryListBox_Changed(ByVal sender As Object, _
                           ByVal e As SelectionChangedEventArgs) 

    If entryListBox.SelectedIndex <> -1 Then

      Dim node As XmlNode = CType(entryListBox.SelectedItem, ListBoxItem).Tag
      If Not node Is Nothing Then

        titleText.Text = node("title").InnerText
        urlText.Text = node("guid").InnerText
        dateText.Text = node("pubDate").InnerText
        bodyText.Text = node("description").InnerText

      End If

    End If

  End Sub

When the ListBox is changed, we fill in the TextBoxes with the selected RSS feed item. We do this by getting the inner text of the feed's item. Note that it also required that we keep a copy of the node around with the ListBoxItem, and do a bit of casting to get at each event handler.

Finally, we need to handle what happens when the Update button is clicked.

C#

void updateButton_Click(object sender, RoutedEventArgs e)
{
  if (entryListBox.SelectedIndex != -1)
  {
    XmlNode node = ((ListBoxItem)entryListBox.SelectedItem).Tag as XmlNode;
    if (node != null)
    {
      node["title"].InnerText = titleText.Text;
      node["guid"].InnerText = urlText.Text;
      node["pubDate"].InnerText = dateText.Text;
      node["description"].InnerText = bodyText.Text;

      blog.Save(BLOGURL);

      FillListBox();
    }
  }
}

Visual Basic .NET

  Sub updateButton_Click(ByVal sender As Object, _
                         ByVal e As RoutedEventArgs)

    If entryListBox.SelectedIndex <> -1 Then

      Dim node As XmlNode = CType(entryListBox.SelectedItem, ListBoxItem).Tag

      If Not node Is Nothing Then

        node("title").InnerText = titleText.Text
        node("guid").InnerText = urlText.Text
        node("pubDate").InnerText = dateText.Text
        node("description").InnerText = bodyText.Text

        blog.Save(BLOGURL)

        FillListBox()

      End If

    End If

  End Sub

Here, we update the nodes with the new information, save the XML document, and cause the ListBox to be refilled, in case the title was changed. While this is a simple little application, the code required is a bit extensive. Could this be easier with XAML data bindings? Yes, it could be. The following is the XAML of the same project, using XAML data bindings.

<StackPanel 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  >
  <StackPanel.Resources>
    <XmlDataProvider x:Key="RssFeed" Source="z:\adoguy.RSS" />
  </StackPanel.Resources>
  <TextBlock HorizontalAlignment="Center" 
      FontWeight="Bold">
    Blog Editor
  </TextBlock>
  <StackPanel Orientation="Horizontal" 
    HorizontalAlignment="Center">
    <ListBox Name="entryListBox" 
        Height="300" 
        ItemsSource="{Binding Source={StaticResource RssFeed}, XPath=//item}"
    >
      <ListBox.ItemTemplate>
        <DataTemplate>
          <TextBlock Text="{Binding XPath=title}" />
        </DataTemplate>
      </ListBox.ItemTemplate>
    </ListBox>
    <Grid Width="500" 
        Margin="5" 
        DataContext="{Binding ElementName=entryListBox, Path=SelectedItem}" >
      <Grid.ColumnDefinitions>
        <ColumnDefinition Width="50" />
        <ColumnDefinition Width="*" />
      </Grid.ColumnDefinitions>
      <Grid.RowDefinitions>
        <RowDefinition Height="25" />
        <RowDefinition Height="25" />
        <RowDefinition Height="25" />
        <RowDefinition Height="*" />
        <RowDefinition Height="25" />
      </Grid.RowDefinitions>
      <TextBlock Grid.Row="0" Grid.Column="0">Title:</TextBlock>
      <TextBox Grid.Row="0" Grid.Column="1" 
          Name="titleText" 
          Text="{Binding XPath=title}" />
      <TextBlock Grid.Row="1" Grid.Column="0">Url:</TextBlock>
      <TextBox Grid.Row="1" Grid.Column="1" 
          Name="urlText" 
          Text="{Binding XPath=guid}" />
      <TextBlock Grid.Row="2" Grid.Column="0">Date:</TextBlock>
      <TextBox Grid.Row="2" Grid.Column="1" 
          Name="dateText" 
          Text="{Binding XPath=pubDate}" />
      <TextBlock Grid.Row="3" Grid.Column="0">Body:</TextBlock>
      <TextBox Grid.Row="3" Grid.Column="1" 
          Name="bodyText" 
          TextWrapping="Wrap" 
          Text="{Binding XPath=description}" />
      <Button Grid.Row="4" Grid.ColumnSpan="2" Grid.Column="1" >
          Update
      </Button>
    </Grid>
  </StackPanel>
</StackPanel>

This XAML works without any code-behind. Figure 2 shows the application working with just the XAML (using XAMLPad).

Click here for larger image

Figure 2. RSS editor in XAMLPad (Click on the image for a larger picture)

You might be wondering, "How can this work?" It works because we are using XAML binding to do much of the work for us. Although each of the techniques in the XAML is covered later in detail, let's run through the pieces of the data binding, to explain how this does, in fact, work.

First, we create a XmlDataProvider object to load in and manage the XML document for the editor.

<XmlDataProvider x:Key="RssFeed" Source="z:\adoguy.RSS" />

This tells the XAML to load up an XML document from my Z: drive, in order to fill in the form.

Next, we bind the ListBox.

<ListBox Name="entryListBox" 
        Height="300" 
        ItemsSource="{Binding Source={StaticResource RssFeed}, XPath=//item}"
    >

This tells the list box to find the resource in the XAML called RssFeed, and to use it as the data source. In addition, it tells the list box to get the list of items to bind to from the XPath expression (in this case, all item elements in the document).

Next, we specify what to show in the list box's items, by specifying the data template.

<DataTemplate>
  <TextBlock Text="{Binding XPath=title}" />
</DataTemplate>

This tells the ListBox to create a TextBlock object for each item, and to use the XPath to determine what to show in the TextBlock.

Next, we bind the grid that contains all of our details.

<Grid Width="500" 
        Margin="5" 
        DataContext="{Binding ElementName=entryListBox, Path=SelectedItem}" >

Here, we are telling the XAML to bind the container (in this case the Grid) to the selected item of the ListBox. We are using the ElementName instead of Source, because we want to bind to a control in our XAML document, instead of some external data. By setting the DataContext, we are allowing all controls within the Grid to be set to the same object (in this case, the SelectedItem).

Next, we bind each TextBox to the part of the XML document that we need.

<TextBlock Grid.Row="0" Grid.Column="0">Title:</TextBlock>
      <TextBox Grid.Row="0" Grid.Column="1" 
          Name="titleText" 
          Text="{Binding XPath=title}" />
      <TextBlock Grid.Row="1" Grid.Column="0">Url:</TextBlock>
      <TextBox Grid.Row="1" Grid.Column="1" 
          Name="urlText" 
          Text="{Binding XPath=guid}" />
      <TextBlock Grid.Row="2" Grid.Column="0">Date:</TextBlock>
      <TextBox Grid.Row="2" Grid.Column="1" 
          Name="dateText" 
          Text="{Binding XPath=pubDate}" />
      <TextBlock Grid.Row="3" Grid.Column="0">Body:</TextBlock>
      <TextBox Grid.Row="3" Grid.Column="1" 
          Name="bodyText" 
          TextWrapping="Wrap" 
          Text="{Binding XPath=description}" />

Since we have already set the DataContext, we can simply specify the XPath expression in order to get to the pieces of the RSS Feeds that we need.

This is quite a lot to try to swallow in a single gulp. Stop and take a breath. I don't expect you to get it in one fell swoop. In the following section, I have broken down many of the ideas you saw in the preceding example into more manageable bites.

XAML Bindings, Simplified

To start with a simple example of how the Binding object works, let's look at the following very simple XAML document.

<Window 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  >
  <Canvas>
    <TextBox Text="This is a TextBox" />
    <TextBlock Canvas.Top="25" >Test</TextBlock>
  </Canvas>
</Window>

This creates a simple canvas with the two controls, as shown in Figure 3.

Aa480224.wpfdatabinding_pt103(en-us,MSDN.10).gif

Figure 3. A simple XAML canvas

We may want to bind the TextBlock to show the TextBox's text as it is typed. To do this, we need a Binding object to tie the two objects together. The first thing to do is to add a name to the TextBox, so that we can refer to it by the element's name.

<TextBox Name="theTextBox" />

Next, we need to add a Binding to the TextBlock's Text element.

<TextBlock Canvas.Top="25">
  <TextBlock.Text>
    <Binding ElementName="theTextBox" Path="Text" />
  </TextBlock.Text>
</TextBlock>

This tells the TextBlock's Text to be set as the user types into the theTextBox control.

Putting this all together yields the following code.

<Window 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  >
  <Canvas>
    <TextBox Name="theTextBox" Text="Hello" />
    <TextBlock Canvas.Top="25">
      <TextBlock.Text>
        <Binding ElementName="theTextBox" Path="Text" />
      </TextBlock.Text>
    </TextBlock>
  </Canvas>
</Window>

This XAML creates a form that changes the TextBlock as you type in the TextBox, as shown in Figure 4.

Aa480224.wpfdatabinding_pt104(en-us,MSDN.10).gif

Figure 4. A bound control

Congratulations: you have your first binding! But that XML syntax is a bit verbose. There should be a better way to write it.

Using Binding Shorthand

In the previous example, we saw how to create data binding by adding Binding elements to properties. In this simple example, it does not seem too onerous to do data binding this way; however, as your XAML documents grow, it is likely that this verbose syntax will become cumbersome. To alleviate this problem, XAML supports a shorthand version of the binding syntax.

For example, using the shorthand, our previous example would look as follows.

<Window 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  >
  <Canvas>
    <TextBox Name="theTextBox" Text="Hello" />
    <TextBlock Canvas.Top="25"
               Text="{Binding ElementName=theTextBox, Path=Text}" />
  </Canvas>
</Window>

The shorthand syntax is enclosed in curly braces ({}), starts with the word Binding, and contains name/value pairs for attributes of the binding. The purpose of the shorthand syntax is to enable data binding with fewer keystrokes and more readability. For the rest of this article, I will be using the shorthand syntax.

Binding Sources

Up to this point, we've used another control as the source for all of our binding examples. In most XAML-based projects, however, you will be binding to sources other than other controls. The key to most data binding is the Source property. In the previous examples, we are using the ElementName property, which is used to bind to a control, instead of using the Source property. For most applications, you will want to bind to more significant sources, such as XML or .NET objects. XAML supports this with its data Provider objects. There are two types of data providers built into XAML: ObjectDataProvider and XmlDataProvider. The ObjectDataProvider is used for binding to and from .NET objects, and, not surprisingly, the XmlDataProvider is used to bind to and from XML fragments and documents. You can specify a data provider in the resources section of any XAML container.

Using the XmlDataProvider

The following is an example using the XmlDataProvider object.

<StackPanel 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  >
  <StackPanel.Resources>
    <XmlDataProvider x:Key="FavoriteColors">
      <x:XData>
        <Colors xmlns="">
          <Color>Blue</Color> 
          <Color>Black</Color> 
          <Color>Green</Color> 
          <Color>Red</Color> 
        </Colors>
      </x:XData>
    </XmlDataProvider>
  </StackPanel.Resources>
  <TextBlock HorizontalAlignment="Center" 
             FontWeight="Bold">
    XML Example
  </TextBlock>
  <ListBox Width="200" Height="300" 
           ItemsSource="{Binding Source={StaticResource FavoriteColors}, 
           XPath=/Colors/Color}">
  </ListBox>
</StackPanel>

Within the StackPanel's resources, we have an XmlDataProvider object. The x:Key signifies the name to use in order to refer to it in Binding objects. Within the provider, we have created XML inline, to use as the source of the data binding. In order to use inline data, you must surround it with an XData element, as shown in the code. In the Binding for the ListBox, we have specified the provider as the Source of the binding. When a data source is located in the XAML document, you need to specify that the object is a static resource, as shown in the code. Lastly, we use an XPath statement to specify what collection within the XML document should be used to fill the ListBox. This code generates the form shown in Figure 5.

Aa480224.wpfdatabinding_pt105(en-us,MSDN.10).gif

Figure 5. XML data binding

Alternatively, we can specify that the provider use a path or URL to find the source of the XML to create the same form, by specifying the Source attribute of the XmlDataProvider.

<XmlDataProvider x:Key="FavoriteColors" 
                 Source="D:\Writing\msdn\avalondatabindingpt1\xaml\colors.xml"
/>

The Source attribute of the XmlDataProvider can also point at standard URLs, to allow you to create quick access to XML APIs such as RSS.

<StackPanel 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  >
  <StackPanel.Resources>
    <XmlDataProvider x:Key="MyRSS" 
      Source="http://adoguy.com/RSS"
    />
  </StackPanel.Resources>
  <TextBlock HorizontalAlignment="Center" 
             FontWeight="Bold">
    XML Example
  </TextBlock>
  <ListBox Width="500" Height="300" 
           ItemsSource="{Binding Source={StaticResource MyRSS}, 
           XPath=//item/title}">
  </ListBox>
</StackPanel>

By calling out to a RSS feed, I can create a page that quickly lists the topics of my blog in a ListBox, as shown in Figure 6.

Aa480224.wpfdatabinding_pt106(en-us,MSDN.10).gif

Figure 6. My blog's topic list

Using the ObjectDataProvider

There are times when binding to .NET objects is required. This is where the ObjectDataProvider comes in. This data provider allows you to create bindings for .NET data types.

For example, we can create a simple string collection in .NET, as follows.

public class MyStrings : List<String>
{
  public MyStrings()
  {
    this.Add("Hello");
    this.Add("Goodbye");
    this.Add("Heya");
    this.Add("Cya");
  }
}

–or–

Public Class MyStrings
  Inherits List(Of String)

  Public Sub New()
    Me.Add("Hello")
    Me.Add("Goodbye")
    Me.Add("Heya")
    Me.Add("Cya")
  End Sub

End Class

You can also use a processing instruction in the XAML document to add an entire namespace of CLR objects to the document's supported types, by specifying the namespace as an xmlns declaration. For example, we can map an entire namespace's classes into the XAML as follows.

<Window 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Simple Source Binding"
    xmlns:local="clr-namespace:XamlExamples"
    x:Class="XamlExamples.SimpleSource"
 >
  <!-- ... -->
</Window>

To import classes from an external assembly, you can specify an xmlns declaration, but specify the assembly name, as follows.

<Window 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Simple Source Binding"
    xmlns:sys="clr-namespace:System,assembly=mscorlib"
    x:Class="XamlExamples.SimpleSource"
 >
  <!-- ... -->
</Window>

Once you've imported the types, you can use an ObjectDataProvider to specify a data source from one of those types.

<Window 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Simple Source Binding"
    xmlns:local="clr-namespace:XamlExamples"
    x:Class="XamlExamples.SimpleSource"
 >
  <Window.Resources>
    <ObjectDataProvider x:Key="MyStringData" 
                        ObjectType="{x:Type local:MyStrings}" />
  </Window.Resources>  
    <StackPanel>
      <TextBlock HorizontalAlignment="Center" 
               FontWeight="Bold">
      Simple Source Example
    </TextBlock>
    <ListBox Name="theListBox" Width="200" Height="300" 
      ItemsSource="{Binding Source={StaticResource MyStringData}}"/>
  </StackPanel>
</Window>

You can also create the resource by specifying a XAML element with the name of the namespace and the type to use, as follows.

<Window 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Simple Source Binding"
    xmlns:local="clr-namespace:XamlExamples"
    x:Class="XamlExamples.SimpleSource"
 >
  <Window.Resources>
    <local:MyStrings x:Key="MyStringData" />
  </Window.Resources>  
    <StackPanel>
      <TextBlock HorizontalAlignment="Center" 
               FontWeight="Bold">
      Simple Source Example
    </TextBlock>
    <ListBox Name="theListBox" Width="200" Height="300" 
      ItemsSource="{Binding Source={StaticResource MyStringData}}"/>
  </StackPanel>
</Window>

This syntax works just like the ObjectDataSource, but it is a bit simpler to use. Now that the namespace is imported, we can add a resource that refers to our data source class, by specifying the name of the class (for example, MyStrings). Data binding is identical to earlier examples, because the XAML code does not care what kind of data source it is, just that it is a data source.

Binding Mode

In most cases, you will want the Binding to be a two-way binding. The Binding object supports several modes to support difference use cases, as shown in Table 1.

Table 1. Supported binding modes

Binding ModeDescription
TwoWayMoves changes, from either the bound control or the source of the binding, to one other in a bi-directional way. (This is the default mode.)
OneWayMoves changes only from the source to the control. As changes occur in the source, the bound control's data is changed.
OneTimeData is bound only at startup, and changes to the source are ignored once the control is filled with data the first time.

You can specify the mode by simply including the mode in the markup, as follows.

{Binding ElementName=theTextBox, Path=Text, Mode=OneTime}

One way to see the how the bi-directional binding works is to create a canvas with two text boxes, one bound to the other.

<Window    
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  >
  <Canvas>
    <TextBox Name="theTextBox" Text="Hello" />
    <TextBox Canvas.Top="25"
             Text="{Binding ElementName=theTextBox, Path=Text, Mode=TwoWay}" />
  </Canvas>
</Window>

If you paste this code into the XAMLPad tool that ships with the SDK, you should notice that the source text box updates the bound text box as you type, but the update from the bound control to the source control occurs only when the bound control loses focus. If you change the Mode to OneWay or OneTime, you can see how these different modes change the way the bindings work.

Controlling Binding Time

In addition to the Mode, you can also specify when the binding pushes the changes, by using the UpdateSourceTrigger. You can specify that the binding make the change only at specified times, by specifying the UpdateSourceTrigger type.

{Binding ElementName=theTextBox, Path=Text, UpdateSourceTrigger=LostFocus}

The UpdateSourceTrigger property specifies when to update the source with changes. This is valid only with Mode=TwoWay bindings (which is the default). The valid values for the UpdateSourceTrigger are shown in Table 2.

Table 2. UpdateSourceTrigger values

UpdateSourceTriggerDescription
ExplicitThe source is updated only by explicitly calling the BindingExpression.UpdateSource method.
LostFocusThe source is updated as the bound control loses focus.
PropertyChangedChanges are updated to the source every time the property changes. This is the default behavior.

Using the DataContext

The last concept to cover in this article is how to use the DataContext. The DataContext is specifically used to specify that all the controls within some container will be bound to a common object.

For example, the following is an example where we have a Canvas that will show the value and text of a specific node in the XML document.

<StackPanel 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  >
  <StackPanel.Resources>
    <XmlDataProvider x:Key="FavoriteColors">
      <Colors xmlns="">
        <Color ID="1">Blue</Color> 
        <Color ID="2">Black</Color> 
        <Color ID="3">Green</Color> 
        <Color ID="4">Red</Color> 
      </Colors>
    </XmlDataProvider>
  </StackPanel.Resources>
  <TextBlock HorizontalAlignment="Center" 
             FontWeight="Bold">
    XML DataContext Example
  </TextBlock>
  <Canvas DataContext="{Binding Source={StaticResource FavoriteColors}, 
                        XPath='/Colors/Color[@ID=2]'}">
    <TextBlock Text="{Binding XPath=@ID}" />
    <TextBlock Canvas.Top="25" Text="{Binding XPath=.}" />
  </Canvas>
</StackPanel>

By setting the DataContext to the XML document (and a specific XPath expression), we are telling the Canvas that all controls within that do not contain a Source can use the container's DataContext. In this way, we can bind the TextBlocks by simply specifying XPath expressions. Note that the XPath expressions in each of the TextBlocks are relative XPath expressions. This would be the same for object binding.

<Window 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Simple Source Binding"
    x:Class="XamlExamples.SimpleSource"
  >
  <Window.Resources>
    <ObjectDataProvider TypeName="XamlExamples.MyStrings, XamlExamples"
                        x:Key="MyStringData" />
  </Window.Resources>  
    <StackPanel>
      <TextBlock HorizontalAlignment="Center" 
               FontWeight="Bold">
      Object DataContext Example
    </TextBlock>
    <Canvas DataContext="{Binding Source={StaticResource MyStringData}}">
      <TextBlock Text="{Binding Path=Length}" />
      <TextBlock Canvas.Top="25" Text="{Binding Path=Item[0]}" />
    </Canvas>
  </StackPanel>
</Window>

Using objects instead of XML simply means that you will want to use Path expressions instead of XPath expressions.

Where Are We?

We have seen how to do data binding directly in XAML with the Binding object, both in shorthand and longhand versions. By utilizing the XML or Object data providers, we can bind to different types of objects in our applications. We also saw how to bind to other controls by using the ElementName syntax of the Binding object. In the next part of this series, I will show you how to take this information and bind to real database data, using your own objects or .NET data containers (for example, DataSources and DataSets).

References

Show:
© 2014 Microsoft