This is a migrated thread and some comments may be shown as answers.

Multiple Series Types with ChartSeriesProvider and TypePath

18 Answers 695 Views
ChartView
This is a migrated thread and some comments may be shown as answers.
Louis
Top achievements
Rank 1
Louis asked on 05 Nov 2013, 05:16 PM
I am trying to chart a variable number of series, any of which can either be displayed as point series (using bar series) or line series. I've managed to get TypePath to work to successfully vary the series type dynamically. However, I have 2 questions:

1. How can I apply Styles to the two different types of series? CategoricalSeriesDescriptor only takes a single Style, and that Style only accepts Properties of the specified TargetType; I can't figure out how to set styles for each series type.

2. (How) can I use a converter to supply the ChartView with the Series Type so that the ViewModel in MVVM doesn't have to supply telerik types to the View? TypePath doesn't allow a binding (I get "System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element." if I try) so I can't apply the "Converter" tag.

If there's a better way to display variable numbers of series using 2 different series types please do point it out as well.

Thanks,
Louis

18 Answers, 1 is accepted

Sort by
0
Yoni
Top achievements
Rank 1
answered on 07 Nov 2013, 05:49 AM
Hello.

I have the same issue.

Please advice.
Yoni
0
Louis
Top achievements
Rank 1
answered on 07 Nov 2013, 04:39 PM
Hi Yoni,

While waiting for an official response, I did manage to figure out how to apply the styles to different Series types automatically; Define them as Resources of the Chart with the TargetType set (but without IDs), and they will automatically apply based on the TypePath. This won't work if you want different styles for the same Series type, but it works for what I need.

