XML Data Binding in WPF

Data binding is a powerful and time saving feature of the WPF framework. It allows a depencency property in the application’s view layer, such as a TextBox’s Text property, to be bound to a property in the application’s underlying data model. Data model in this instance means .NET classes or, as I’ll show later, an XML document. Data binding saves us from the tedious task of writing code to move data to and from WPF controls on the screen.

In my last two posts about styling the ListView I bound a ListView control to an underlying collection of type ObservableCollection<string>. Most real-world applications will bind ItemsControls to a collection of objects more complex than string. The screen controls then access properties of the underlying objects using data binding. More visually complex representations can be created by using data templates.

The XmlDataProvider in conjunction with the Binding class enable binding to elements and attributes in an XML DOM using XPath expressions. XmlDataProvider can load an XML document from a local system disk, from a remote system, or, as the example here does, from an XML data island in the application itself. XmlDataProviders can be easily created in a resource dictionary using XAML.

<Application.Resources>
    <XmlDataProvider x:Key="Xml1" Source="c:\documents\xml\automobiles.xml"/>
    <XmlDataProvider x:Key="Xml2" Source="http://www.somecompany.com/finance/Q3results.xml"/>

The sample application here displays various facts about the states of the United States of America. The data is limited to the first 13 states as all 50 aren’t needed for the example. They are listed in the order that they ratified the Constitution for the United States of America. The XML schema for the document is listed below.

<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:complexType name="StateType">
    <xs:all>
      <xs:element name="Capital" type="xs:string" />
      <xs:element name="Nickname" type="xs:string" />
      <xs:element name="Bird" type="xs:string" />
      <xs:element name="Flower" type="xs:string" />
      <xs:element name="Tree" type="xs:string" />
      <xs:element name="Motto" type="xs:string" />
    </xs:all>
    <xs:attribute name="Name" type="xs:string" use="required" />
    <xs:attribute name="Abbrev" type="xs:string" use="required" />
  </xs:complexType>
  <xs:complexType name="StatesType">
    <xs:sequence maxOccurs="unbounded">
      <xs:element name="State" type="StateType" />
    </xs:sequence>
  </xs:complexType>
  <xs:element name="States" type="StatesType" />
</xs:schema>

The XmlDataProvider containing the XML document itself is created in the application resource dictionary. Note that the data island is contained within an <x:XData> element.

<XmlDataProvider x:Key="StateData">
    <x:XData>
        <States xmlns="">
            <State Name="Delaware" Abbrev="DE">
                <Capital>Dover</Capital>
                <Nickname>The First State</Nickname>
                <Bird>Blue Hen Chicken</Bird>
                <Flower>Peach Blossom</Flower>
                <Tree>American Holly</Tree>
                <Motto>Liberty and Independence</Motto>
            </State>
            <!-- Remaining states omitted for brevity -->
        </States>
    </x:XData>
</XmlDataProvider>

The application window consists of two panes. The left-hand pane contains a ListBox that lists the states from the XML document. This isn’t a simple list of state names. A DataTemplate is used to display an image of the state’s flag along with its name. The GIF images of the state flags used in this example were provided by the kind folks at 3DFlags.com.

xmldata

The DataTemplate is created in the application resource dictionary. See the XAML source below. Note that a value converter is used to construct the name of the flag image file. The GIF images are included in the Visual Studio project in a folder named Images.

<local:ImageNameConverter x:Key="ImageNameConverter"/>
<DataTemplate x:Key="StateListTemplate">
    <StackPanel Orientation="Horizontal">
        <Image Width="64" Source="{Binding XPath=@Abbrev,Converter={StaticResource ImageNameConverter}}"/>
        <TextBlock Margin="6,12,0,0" Text="{Binding XPath=@Name}" FontSize="14"/>
    </StackPanel>
</DataTemplate>

Here’s the source for the value converter.

class ImageNameConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if (value is string)
        {
            return @"Images\" + (value as string) + ".gif";
        }
        else
        {
            throw new ArgumentException();
        }
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

In the main Window I establish the data source for the window by binding to the XmlDataSource. An XPath expression is used to access the collection of <State> elements in the document.

<Window x:Class="XMLData.Window1"
    DataContext="{Binding Source={StaticResource StateData},XPath=//States/State}">
</Window>

The ListBox ItemTemplate property references the DataTemplate shown earlier.

<ListBox x:Name="_stateList" Grid.Column="0" Margin="2,2,6,2" 
         Background="Beige"
         ItemsSource="{Binding}" ItemTemplate="{StaticResource StateListTemplate}"
         IsSynchronizedWithCurrentItem="True"/>

The rest of the application references the ListBox’s SelectedItem. For example the status bar displays the name of the state currently selected in the ListBox. I did this by binding the Text property of a TextBlock to the SelectedItem property of the ListBox. A value converter is used once again to get the desired text.

<StatusBarItem>
    <TextBlock Text="{Binding ElementName=_stateList,Path=SelectedItem,Converter={StaticResource SelectedItemConverter}}"/>
</StatusBarItem>

The right-hand pane of the application displays far more information than just the state name. A FlowDocumentScrollViewer is used to display a FlowDocument that contains the relevant information. Within the FlowDocument instances of TextBlock have their Text properties bound to various elements within the currently selected <State> element. Again, XPath expressions are used to obtain the desired data. By using data binding I avoid writing any C# code whatsoever to update the details pane when a new state is selected in the list box. The WPF binding framework handles this automatically. In fact there is very little code behind in this application at all. All data movement is handled through binding. I only created two value converters to coerce some of the data into a more displayable form.

<FlowDocumentScrollViewer Grid.Column="1" Margin="2">
    <FlowDocument Background="Beige" DataContext="{Binding ElementName=_stateList,Path=SelectedItem}">
        <Paragraph FontSize="16" FontStyle="Italic" FontWeight="Bold" TextAlignment="Center">
            <TextBlock Text="{Binding XPath=@Abbrev}"/>
            <Run Text=" - "/>
            <TextBlock Text="{Binding XPath=@Name}"/>
        </Paragraph>
        <Table FontSize="12" CellSpacing="8">
            <Table.Columns>
                <TableColumn Width="Auto"/>
                <TableColumn/>
            </Table.Columns>
            <TableRowGroup>
                <TableRow>
                    <TableCell><Paragraph>Capital</Paragraph></TableCell>
                    <TableCell>
                        <Paragraph>
                            <TextBlock Text="{Binding XPath=Capital}"/>
                        </Paragraph>
                    </TableCell>
                </TableRow>
                <!-- Other rows omitted for brevity -->
            </TableRowGroup>
        </Table>
    </FlowDocument>
</FlowDocumentScrollViewer>

All source code may be downloaded here.

Advertisements

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




January 2009
M T W T F S S
« Dec   Feb »
 1234
567891011
12131415161718
19202122232425
262728293031  
I am a part of all that I have met;
Yet all exprience is an arch whitherthro'
Gleams that untravell'd world, whose margin fades
For ever and for ever when I move.
How dull it is to pause, to make an end,
To rust unburnish'd, not to shine in use!
Alfred, Lord Tennyson

%d bloggers like this: