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.
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?
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.
[…] behavior with native UIElement MouseWheel eventMemory leak with focusable UIElement in Silverlight?Databinding Multi-Series ChartsBe careful when using WeakReference.IsAliveBasicHttpBinaryBinding for […]
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 ?
Hi,
the Silverlight Toolkit Team unsealed them in the October 2009 Release.
Check complete change list here: http://go.microsoft.com/fwlink/?LinkId=165469
Please download and use the latest Silverlight Toolkit from http://www.codeplex.com/Silverlight
Best regards,
Beat
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.
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
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
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).
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
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
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
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
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
… 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?
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.
After playing around with this problem since the last 2 hours things become clear to me.
Silverlight 3 (and 4) does not support bindings on style setters.
If you declare a style in the usercontrol resourse section with a setter and a binding, it will bring your application down with a XamlParseException.
I’ve found a blog post from David Anson (Microsoft developer working on the Silverlight plat-form) in which he describes this limitation and a possible workaround.
http://blogs.msdn.com/b/delay/archive/2009/05/07/one-more-platform-difference-more-or-less-tamed-settervaluebindinghelper-makes-silverlight-setters-better.aspx
I’ve been trying it and it works very well.
Try this:
Thank you for your time and your solution.
You are welcome
Dear beatkiener,
It was more than FANTASTIC really. It come with a very technical solution.
Thanks for sharing your bright idea.
[…] along with a post (Databinding Multi-Series Charts) from Beat Kiener got be going quickly. This framework just felt right. I can’t put […]
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
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
[…] 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 […]
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
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
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
Hi Beat,
Great solution, have you managed to get selection changed working for any of your series?
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?
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
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
Ero stato anche guardando attraverso i singoli è sufficiente, come da un’altra pagina web.
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
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:
<!—->
…
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?
[…] 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 […]
Chart .NET control data binding
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
Great Post. Thank you.
Any idea how to change the position of the legend to the bottom instead of the right side?
For those who looking for the same thing like I do, here is the style to move the legend to the bottom
Won’t allow me to post code???
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.
The url for thekieners doesnt seem to be up anymore. Is there anywhere else to download the example code? I am mainly interested in the second version with the observablecollection
Ryan, I search the code too. Have you find it ?
unfortunately I lost the domain. Therefore the blog runs via bkiener.wordpress.com and the file domain changed to rnd.glauxsoft.ch.
Please use http://rnd.glauxsoft.ch/blogcontent/2010/MultiChartDemo2.zip
instead of http://files.thekieners.com/blogcontent/2010/MultiChartDemo2.zip
Hi,
Thanks for this wonderful blog and explanations. This is really helpful. However I am stuck into one trouble and wanted to reach out to you to seek for your help.
I want to create a single chart which has two line series which are populated via MVVM binding both are coming fine. However both has different Y-axis value range.
1st line chart maximum is only till 100 whereas 2nd line chart maximum goes to 10000. Now due to this when actual graph is displayed, i cant event see 1st line since its almost touching to bottom axis.
Is there any way to have different axis for each series within chart. So that both graph can even different scale so it becomes easy to relate data.