I also found another potential problem in doing this (if you're doing it the way I tried to, with 2 different types underlying the different Series); the objects that supply the ItemsSource must all be of the same actual type. You cannot have different types with a common base class or that support a common interface, as the Grid seems to use reflection to create its own internal storage of the list, and types it based on the actual type of object you pass to it in the first Series (regardless of what type your Collection exposes). I handled this by using an adapter class that can store either type of object, and presents the relevant fields (including graph type) through its own properties. I did all this in the code-behind (which is quite ugly) so it's part of the View, as it really is view-implementation specific.

I'd still like to hear if there's a way to adapt the graph type in XAML or in a better way that doesn't lead to such a big layer in the View itself.

Hope this helps,
Louis

P.S. Here's a simplified version of what I ended up with, for your reference:

                <telerik:RadCartesianChart ...>
                    <telerik:RadCartesianChart.Resources>
                        <Style TargetType="telerik:LineSeries">
                            <Setter Property="StrokeThickness" Value="2"/>
                            <Setter Property="CategoryBinding" Value="{StaticResource timeBinding}"/>
                            <Setter Property="ValueBinding" Value="{StaticResource rateBinding}"/>
                        </Style>
                        <Style TargetType="telerik:BarSeries">
                            <Setter Property="CategoryBinding" Value="{StaticResource timeBinding}" />
                            <Setter Property="ValueBinding" Value="{StaticResource rateBinding}" />
                            <Setter Property="PointTemplate" Value="{StaticResource HistoricalPointDataTemplate}"/>
                        </Style>
                    </telerik:RadCartesianChart.Resources>
...
                    <telerik:RadCartesianChart.SeriesProvider>
                        <telerik:ChartSeriesProvider x:Name="ChartProvider" Source="{Binding Path=GraphSeries,RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"> <!-- this is to point to the code-behind layer instead of the VM -->
                            <telerik:ChartSeriesProvider.SeriesDescriptors>
                                <telerik:CategoricalSeriesDescriptor ItemsSourcePath="Data" TypePath="SeriesType" />
                            </telerik:ChartSeriesProvider.SeriesDescriptors>
                        </telerik:ChartSeriesProvider>
                    </telerik:RadCartesianChart.SeriesProvider>
 
                </telerik:RadCartesianChart>
0
Petar Marchev
Telerik team
answered on 08 Nov 2013, 04:21 PM
Hi,

Louis, I think that the answer to your original question is SeriesDescriptorSelector. You can implement your own MyChartSeriesDescriptorSelector where you have set up the series descriptors and return the one you desire in the SelectDescriptor override.

As far as the solution you have found - great job! You are correct about the limitation - if you need two different bar series style - this work-around will not fulfill the requirements.

I am unsure what you mean by "You cannot have different types with a common base class". It is possible to do that but the code is optimized for scenarios where all items are of the same type. Pehaps you can use the GenericDataPointBinding instead of the PropertyNameDataPoingBinding which is more flexible and does not require such an optimization (due to its nature), and is actually faster anyway.

I have attached a small project to demonstrate that the chart renders correctly two bar series that share the same property name data point bindings and use two different ItemsSources as you have described. If you experience any issues I will ask that you provide more information on the matter.

Regards,
Petar Marchev
Telerik
TRY TELERIK'S NEWEST PRODUCT - EQATEC APPLICATION ANALYTICS for WPF.
Learn what features your users use (or don't use) in your application. Know your audience. Target it better. Develop wisely.
Sign up for Free application insights >>
0
Louis
Top achievements
Rank 1
answered on 08 Nov 2013, 05:37 PM
Thanks for the reply Petar, but looking at the example you attached, you are varying the items within the ItemsSource of specific Series definitions. I was referring to having different items in the Source collection of the ChartSeriesProvider. In my case, I have 2 different types of data to chart (one as points and the other as lines), but I have a variable number of each of these. Using SeriesProvider, I wasn't able to supply 2 different classes (one for the points, one for the lines) that either derived from a common base class or that supported a common interface (reflected in the ObservableCollection<BaseClass>) to the ItemsSource. Upon trying to add an instance of the second type of series, an exception was thrown by Telerik code from what I recall looked like an underlying generic collection class that had the actual class type of the first type of series, rather than the BaseClass.

What would be very useful in my case would be to be able to specify two different ChartSeriesProviders in a single chart; correct me if I'm wrong, but I don't think this is possible. If I could do this, it would use two different Sources and not suffer from the problem.

The biggest outstanding question I have is #2 from my original post; I'd like to be able to use a converter for the TypePath as it's not reasonable to expose Telerik series types from a ViewModel.

Thanks again,
Louis
0
Petar Marchev
Telerik team
answered on 12 Nov 2013, 10:47 AM
Hi Louis,

It is true that the current implementation of the chart does not support two series providers, unless you toggle two different providers in your app. And it is also true that it is not possible to have a converter for the TypePath property as this property specifies the name of the property from which the series type is expected. I have forwarded this to our developers and they will try and find a way to remove this limitation.

I am still unsure what is the scenario that you describe so I will ask that you send us a small project that demonstrates this so that we can investigate.

Regards,
Petar Marchev
Telerik
TRY TELERIK'S NEWEST PRODUCT - EQATEC APPLICATION ANALYTICS for WPF.
Learn what features your users use (or don't use) in your application. Know your audience. Target it better. Develop wisely.
Sign up for Free application insights >>
0
Louis
Top achievements
Rank 1
answered on 12 Nov 2013, 08:18 PM
Hi Petar, thanks for your reply, and for passing my issue on to the developers. When you say "unless you toggle two different providers in your app", you can't do that and have both sets show on the graph at the same time, can you?

I've come up with minimal example of my issues for your reference:

MainWindow.xaml:
<Window x:Class="TwoDynamic.MainWindow"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <ResourceDictionary>
            <DataTemplate x:Key="PointDataTemplate">
                <Ellipse Width="4" Height="4" Fill="Red"/>
            </DataTemplate>
        </ResourceDictionary>
    </Window.Resources>
    <Grid>
        <telerik:RadCartesianChart>
            <telerik:RadCartesianChart.Resources>
                <Style TargetType="telerik:LineSeries">
                    <Setter Property="StrokeThickness" Value="2"/>
                    <Setter Property="Stroke" Value="Blue" />
                </Style>
                <Style TargetType="telerik:BarSeries">
                    <Setter Property="PointTemplate" Value="{StaticResource PointDataTemplate}" />
                    <Setter Property="CombineMode" Value="None"/>
                </Style>
            </telerik:RadCartesianChart.Resources>
            <telerik:RadCartesianChart.Grid>
                <telerik:CartesianChartGrid MajorLinesVisibility="XY" />
            </telerik:RadCartesianChart.Grid>
            <telerik:RadCartesianChart.HorizontalAxis>
                <telerik:DateTimeContinuousAxis LabelFitMode="Rotate" LabelFormat="MMM-dd" />
            </telerik:RadCartesianChart.HorizontalAxis>
            <telerik:RadCartesianChart.VerticalAxis>
                <telerik:LinearAxis/>
            </telerik:RadCartesianChart.VerticalAxis>
            <telerik:RadCartesianChart.SeriesProvider>
                <telerik:ChartSeriesProvider Source="{Binding Path=Series}">
                    <telerik:ChartSeriesProvider.SeriesDescriptors>
                        <telerik:CategoricalSeriesDescriptor ItemsSourcePath="Data" TypePath="SeriesType" CategoryPath="Date" ValuePath="Value"/>
                    </telerik:ChartSeriesProvider.SeriesDescriptors>
                </telerik:ChartSeriesProvider>
            </telerik:RadCartesianChart.SeriesProvider>
        </telerik:RadCartesianChart>
    </Grid>
</Window>

MainWindow.xaml.cs:
using System.Windows;
 
namespace TwoDynamic
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            DataContext = new ViewModel();
        }
    }
}

