Databinding Multi-Series Charts

I am working on a Silverlight project, where we doing straight MVVM. Binding the Chart control included in the Silverlight Toolkit (www.codeplex.com/Silverlight) to a ViewModel is quite simple. But what when I want to bind a collection of different “graphs” each with an own collection of data-points to a single chart?

Suppose a line-chart with dynamically changing the numbers of lines.

image

I decided to use a DataTemplate similar to the ItemsTemplate in the ItemsControl class.
So, I created the MultiChart control as sub class of the Silverlight Toolkit Chart control.

public class MutliChart : System.Windows.Controls.DataVisualization.Charting.Chart
{
}

 

Then I added two dependency properties (full source code is attached)

public IEnumerable SeriesSource
{
    get { return (IEnumerable)GetValue(SeriesSourceProperty); }
    set { SetValue(SeriesSourceProperty, value); }
}

public DataTemplate SeriesTemplate
{
    get { return (DataTemplate)GetValue(SeriesTemplateProperty); }
    set { SetValue(SeriesTemplateProperty, value); }
}

 Now, I’m able to databind my multi-series datasource to the SeriesSource property similar as one binds the ItemsSource to an ItemsControl or a DataGrid control. The MultiChart control generates DataSeries items based on the SeriesTemplate. The series are now full bindable to a dynamic list in the ViewModel.

The XAML locks like this:

    <local:MultiChart SeriesSource="{Binding MySalesData}" >
        <local:MultiChart.SeriesTemplate >
            <DataTemplate >
                <chartingToolkit:LineSeries
			       Title="{Binding Title}"
			       ItemsSource="{Binding Sales}"
			       IndependentValueBinding="{Binding SalesName}"
			       DependentValueBinding="{Binding SalesTotal}" />
            </DataTemplate>
        </local:MultiChart.SeriesTemplate>
    </local:MultiChart>

This implementation is similar to the content model in other controls i.e., Content and ContentTemplate, Header and HeaderTemplate, Items or ItemsSource and ItemTemplate, etc.

But what if I want to have different chart-series in the same chart such as column-series and line-series databound to the ViewModel?

image

What I need is a different DataTemplate per item type in the data source. Unfortunately Silverlight does not support typed data-templates natively. WPF answers this problem with DataTemplateSelector (and the ContentTemplateSelector, HeaderTemplateSelector, and ItemTemplateSelector properties).  Silverlight doesn’t yet support it, but we can achieve the same effect after a little more code.

So, I added another dependency property called SeriesTemplateSelector.

public DataTemplateSelector SeriesTemplateSelector
{
    get { return (DataTemplateSelector)GetValue(SeriesTemplateSelectorProperty); }
    set { SetValue(SeriesTemplateSelectorProperty, value); }
}

The class DataTemplateSelector does not exists in Silverlight. So, I just copied the DataTemplateSelector base class from the WPF assembly (via Reflector).

// code from WPF

using System;
using System.Windows;

namespace System.Windows.Controls
{
    // Summary: 
    // Provides a way to choose a System.Windows.DataTemplate based on the data 
    // object and the data-bound element. 
    public class DataTemplateSelector
    {
        // Summary: 
        // Initializes a new instance of the System.Windows.Controls.DataTemplateSelector 
        // class. 
        public DataTemplateSelector()
        {
        }

        // Summary: 
        // When overridden in a derived class, returns a System.Windows.DataTemplate 
        // based on custom logic. 
        // 
        // Parameters: 
        // item: 
        // The data object for which to select the template. 
        // 
        // container: 
        // The data-bound object. 
        // 
        // Returns: 
        // Returns a System.Windows.DataTemplate or null. The default value is null. 
        public virtual DataTemplate SelectTemplate(object item, DependencyObject container)
        {
            return null;
        }
    }
}

 If the SeriesTemplateSelector is set then the MultiChart control selects its data-template with this instance.

At the end my XAML looks like this:

