Silverlight–Displaying Tookit Charts from XML Data

Every now and again I revisit my obsession with data-binding to XML data and Silverlight and this is one of those posts.

Now, to be fair, this was prompted by chatting to Rich who pointed me at one of my old blog posts about binding to XML data;

“Silverlight and XML Binding”

which I fully accept has a bunch of limitations but does (via a hacky use of being able to bind to string indexers) get you some way towards binding to XML.

In that blog post, I wrote two classes – BindableXNode and XmlDataSource and I’ll repeat neither of them here.

Our chat was about binding from XML data onto a Silverlight Toolkit charting control and, specifically, binding multiple datasets onto a single chart from that XML.

Now, I don’t think that a Silverlight Toolkit chart control has a facility to bind to multiple sources of chart data at a time and so I borrowed a simplified version of a MultiChart control from this excellent blog post by Beat which adds a SeriesSource property to the ChartControl ( it also does a little more that I didn’t need so I removed those bits for simplicity ).

Then I wrote a little XML file of test data;

<?xml version="1.0" encoding="utf-8" ?>
<dataSets>
  <dataSet title="Two Times Table">
    <data x="1"
          y="2"/>
    <data x="2"
          y="4"/>
    <data x="3"
          y="6"/>
    <data x="4"
          y="8"/>
    <data x="5"
          y="10"/>
  </dataSet>
  <dataSet title="Three Times Table">
    <data x="1"
          y="3"/>
    <data x="2"
          y="6"/>

  <!-- you get the idea, it goes up to 5 times table -->

</dataSets>

and I dropped this into my ClientBin folder on my web site within a new Silverlight project;

image

Now, time to read it and bind it onto the screen. Here’s the simple UI I ended up with;

image

In the left hand column is a ListBox which is displaying the 5 charts separately whilst in the right hand column is a MultiChart displaying all the lines on a single chart.

Here’s the XAML I ended up with (there’s no code in this blog post, it’s a XAML-only zone Smile);

<UserControl
  x:Class="XmlTestApp.MainPage"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  xmlns:local="clr-namespace:XmlTestApp"
  mc:Ignorable="d"
  d:DesignHeight="300"
  d:DesignWidth="400"
  xmlns:toolkit="http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit">

  <!-- My kingdom for a markup extension 🙂 -->
  <UserControl.Resources>
    <local:XmlDataSource
      x:Key="dataSource"
      DownloadUri="data.xml" />
  </UserControl.Resources>

  <Grid
    x:Name="LayoutRoot"
    Background="White">
    <Grid.ColumnDefinitions>
      <ColumnDefinition />
      <ColumnDefinition />
    </Grid.ColumnDefinitions>
    <ListBox
      DataContext="{StaticResource dataSource}"
      ItemsSource="{Binding Path=Xml.[descendant-or-self::dataSet]}">
      <ListBox.ItemTemplate>
        <DataTemplate>
          <Border
            BorderBrush="Black"
            CornerRadius="2"
            Margin="3"
            Padding="3">
            <Grid>
              <Grid.RowDefinitions>
                <RowDefinition
                  Height="Auto" />
                <RowDefinition
                  MaxHeight="144" />
              </Grid.RowDefinitions>
              <TextBlock
                Margin="3"
                HorizontalAlignment="Center"
                Text="{Binding Path=[string(attribute::title)]}" />
              <toolkit:Chart                
                Grid.Row="1"
                Margin="3"
                HorizontalAlignment="Center"
                Title="{Binding Path=[string(attribute::title)]}">
                <toolkit:Chart.Series>
                  <toolkit:LineSeries
                    Title="{Binding Path=[string(attribute::title)]}"
                    ItemsSource="{Binding Path=[child::data]}"
                    DependentValueBinding="{Binding Path=[number(attribute::y)]}"
                    IndependentValueBinding="{Binding Path=[number(attribute::x)]}" />
                </toolkit:Chart.Series>
              </toolkit:Chart>
            </Grid>
          </Border>
        </DataTemplate>
      </ListBox.ItemTemplate>
    </ListBox>
    <local:MultiChart
      Grid.Column="1"
      DataContext="{StaticResource dataSource}"
      SeriesSource="{Binding Path=Xml.[descendant-or-self::dataSet]}">      
      <local:MultiChart.SeriesTemplate>
        <DataTemplate>
          <toolkit:LineSeries
            Title="{Binding Path=[string(attribute::title)]}"
            ItemsSource="{Binding Path=[child::data]}"
            DependentValueBinding="{Binding Path=[number(attribute::y)]}"
            IndependentValueBinding="{Binding Path=[number(attribute::x)]}" />
        </DataTemplate>
      </local:MultiChart.SeriesTemplate>
    </local:MultiChart>
  </Grid>
</UserControl>

and so this is using my XmlDataSource class to asynchronously download data.xml. It then sets up a bunch of bindings;

  • ListBox – ItemsSource = dataSet elements
    • TextBlock – Text = title attribute
    • Chart
      • LineSeries
        • Title = title attribute
        • ItemsSource = data child elements
        • DependentValueBinding = y attribute
        • IndependentValueBinding = x attribute
  • MultiChart – SeriesSource = dataSet elements
    • SeriesTemplate
      • LineSeries (bound up as before & duplicated here)

and that’s pretty much it. Async download of XML and charts onto the screen with no “code” to write but a lot of XAML to play around with.

Here’s the project for download.

Please note this includes a source file that is not mine (MultiChart.cs) and I’ve clearly marked it that way. You should contact the original author around use of his work.