And ViewModel.cs:
using System;
using System.Collections.ObjectModel;
using Telerik.Windows.Controls.ChartView; // Should not have this in VM layer!
 
namespace TwoDynamic
{
    public class DataPoint
    {
        public DateTime Date { get; set; }
        public double Value {get;set;}
    }
    public abstract class SeriesBase
    {
        public abstract ObservableCollection<DataPoint> Data { get; set; }
        // This DOES NOT BELONG in the ViewModel! TypePath really needs
        // a converter so this can provide a generic type, and have the
        // Telerik-specific type supplied in the View layer.
        public abstract  Type SeriesType { get; }
    }
    public class PointData: SeriesBase
    {
        public override Type SeriesType { get { return typeof(BarSeries); } }
        public override ObservableCollection<DataPoint> Data { get; set; }
    }
    public class LineData : SeriesBase
    {
        public override Type SeriesType { get { return typeof(LineSeries); } }
        public override ObservableCollection<DataPoint> Data { get; set; }
    }
    public class ViewModel
    {
        private Random _Random;
        private ObservableCollection<SeriesBase> _Series;
        public ObservableCollection<SeriesBase> Series { get { return _Series; } }
        public ViewModel()
        {
            _Random = new Random();
            _Series = new ObservableCollection<SeriesBase>();
            // In reality there would be a variable number of these.
            _Series.Add(new PointData() { Data = GenerateSomeData() });
            _Series.Add(new PointData() { Data = GenerateSomeData() });
            // I can either add PointData items, or LineData items, but not both
            //_Series.Add(new LineData() { Data = GenerateSomeData() });
            //_Series.Add(new LineData() { Data = GenerateSomeData() });
        }
        private ObservableCollection<DataPoint> GenerateSomeData()
        {
            ObservableCollection<DataPoint> newCollection = new ObservableCollection<DataPoint>();
            for (int i = 0; i < 10; i++)
                newCollection.Add(new DataPoint() { Date = new DateTime(2012, 12, 31).AddDays(i), Value = _Random.Next(100) });
            return newCollection;
        }
    }
}

My problem #1 is that in ViewModel, I can add any number of EITHER LineData OR PointData items to the list and it all works fine. However, even though they're both SeriesBase types which is what is exposed in the collection, if I add both it throws the exception:
System.InvalidCastException was unhandled
  HResult=-2147467262
  Message=Unable to cast object of type 'TwoDynamic.LineData' to type 'TwoDynamic.PointData'.
  Source=Anonymously Hosted DynamicMethods Assembly
  StackTrace:
       at Telerik_DynamicGetter_SeriesType(Object )
       at Telerik.Windows.Controls.ChartView.ChartSeriesDescriptor.ResolveType(Object context) in c:\TB\221\WPF_Scrum\Release_WPF\Sources\Development\Controls\Chart\Visualization\DataBinding\DynamicSeries\SeriesDescriptors\ChartSeriesDescriptor.cs:line 178