<UserControl>
    <UserControl.Resources >
        <local:SeriesTemplateSelector x:Key="chartTemplateSelector">
            <local:SeriesTemplateSelector.SalesTemplate>
                <DataTemplate >
                    <chartingToolkit:LineSeries
                                    Title="{Binding SalesName}"
                                    ItemsSource="{Binding SalesTotals}"
                                    IndependentValueBinding="{Binding Date}"
                                    DependentValueBinding="{Binding SalesTotal}" />
                </DataTemplate>
            </local:SeriesTemplateSelector.SalesTemplate>
            <local:SeriesTemplateSelector.MedianTemplate>
                <DataTemplate >
                    <chartingToolkit:ColumnSeries
                                    Title="{Binding SalesName}"
                                    ItemsSource="{Binding SalesTotals}"
                                    IndependentValueBinding="{Binding Date}"
                                    DependentValueBinding="{Binding SalesTotal}" />
                </DataTemplate>
            </local:SeriesTemplateSelector.MedianTemplate>
        </local:SeriesTemplateSelector>
    </UserControl.Resources>
    <Grid x:Name="LayoutRoot">
            <local:MultiChart SeriesSource="{Binding SalesDataWithMedian}"
                              SeriesTemplateSelector="{StaticResource chartTemplateSelector}"
                              Title="Dynamic Multi Lines with different DataTemplates">

            </local:MultiChart>
    </Grid>
</UserControl>

Here the implementation of SeriesTemplateSelector class:

   public class SeriesTemplateSelector : DataTemplateSelector
    {
        public DataTemplate SalesTemplate { get; set; }
        public DataTemplate MedianTemplate { get; set; }

        public override DataTemplate SelectTemplate(object item, DependencyObject container)
        {

            if (item is SalesPerformance)
            {
                SalesPerformance salesPerf = item as SalesPerformance;

                if (salesPerf.SalesName == "Median")
                {
                    return MedianTemplate;
                }
                else
                {
                    return SalesTemplate;
                }
            }

            // default
            return null;

        }
    }
 

Finally I got a fully bindable mutli-series chart control which provides the same content model as other well-known control such the ItemsControl.

Here you can find the source code in a demo project.

UPDATE 05. Dec. 2010

Due to some great freedback, I’ve attached a quick implementation of the collection changed event with the use of the weak-event pattern, in order that you can work with an observable collection too.

Please have a lock to the attached sample project:
http://files.thekieners.com/blogcontent/2010/MultiChartDemo2.zip

There is a new checkbox on top of the demo-view where you can set a flag in the view model in order that the source gets updated instead of creating the new on each timer tick.
So you should be able to see how it works. Please note that only the charts on the left side changing the number of series-items or numbers of series.

I hope this is helpfully and any further feedback is much appreciated.

About these ads
Tagged

