XAML Markup Extensions
Before we take a look at the results of our data binding, let’s take a moment to discuss
XAML markup extensions, which is what you’re using when you set an attribute
to something inside of curly braces (e.g., Text="{Binding Path=Name}"). Markup
extensions add special processing to XAML attribute values. For example, this:
<TextBox Text="{Binding Path=Name}" />
is just a shortcut for this (which you’ll recognize as the property element syntax):
<TextBox.Text>
<Binding Path="Name" />
</TextBox.Text>
Data Templates
With the data binding markup syntax explained, let’s turn back to our example data
binding application, which so far doesn’t look quite like what we had in mind,It’s clear that the data is making its way into the application, because the currently
selected name and nickname are shown for editing. The problem is that, unlike the
TextBox controls, which were each given a specific field of the Nickname object to show,
the ListBox is expected to show the whole thing. Lacking special instructions, it’s calling
the ToString method of each object, which results in only the name of the type. To
show the data, we need to compose a data template.
Example . Using a data template
<ListBox
ItemsSource="{Binding}"
IsSynchronizedWithCurrentItem="True">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock>
<TextBlock Text="{Binding Path=Name}" />:
<TextBlock Text="{Binding Path=Nick}" />
</TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
A data template is a set of elements that should be inserted somewhere. In our case,
we are specifying a data template to be inserted for each listbox item by setting the
ItemTemplate property. In Example, we’ve composed a data template from a text block that flows together two other text blocks, each bound to a property on a
Nickname object separated by a colon, At this point, we’ve got a completely data-bound application. As data in the collection
or the individual objects changes, the UI will be updated, and vice versa. However,
there is a great deal more to say on this topic, including binding to XML and
relational data, master-detail binding, and hierarchical binding.
Dependency Properties
Although our data source Nickname object made its data available via standard .NET
properties, we need something special to support data binding on the target element.
Even though the TextContent property of the TextBlock element is exposed
with a standard property wrapper, in order for it to integrate with WPF services like
data binding, styling, and animation, it also needs to be a dependency property. A
dependency property provides several features not present in .NET properties,
including the ability to inherit its value from a container element, provide for objectindependent
storage (providing a potentially huge memory savings), and change
tracking.
Most of the time, you won’t have to worry about dependency properties versus .NET
properties.
Resources
Resources are named chunks of data defined separately from code and bundled with
your application or component. .NET provides a great deal of support for resources,
a bit of which we already used when we referenced tom.png from our XAML button
earlier in this chapter. WPF also provides special support for resources scoped to elements
defined in the tree.
As an example, let’s declare some default instances of our custom Nickname objects in
XAML
Notice the Window.Resources, which is property element syntax to set the Resources
property of the Window1 class. Here we can add as many named objects as we like,
with the name coming from the Key attribute and the object coming from the XAML
elements (remember that a XAML element is just a mapping to .NET class names).
In this example, we’re creating a Nicknames collection named names to hold three
Nickname objects, each constructed with the default constructor, and then setting
each of the Name and Nick properties.
Also notice the use of the StaticResource markup extension to reference the names
resource as the collection to use for data binding. With this XAML in place, our window
construction reduces to the code shown in Example
Example . Declaring objects in XAML
<!-- Window1.xaml -->
<Window ... xmlns:local="clr-namespace:DataBindingDemo" />
<Window.Resources>
<local:Nicknames x:Key="names">
<local:Nickname Name="Don" Nick="Naked" />
<local:Nickname Name="Martin" Nick="Gudge" />
<local:Nickname Name="Tim" Nick="Stinky" />
</local:Nicknames>
</Window.Resources>
<DockPanel DataContext="{StaticResource names}">
<TextBlock DockPanel.Dock="Top" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center">Name: </TextBlock>
<TextBox Text="{Binding Path=Name}" />
<TextBlock VerticalAlignment="Center">Nick: </TextBlock>
<TextBox Text="{Binding Path=Nick}" />
</TextBlock>
...
</DockPanel>
</Window>
Example . Finding a resource in code
public partial class Window1 : Window {
Nicknames names;
public Window1( ) {
InitializeComponent( );
this.addButton.Click += addButton_Click;
// get names collection from resources
this.names = (Nicknames)this.FindResource("names");
// no need to make data available for binding here
//dockPanel.DataContext = this.names;
}
void addButton_Click(object sender, RoutedEventArgs e) {
this.names.Add(new Nickname( ));
}
}
Now instead of creating the collection of names, we can pull it from the resources
with the FindResource method. Just because this collection was created in XAML
doesn’t mean that we need to treat it any differently than we treated it before, which
is why the Add button event handler is the exact same code. Also, there’s no need to
set the data context on the dock panel because that property was set in the XAML.
For the full scoop on resources, including resource scoping and lookup, static and
dynamic binding to resources, and using resources for theming and skinning,