My real LineData and PointData classes are of course much more complicated, and have lots of distinct functionality relevant to the actual data that is being displayed. I have worked around this by creating an adapter class that can hold either of these types of objects, and whose properties return the contained object's values, but this is a pretty ugly hack.

The second issue I think you already understand, and that is that there should be no references to Telerik types in the ViewModel, but there's no (clean) way to use the TypePath setting without doing so (thus the need for the converter).

Does that clear things up?

Thanks,
Louis
0
Accepted
Petar Marchev
Telerik team
answered on 13 Nov 2013, 11:58 AM
Hi Louis,

Thank you for the attached code snippets, I was able to create a new project based on it and reproduced the exception in mind. I have logged this for investigation and you can find it in our system and I have also updated your Telerik points as a sign of gratitude.

As a work-around I can suggest you slightly modify the SeriesBase class and its inheritors. I have attached a project to demonstrate the change in mind and it is essentially this:
public class SeriesBase
{
 public ObservableCollection<DataPoint> Data { get; set; }
 public Type SeriesType { get { return this.SeriesTypeOverride; } }
 public virtual Type SeriesTypeOverride { get { return null; } }
}
 
public class PointData : SeriesBase
{
 public override Type SeriesTypeOverride { get { return typeof(BarSeries); } }
}

I apologize for not being clear enough in my previous post - the SeriesProvider is a property of the chart and you can have only one provider per chart at a time.

I will again suggest that you go with the descriptor selector as I think this is what will solve all the issues you are experiencing.

"I have worked around this by creating an adapter class .... but this is a pretty ugly hack."
I think that this is a great solution as it would allow you to have the view models unchanged and Telerik agnostic, while this adapter class can wrap the original view model and have Telerik's controls cognition.

I fully agree that there is need of a converter and we have plans to enhance the series provider in this direction.