40 thoughts on “Databinding Multi-Series Charts

  1. [...] behavior with native UIElement MouseWheel eventMemory leak with focusable UIElement in Silverlight?Databinding Multi-Series ChartsBe careful when using WeakReference.IsAliveBasicHttpBinaryBinding for [...]

  2. enio says:

    how do you inherit from a sealed class?
    i download the demo project and visual studio is telling me that, “MultiChart: cannot derive from a sealed class System.Windows.Controls.DataVisualization.Charting.Chart”.

    can you help me with this ?

  3. enio says:

    yeah, now this is working, in the changes of the last version says:
    “Supports more flexible subclassing scenarios of core classes”
    this solves my problem to derive the MultiChart from the Chart class,

    but, what if i want use it in a WPF Application, and not in silverlight application?

    is been quite a mess, because a need to get the WPFToolkit, and i am very confusing with the correct versions of the System.Windows.Controls.DataVisualization.Toolkit that i need to have to run it in a WPF Application.

  4. Brian says:

    I’m setting data point colors via binding as seen in these examples, http://blogs.msdn.com/b/delay/archive/2009/02/04/columns-of-a-different-color-customizing-the-appearance-of-silverlight-charts-with-re-templating-and-mvvm.aspx

    Do you have any thoughts as to how to get the Legend colors to match the chart colors?

    Thanks

  5. Matthias says:

    Thank you for sharing this great article. I try to binding the color of the series dynamicly by using this:

    But it doesn’t work. I get an XamlParseException.
    ystem.Windows.Markup.XamlParseException ist aufgetreten.
    Message=”AG_E_RUNTIME_MANAGED_UNKNOWN_ERROR [Line: 9 Position: 69]”
    LineNumber=9
    LinePosition=69
    StackTrace:
    bei MS.Internal.XcpImports.MethodEx(IntPtr ptr, String name, CValue[] cvData)
    bei MS.Internal.XcpImports.MethodEx(DependencyObject obj, String name)
    bei MS.Internal.XcpImports.DataTemplate_LoadContent(DataTemplate template)
    bei System.Windows.DataTemplate.LoadContent()
    bei MultiChartDemo.MultiChart.OnSeriesSourceChanged(IEnumerable oldValue, IEnumerable newValue)

    Have you got an idea for a solution.
    Thanks
    Matthias

    • beatkiener says:

      Hi Matthias,

      Which Color property do you want to bind? I can’t find a color property on the Series class (or one of its subclasses).

      • Matthias says:

        Yes thats right. I add an Property LineColor to the class SalesPerformance and set them in default to black in the UpdateData()-Method in the MainPageViewModel-class. I configured the binding in this way

        chartingToolkit:LineSeries.DataPointStyle
        Style TargetType=”chartingToolkit:LineDataPoint”
        Setter Property=”Background” Value=”{Binding LineColor}”/
        /Style
        /chartingToolkit:LineSeries.DataPointStyle

  6. beatkiener says:

    Have you added the property as a dependency-property? Only dependency-property are allowed to use in XAML or the binding engine.

    If not, this link will be helpful for you: http://msdn.microsoft.com/en-us/library/ms750428.aspx

    • Matthias says:

      Why you can bind SalesName to Title – it is not an dependency-property?

      public class SalesPerformance
      {
      public string SalesName { get; set; }
      public List SalesTotals { get; set; }

      }

      chartingToolkit:LineSeries
      Title=”{Binding SalesName}”
      ItemsSource=”{Binding SalesTotals}”
      IndependentValueBinding=”{Binding Date}”
      DependentValueBinding=”{Binding SalesTotal}” /

      Thank you

  7. beatkiener says:

    Yes you are right. Sorry I’ve read it wrong. I think your problem in the binding exists because of wrong data-types. Background property needs a type or subtype of brush, like SolidColorBrush. Does your LineColor return a color or a brush?

    One solution is to use a converter in the binding expression, to convert from a color to a brush, another to return directly a brush from your viewmodel.

    http://blogs.microsoft.co.il/blogs/alex_golesh/archive/2008/04/29/colorconverter-in-silverlight-2.aspx

    • Matthias says:

      I use an brush:
      public class SalesPerformance
      {
      public Brush SalesColor { get; set; }
      public string SalesName { get; set; }
      public List SalesTotals { get; set; }
      }

      update-Method:
      List salesData = new List();
      SalesPerformance salesPerf = new SalesPerformance();
      salesPerf.SalesName = “Miller”;
      salesPerf.SalesColor = new SolidColorBrush(Colors.Black);
      salesPerf.SalesTotals = new List();
      salesPerf.SalesTotals.Add(new SalesInfo { Date = DateT
      ….

      XAML:
      local:SeriesTemplateSelector.SalesTemplate
      DataTemplate
      chartingToolkit:LineSeries
      Title=”{Binding SalesName}”
      ItemsSource=”{Binding SalesTotals}”
      IndependentValueBinding=”{Binding Date}”
      DependentValueBinding=”{Binding SalesTotal}”
      chartingToolkit:LineSeries.DataPointStyle
      Style TargetType=”chartingToolkit:DataPoint”
      Setter Property=”Background” Value=”{Binding SalesColor}”/
      /Style
      /chartingToolkit:LineSeries.DataPointStyle
      /chartingToolkit:LineSeries
      /DataTemplate
      /local:SeriesTemplateSelector.SalesTemplate

  8. Matthias says:

    … but i get still an System.Windows.Markup.XamlParseException
    Message=”AG_E_RUNTIME_MANAGED_UNKNOWN_ERROR [Line: 6 Position: 69]”
    LineNumber=6
    LinePosition=69
    StackTrace:
    bei MS.Internal.XcpImports.MethodEx(IntPtr ptr, String name, CValue[] cvData)
    bei MS.Internal.XcpImports.MethodEx(DependencyObject obj, String name)
    bei MS.Internal.XcpImports.DataTemplate_LoadContent(DataTemplate template)
    bei System.Windows.DataTemplate.LoadContent()
    bei MultiChartDemo.MultiChart.OnSeriesSourceChanged(IEnumerable oldValue, IEnumerable newValue)
    InnerException:

    Have you another idea?

    • beatkiener says:

      I’ve tested it with my demo project with the same result as you get.
      Now I’ve updated the demo project to Silverlight 4 to get better exception messages.

      The result: it seems the property cannot be databound to a value, because the exception informs me that the property is read-only after the first rendering.

      {System.Windows.Markup.XamlParseException: Set property ” threw an exception. [Line: 21 Position: 53] —> System.NotSupportedException: Cannot set read-only property ”.
      at MS.Internal.XamlMemberInfo.SetValue(Object target, Object value)
      at MS.Internal.XamlManagedRuntimeRPInvokes.SetValue(XamlTypeToken inType, XamlQualifiedObject& inObj, XamlPropertyToken inProperty, XamlQualifiedObject& inValue)
      — End of inner exception stack trace —
      at System.Windows.Application.LoadComponent(Object component, Uri resourceLocator)
      at MultiChartDemo.MainPage.InitializeComponent()
      at MultiChartDemo.MainPage..ctor()}

      But I can set the value in the style to a static resource like this.

      Style TargetType=”Control” x:Key=”pointStyle”
      works
      Setter Property=”Background” Value=”{StaticResource brush}”
      doesn’t work
      Setter Property=”Background” Value=”{Binding LineColor}”
      /Style

      I will check the source code the chart series this evening.

  9. Matthias says:

    Thank you for your time and your solution.

  10. Kayvan says:

    Dear beatkiener,

    It was more than FANTASTIC really. It come with a very technical solution.

    Thanks for sharing your bright idea.

  11. [...] along with a post (Databinding Multi-Series Charts) from Beat Kiener got be going quickly.  This framework just felt right.  I can’t put [...]

  12. Andreas says:

    Hi Beat,

    this is really a nice way to support a dynamic amount of different series types in one chart. But with the latest Silverlight changes that support stacked bars (…) it becomes a little bit harder. Can the data template be easily enhanced to include a them as well? The problem is that the hierarchy is enhanced with them. A StackedBarSeries includes several SeriesDefinitions. Thanks in advance for your inspiration ;-)

    Cheers
    Andreas

  13. Andreas says:

    Dear BeatKiener,
    This is really a nice way to bind a dynamic amount of different series types to one chart. But with silverlight 4 stacked bar (…) support it seems like there is a limit. The problem is that the hierarchy is extended by one level: A StackedBarSeries itself contains several (again 1-n) SeriesDefinitions. Can you imagine a way how a DataTemplate can be reflected to include StackedBar support? Thanks in advance ;-) and
    Best Regards
    Andreas

  14. [...] 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 [...]

  15. Greg says:

    Hey Beat,

    First of all, I’d like to thank you for this article, it is very useful, nice job.

    Would you be able to give me any guidance on which direction I’d need to go if I wanted to extend this further so that the graph were to update itself if the SeriesSource were an ObservableCollection and was updated?

    Best Regards,
    Greg

    • beatkiener says:

      Hi Greg,

      Thank you very much for your feedback.

      I’ve attached you a quick implementation of the collection changed event with the use of the weak-event pattern, in order that you can work with an observable collection.

      Please have a lock to the attached sample project:
      http://files.thekieners.com/blogcontent/2010/MultiChartDemo2.zip

      There is a new checkbox on top of the demo-view where you can set a flag in the view model in order that the source gets updated instead of creating the new on each timer tick. So you should be able to see how it works. Please note that only the charts on the left side changing the number of series-items or numbers of series.

      I hope this is helpfully for you and any further feedback is much appreciated.

      Best regards,
      Beat

  16. Greg says:

    Hey Beat,

    That helps a ton, I can’t thank you enough.

    For anybody following, Beat also has two great articles referenced in the sample project (in comments in MultiChart.cs) that explain the weak-event pattern used.

    http://blog.thekieners.com/2010/02/11/simple-weak-event-listener-for-silverlight/
    http://blog.thekieners.com/2010/02/17/weakeventsource-implementation-2/

    Thanks again,
    Greg

  17. Johnny says:

    Hi Beat,
    Great solution, have you managed to get selection changed working for any of your series?

  18. jar says:

    Hi,
    i use you trick to bind multiseries in WPF but i have very big problem. When datas are changing there is PropertyChange event fired but chart doesn;t change. Chart is drawing only on start program.
    Any ideas?

  19. Lawrence Pham says:

    Hi Beat,

    How do you add the title to each series on the Y-axis. I’ve tried this but the binding does not seem to work:

    Thank you very much.

    Lawrence

  20. william says:

    Hello beat, great job.

    Could you explain more about how can I introduce the multi-series chart control in my web-site?

    Thanks a lot.

    William

  21. NEX-C3 says:

    Ero stato anche guardando attraverso i singoli è sufficiente, come da un’altra pagina web.

  22. Christoph Neyen says:

    Hi beatkiener,
    i love your multichart and use it very often. Now i would like to have an dependentaxis set to show mulitple lineSeries. I have made a datatemplate with an axis. But for every linesSeries there will be one axis show. Is it possible to define a axis for all series? I also used the templateSelector, but then for every series except the first with axis, the default scaling is used and not my axis?
    Any idea?

    thanks in advance
    Christoph

  23. madben2000 says:

    When trying to use a custom LineDataPointStyle to get different line colors, the chart lines between the data point disappear, whereas the data points have the correct colors. Why are the chart line disappearing? Any suggestions? Tried also the above mentioned SetterValueBindingHelper, but same result. Here is an excerpt of the LineDataPoint style:

    <!—->

  24. twistur says:

    Hi Beatkiener,

    First of all, thank you so much. You saved us tonloads of work with your creation. This is exactly what we wanted. We basically have a feature that allows user to add/remove series on the fly and the add works perfect. The remove part though is very unpredictable and quirky. When I remove items from the bound source, I’m not guaranteed that the corresponding series in the MultiChart.Series collection is removed. Is this a known issue?

  25. […] first problem I had was binding multiple series to a chart. I found Beat Kiener’s excellent blogpost on binding multiple series to a chart which worked great until I put it in an items control, which I have to do to meet my “any […]

  26. Interesting blog! Is your theme custom made or did you download it from somewhere?
    A design like yours with a few simple adjustements would really make my blog shine.

    Please let me know where you got your design. Thank you

  27. Truyen says:

    Great Post. Thank you.
    Any idea how to change the position of the legend to the bottom instead of the right side?

  28. Truyen says:

    For those who looking for the same thing like I do, here is the style to move the legend to the bottom

  29. Truyen says:

    Won’t allow me to post code???

  30. click me says:

    Good day I am so grateful I found your weblog, I really found you by error,
    while I was searching on Aol for something else, Regardless I
    am here now and would just like to say many thanks for a marvelous post and a
    all round entertaining blog (I also love the theme/design), I don’t have
    time to go through it all at the minute but I have book-marked it and also included your RSS feeds,
    so when I have time I will be back to read a great deal more,
    Please do keep up the great job.

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

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: