ChartSeriesProvider with CompositeCollection?

6 posts, 1 answers
  1. Louis
    Louis avatar
    83 posts
    Member since:
    Aug 2013

    Posted 06 Nov 2014 Link to this post

    Is it possible to use a ChartSeriesProvider with a CompositeCollection as the Source? Just specifying one as a StaticResource (which works fine for something like a ListBox that uses ItemsSource) results in an exception:
        Could not find property 'SeriesType' for type 'System.Windows.Data.CollectionContainer'

    Example source of what I'm trying to do (modified version of the example Petar provided in this thread:

    <Window x:Class="TwoDynamic.MainWindow"
            Title="MainWindow" Height="350" Width="525">
        <Window.Resources>
            <CollectionViewSource x:Key="FirstList" Source="{Binding Path=Series1}"/>
            <CollectionViewSource x:Key="SecondList" Source="{Binding Path=Series2}"/>
            <CompositeCollection x:Key="NewList">
                <CollectionContainer Collection="{Binding Source={StaticResource FirstList}}" />
                <CollectionContainer Collection="{Binding Source={StaticResource SecondList}}" />
            </CompositeCollection>
        </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="CombineMode" Value="None"/>
                    </Style>
                </telerik:RadCartesianChart.Resources>
                <telerik:RadCartesianChart.VerticalAxis>
                    <telerik:LinearAxis/>
                </telerik:RadCartesianChart.VerticalAxis>
                <telerik:RadCartesianChart.HorizontalAxis>
                    <telerik:DateTimeContinuousAxis LabelFitMode="Rotate" LabelFormat="MMM-dd" />
                </telerik:RadCartesianChart.HorizontalAxis>
                <telerik:RadCartesianChart.SeriesProvider>
                    <telerik:ChartSeriesProvider Source="{StaticResource NewList}">
                        <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>

    And the ViewModel:
    namespace TwoDynamic
    {
        public class DataPoint
        {
            public DateTime Date { get; set; }
            public double Value { get; set; }
        }
        public class SeriesBase
        {
            public ObservableCollection<DataPoint> Data { get; set; }
            public Type SeriesType { get { return SeriesTypeOverride; } }
            public virtual Type SeriesTypeOverride { get { return null; } }
        }
        public class PointData : SeriesBase
        {
            public override Type SeriesTypeOverride { get { return typeof(BarSeries); } }
        }
        public class LineData : SeriesBase
        {
            public override Type SeriesTypeOverride { get { return typeof(LineSeries); } }
        }
        public class ViewModel
        {
            private Random _Random = new Random();
            public ObservableCollection<SeriesBase> Series1 { get; private set; }
            public ObservableCollection<SeriesBase> Series2 { get; private set; }
            public ViewModel()
            {
                Series1 = new ObservableCollection<SeriesBase>();
                Series1.Add(new PointData() { Data = GenerateSomeData() });
                Series1.Add(new PointData() { Data = GenerateSomeData() });
                Series2 = new ObservableCollection<SeriesBase>();
                Series2.Add(new LineData() { Data = GenerateSomeData() });
                Series2.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;
            }
        }
    }
  2. Petar Marchev
    Admin
    Petar Marchev avatar
    968 posts

    Posted 10 Nov 2014 Link to this post

    Hi Louis,

    Let me try to explain quickly how the chart series provider works.

    The series provider is the mechanism that creates series for you. You pass to it a Source, which contains information about each series you need to be created. Now, say you have 5 items in the Source, so you want 5 series to be created. Now, the provider knows that you want 5 series, but does not know the type of the series and does not know how to do the data binding.

    You have used a categorical descriptor, and you have set the ItemsSourcePath to Data. Now, the series provider expects that each of the 5 items you passed has a Data property from which the ItemsSource will be extracted. You have also set the TypePath to be SeriesType, so the provider also expects that each of the 5 items will have a SeriesType property.

    You are trying to feed the series provider with a CompositeCollection. So far, so good. You have added two CollectionContainer items to it. Now, the collection container object does not hate neither a Data property, nor SeriesType property. This is why an exceptions is thrown.

    I hope I was able to explain well what is going on and why.

    A summary - yes, you can use a CompositeCollection, to feed the series provider, as long as the contained elements have the necessary information in order for a series to be created. If you have CompositeCollection with SeriesBase items in it - all should work fine.

    Let us know if you need more information.

    Regards,
    Petar Marchev
    Telerik
     

    Check out the Telerik Platform - the only platform that combines a rich set of UI tools with powerful cloud services to develop web, hybrid and native mobile apps.

     
  3. UI for WPF is Visual Studio 2017 Ready
  4. Louis
    Louis avatar
    83 posts
    Member since:
    Aug 2013

    Posted 10 Nov 2014 in reply to Petar Marchev Link to this post

    Hi Petar,

    Thank you for your reply. However, I'm afraid I still don't understand all the dynamics of what's happening.

    If we replace the Telerik:RadCartesianChart with a simple ListBox attached to the same CompositeCollection whose ItemTemplate depends on the same property (say SeriesType), it successfully picks that Property up from the items contained in the sublists:

    <ListBox ItemsSource="{StaticResource NewList}">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <TextBlock Text="{Binding SeriesType}"/>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>

    The result is as expected (showing 2 instances of BarSeries and 2 of LineSeries). I don't understand why a ListBox is correctly picking up the properties from the contained items but the ChartSeriesProvider doesn't.

    Also, I don't know how to practially apply your comment: "If you have CompositeCollection with SeriesBase items in it - all should work fine". Changing the 2 lists to containe SeriesBase rather than the derived types doesn't solve the issue. Is there a way I can create a CompositeCollection that contains all the elements from the 2 lists in a way that can be consumed by the Chart? (Obviously I can't add the items individually to the CompositeCollection, as they're not statically known, and doing so in code-behind defeats the purpose of the CompositeCollection.)

    Thanks again for your help,

    Louis
  5. Answer
    Petar Marchev
    Admin
    Petar Marchev avatar
    968 posts

    Posted 11 Nov 2014 Link to this post

    Hi Louis,

    I understand that this is not the most intuitive result - ListBox works, but chart doesn't. I will try to explain what is going on.

    When building the CompositeCollection, you are essentially concatenating two items sources - FirstList and SecondList. Say that the FirstList has 3 items, and the SecondList has 4 items (the composite collection has 2 items). The list box shows, surprisingly, 7 items - no hierarchy, flat result. Even though you passed an items source with 2 items - it shows seven items. The list box has special handling logic for CompositeCollection and CollectionContainer, I guess.

    Try this code in the Click handler of a button:
    CompositeCollection newList = (CompositeCollection)this.Resources["NewList"];
    foreach (CollectionContainer cContainer in newList)
    {
    }


    The chart series provider does not have special handling logic for the composite collection. To the provider, the composite collection is a regular IEnumerable and when foreach-ing it, it gets the cContainer object, of type CollectionContainer, and this object does not have a SeriesType property.

    If the chart would show something here - it is not going to be 7 series (list box variant), it would show 2 series, one for each cContainer object.

    I hope that this covers everything.

    What I was trying to say with "If you have CompositeCollection with SeriesBase items in it - all should work fine" is that if you were to add 7 SeriesBase objects (or its inheritors) to the composite collection, it should work just fine. It should work because now when foreach-ing the composite collection, the chart gets the PointData or LineData object and can find the SeriesType property.

    I think that you should create a new property in your view model SeriesProviderSource, which contains the SeriesBase objects you need the chart to work with. Let us know if you need more information.

    Regards,
    Petar Marchev
    Telerik
     

    Check out the Telerik Platform - the only platform that combines a rich set of UI tools with powerful cloud services to develop web, hybrid and native mobile apps.

     
  6. Louis
    Louis avatar
    83 posts
    Member since:
    Aug 2013

    Posted 11 Nov 2014 in reply to Petar Marchev Link to this post

    Thanks Petar, I guess the piece I was missing was:

    Petar Marchev said:The list box has special handling logic for CompositeCollection and CollectionContainer, I guess.


    I did end up creating my own composite collection of SeriesBase objects in the ViewModel.

    It would solve many problems to be able to have multiple SeriesProviders to feed data of different types to a Chart. I've submitted a Feature Request for this.

    Thanks again for your explanation,
    Louis
  7. Petar Marchev
    Admin
    Petar Marchev avatar
    968 posts

    Posted 14 Nov 2014 Link to this post

    Hello Louis,

    Unfortunately, I will have to decline the feature request. By design, the series provider should be the single entity that serves as series creator. The series provider offers more than one way of selecting a series type, one of which is the Style property of the descriptor, another is the TypePath property, and another is the SeriesDescriptorSelector. 

    I actually think that it is very convenient to have a collection of SeriesBase objects, instead of nested collections. Let us know if you have any other questions.

    Regards,
    Petar Marchev
    Telerik
     

    Check out the Telerik Platform - the only platform that combines a rich set of UI tools with powerful cloud services to develop web, hybrid and native mobile apps.

     
Back to Top
UI for WPF is Visual Studio 2017 Ready