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.
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="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://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="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://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).
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="https://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.
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="https://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.
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="https://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="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
>
<StackPanel.Resources>
<XmlDataProvider x:Key="FavoriteColors"> <x:XData> <Colors > <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.
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="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://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.
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="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://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="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://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="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://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="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://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 Mode | Description |
---|---|
TwoWay | Moves 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.) |
OneWay | Moves changes only from the source to the control. As changes occur in the source, the bound control's data is changed. |
OneTime | Data 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="https://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
UpdateSourceTrigger | Description |
---|---|
Explicit | The source is updated only by explicitly calling the BindingExpression.UpdateSource method. |
LostFocus | The source is updated as the bound control loses focus. |
PropertyChanged | Changes 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="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
>
<StackPanel.Resources>
<XmlDataProvider x:Key="FavoriteColors">
<Colors >
<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="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://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
- WinFX SDK
- Programming the Windows Presentation Foundation, by Chris Sells and Ian Griffiths