Regards,
Petar Marchev
Telerik
TRY TELERIK'S NEWEST PRODUCT - EQATEC APPLICATION ANALYTICS for WPF.
Learn what features your users use (or don't use) in your application. Know your audience. Target it better. Develop wisely.
Sign up for Free application insights >>
0
Louis
Top achievements
Rank 1
answered on 13 Nov 2013, 04:31 PM
Thanks Petar for the clarifications and the example... I would never have guessed that it was the abstract nature of the base class causing problems. Note that using a common interface also suffers from the same problem (not surprisingly, since it is similar to an abstract class, but something that your developers might consider when crafting a solution to the abstract problem).

I agree with you that the SeriesDescriptorSelector would work great, although for my purposes just the un-ID'd TargetType'd styles work fine since I don't need multiple styles for the same TargetType.

Thank you for all the help. I think we're good (with the issues in this thread at least) pending the future Abstract base class and converter enhancements.

Louis
0
Frederic
Top achievements
Rank 1
answered on 27 Aug 2015, 03:08 PM

Hi Petar,

I made a solution based upon your latest post with sample project 755202.2.zip where the charts gets generated dynamically.

At this point the solution works.

Although I was able to bind the color with mvvm to a property of the model for a lineserie, I was not able to get it up and running for a bar serie.

Below you can find the styling code.

 

<telerik:RadCartesianChart x:Name="Seriesgraph" HoverMode="FadeOtherSeries" Background="WhiteSmoke"  Grid.Column="2" Margin="12.8,10,10,10" Grid.Row="5" Grid.ColumnSpan="5" Grid.RowSpan="3">
<telerik:RadCartesianChart.Resources>
              <Style TargetType="telerik:LineSeries"  BasedOn="{StaticResource lineSeriesStyle}">
                    <Setter Property="StrokeThickness" Value="{Binding Dikte}"/>
                    <Setter Property="behavior:ChartUtilitiesAttachedProperty.SeriesLegendSettings" Value="{Binding PlcVariableName}" />
                    <Setter Property="Stroke" Value="{Binding Color}" />
                    <Setter Property="ShowLabels" Value="True"/>
                    <Setter Property="DashArray"  Value="0.1"/>
                </Style>
                <Style TargetType="telerik:BarSeries"  BasedOn="{StaticResource BarSeriesStyle}">
                    <Setter Property="CombineMode" Value="Stack"/>
                    <Setter Property="behavior:ChartUtilitiesAttachedProperty.SeriesLegendSettings" Value="{Binding PlcVariableName}" />
                    <Setter Property="ShowLabels" Value="True"/>
                    <Setter Property="PointTemplate">
                        <Setter.Value>
                            <DataTemplate>
                                <Rectangle Fill="{Binding Color}" />
                            </DataTemplate>
                        </Setter.Value>
                    </Setter>
                </Style>
            </telerik:RadCartesianChart.Resources>

 

Can you guide me towards a solution, many thanks in advance.

Kind Regards

Frederic

 

 

 

 

 

 

 

 

 

 

 

0
Louis
Top achievements
Rank 1
answered on 27 Aug 2015, 04:03 PM

Hi Frederic,

Are you getting any binding errors in the output? My implementation is a bit different, but I had to use Fill="{Binding Path=DataItem.Color}" to get to the Color property on my VMs.

Louis
0
Frederic
Top achievements
Rank 1
answered on 27 Aug 2015, 07:33 PM

Hi Louis,

I was able to set the color of barseries as defined in the viewmodel. Thank you for sharing this information.

Since I am doing more or less the same as your solution, perhaps you can help with an additional topic?

I'm able to display a legend for all chart types except for the bar series.

I found a  post  here  that reveals that we cannot using a pointtemplate and legend item together:

When you use a PointTemplate, the chart does not know how you color the visuals and it does not know how to chose a color for the legend item. I think you should try using the DefaultVisualStyle instead of the PointTemplate for the BarSeries. This way the legend item will get its color automatically.   
Petar Marchev
Telerik

Where you able to get it up and running in your solution? Any ideas in resolving this?

Many thanks in advance

Frederic

0
Louis
Top achievements
Rank 1
answered on 27 Aug 2015, 07:41 PM

Sorry Frederic, I don't have any experience with using legends on the Telerik ChartView; we create our own legend outside the chart, which might be an option for you as well.

Louis

0
Frederic
Top achievements
Rank 1
answered on 27 Aug 2015, 07:54 PM

Thanks for the suggestion, a solid alternative.

Kind regards

Frederic

0
Petar Marchev
Telerik team
answered on 28 Aug 2015, 07:52 AM
Hello Frederic,

You should be able to use the DefaultVisualStyle property instead of the PointTemplate for the bar series. It should be a style targeting a Border element, and the DataContext of this Border is the DataPoint, so you can use a setter for the Background of the Border.

Regards,
Petar Marchev
Telerik
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Feedback Portal and vote to affect the priority of the items
0
Frederic
Top achievements
Rank 1
answered on 28 Aug 2015, 08:56 AM

Hi Petar,

Thank you for the suggestions.

We have implemented the suggestions and now we have legenditems with corresponding color for the bar series aswell.

Kind regards

Frederic

0
Sivakumar
Top achievements
Rank 1
answered on 06 Apr 2016, 08:04 AM

Hi Petar,

I am doing exactly same as this post(/multiple-series-types-with-chartseriesprovider-and-typepath) with one change and I am using RangeBarSeries Type instead of BarSeries.But i am getting compile error as below.Please advice the right approach to add mixed series.

Additional information: Unable to cast object of type 'Telerik.Windows.Controls.ChartView.RangeBarSeries' to type 'Telerik.Windows.Controls.ChartView.CategoricalSeries'.

0
Petar Marchev
Telerik team
answered on 06 Apr 2016, 09:48 AM
Hello Sivakumar,

I will ask you to open a new thread because we prefer to keep different questions separated instead of cluttering a single forum post. Thank you for understanding.

Regards,
Petar Marchev
Telerik
Do you need help with upgrading your AJAX, WPF or WinForms project? Check the Telerik API Analyzer and share your thoughts.
0
Johannes
Top achievements
Rank 1
answered on 10 Feb 2017, 11:42 AM
You can not imagine how long I was searching for this solution. Thank you very much!
Tags
ChartView
Asked by
Louis
Top achievements
Rank 1
Answers by
Yoni
Top achievements
Rank 1
Louis
Top achievements
Rank 1
Petar Marchev
Telerik team
Frederic
Top achievements
Rank 1
Sivakumar
Top achievements
Rank 1
Johannes
Top achievements
Rank 1
Share this question
or