Automatically genereted series in a chart from a flexible data source

8 posts, 0 answers
  1. Morten Nilsen
    Morten Nilsen avatar
    32 posts
    Member since:
    Jun 2010

    Posted 13 Jan 2011 Link to this post

    Hello!

    I can't find any obvious documentation about my specific use case, so please help me along here:

    I have a class such as this:

        public class ProductPrognosis
        {
            public string PartNumber { get; set; }
            public Dictionary<DateTime, int> Prognosis { get; set; }
        }

    This class gets filled with data extracted from an Excel document.
    I then have a list of these objects that I use as my ItemsSource on a chart control:

    <telerik:RadChart  ItemsSource="{Binding Prognoses}" Grid.Row="1" Margin="4,0" Background="#66ffffff" />

    My problem is, I cannot find a way to tell RadChart how to get at the series in the collection..
    I would have expected some properties, such as SeriesNameProperty and SeriesDataProperty that when defined made RadChart auto-create series based on the set of data presented..

    Looking at the samples you have published, I only see manual definition of series using quite verbose XML. And I fear that is not an acceptable solution. WPF UIs are meant to be data driven, not tersely defined in XAML.

    I hope someone can solve this with a simple solution, I would hate to have to create a whole suite of magical tools that generate irrelevant objects (to me) on the fly..

    Regards, Morten
  2. Bartholomeo Rocca
    Bartholomeo Rocca avatar
    247 posts
    Member since:
    May 2006

    Posted 14 Jan 2011 Link to this post

    Hello Morten,

    You can check the chart databinding documentation here that explains how to use the SeriesMapping / ItemMappings concepts in order to enable the control to generate the correct series automatically.


    Greetings,
    Bart.
  3. UI for WPF is Visual Studio 2017 Ready
  4. Morten Nilsen
    Morten Nilsen avatar
    32 posts
    Member since:
    Jun 2010

    Posted 14 Jan 2011 Link to this post

    Yes, I have seen those pages, but they don't really address the issue in hand..
    I can't go and define these things in XAML as the series are complete unkowns.

    If there isn't any better options, here are the solutions to the case I can see:

    1) Create the SeriesMapping objects in the ViewModel and bind SeriesMappings to it.

    2) Build a markup extension class that when given a few parameters generate the objects and bind to that.

    Option 1 really isn't an option at all, as that completely destroys the MVVM pattern by putting a very specific view component into the viewmodel.
    Option 2 will take a considerable amount of time to implement for me, as I'm not an expert at that particular field of binding (yet).

    What I believe we need is something like the following:

    <Grid x:Name="LayoutRoot" Background="White">
       <telerik:RadChart x:Name="radChart" ItemsSource="{Binding Tickers}">
           <telerik:RadChart.DefaultView>
               <telerik:ChartDefaultView>
                   <telerik:ChartDefaultView.ChartTitle>
                       <telerik:ChartTitle Content="Trade Data" />
                   </telerik:ChartDefaultView.ChartTitle>
               </telerik:ChartDefaultView>
           </telerik:RadChart.DefaultView>
           <telerik:RadChart.SeriesMappings>
               <telerik:AutomaticSeriesMapping LegendLabelBinding="{Binding Ticker}">
                   <telerik:AutomaticSeriesMapping.SeriesDefinition>
                       <telerik:CandleStickSeriesDefinition />
                   </telerik:AutomaticSeriesMapping.SeriesDefinition>
                   <telerik:AutomaticSeriesMapping.ItemMappings>
                       <telerik:ItemMapping DataPointMember="Open" FieldName="Open" />
                       <telerik:ItemMapping DataPointMember="High" FieldName="High" />
                       <telerik:ItemMapping DataPointMember="Low" FieldName="Low" />
                       <telerik:ItemMapping DataPointMember="Close" FieldName="Close" />
                   </telerik:AutomaticSeriesMapping.ItemMappings>
               </telerik:AutomaticSeriesMapping>
           </telerik:RadChart.SeriesMappings>
       </telerik:RadChart>
    </Grid>

    This is a modified snippet from the above sited web page.

    Regards, Morten
  5. Morten Nilsen
    Morten Nilsen avatar
    32 posts
    Member since:
    Jun 2010

    Posted 14 Jan 2011 Link to this post

    A small update on this:

    I've been poking random things through the use of XAML intellisense and google (isn't there some useful documentation of what properties exist on what objects and what they mean?) and ended up with this:

    <telerik:SeriesMapping CollectionIndex="0" LegendLabel="test">
        <telerik:SeriesMapping.SeriesDefinition>
            <telerik:LineSeriesDefinition />
        </telerik:SeriesMapping.SeriesDefinition>
        <telerik:SeriesMapping.ItemMappings>
            <telerik:ItemMapping FieldName="Key" FieldType="{x:Type sys:DateTime}" DataPointMember="XValue" />
            <telerik:ItemMapping FieldName="Value" FieldType="{x:Type sys:Int32}" DataPointMember="YValue" />
        </telerik:SeriesMapping.ItemMappings>
    </telerik:SeriesMapping>

    I've been trying to get this connected to an undefined number of collection items, ie. get rid of "ColletionIndex" and making LegendLabel a binding, but I keep failing.. I had something using "ChartArea" and "XAxis", but that threw a NullReferenceException somewhere deep in the Telerik namespace..

    To help RadChart out, I've added IEnumerable<KeyValuePair<DateTime,int>> to my CLR object, which makes the above work fine, sort of.

    I hope my point comes across in the right way; MVVM programming is meant to be neat and effective, and writing up codebehind or generating intermediary objects somewhere is far from neat. Having to write a slate of XAML to define how the chart handles incoming data is fine, but it should be flexible enough to allow the consumption of unmodified objects from the deeper levels, in my opinion.

    Anyway, I now have this nearly-working chart:
    <telerikChart:RadChart Grid.Row="1" Margin="4,0" Background="#66ffffff" ItemsSource="{Binding Prognoses}">
        <telerikChart:RadChart.DefaultView>
            <telerikCharting:ChartDefaultView>
                <telerikCharting:ChartDefaultView.ChartLegend>
                    <telerikCharting:ChartLegend x:Name="chartLegend" UseAutoGeneratedItems="True" HeaderStringFormat="d" />
                </telerikCharting:ChartDefaultView.ChartLegend>
                <telerikCharting:ChartDefaultView.ChartArea>
                    <telerikCharting:ChartArea LegendName="chartLegend">
                        <telerikCharting:ChartArea.AxisX>
                            <telerikCharting:AxisX IsDateTime="True" DefaultLabelFormat="d" />
                        </telerikCharting:ChartArea.AxisX>
                    </telerikCharting:ChartArea>
                </telerikCharting:ChartDefaultView.ChartArea>
            </telerikCharting:ChartDefaultView>
        </telerikChart:RadChart.DefaultView>
        <telerikChart:RadChart.DefaultSeriesDefinition>
            <telerikCharting:LineSeriesDefinition />
        </telerikChart:RadChart.DefaultSeriesDefinition>
        <!--<telerikChart:RadChart.SeriesMappings>
            <telerikCharting:SeriesMapping>
                <telerikCharting:SeriesMapping.ItemMappings>
                    <telerikCharting:ItemMapping DataPointMember="XValue" FieldName="Key" />
                    <telerikCharting:ItemMapping DataPointMember="YValue" FieldName="Value" />
                </telerikCharting:SeriesMapping.ItemMappings>
            </telerikCharting:SeriesMapping>
        </telerikChart:RadChart.SeriesMappings>-->
    </telerikChart:RadChart>

    The only missing wheel, is associating the SeriesMapping with the DefaultSeriesDefinition and binding LegendLabel to the appropriate property.
    It would also be beneficial if I could tell the SeriesMapping to get series data from a named property, rather than the object itself having to implement IEnumerable.

    Regards, Morten
  6. Evgenia
    Admin
    Evgenia avatar
    1407 posts

    Posted 19 Jan 2011 Link to this post

    Hi Morten,

    After carefully reading all your posts here is what I can suggest you:
    1.    I guess you need Dictionary<DateTime, int>  because you want each DateTime value on XAxis to be strongly related to it's int value by YAxis. There is no need of Dictionary collection as if you use List or ObservableCollection of class of Business Objects each XValue will be connected to it's YValue in the way the collection was filled. To get clear idea of what I mean you can review our help topic.
    2.    To databind LegendLabel you should create new ItemMapping in the SameMapping and set  DataPointmember.LegendLabel to be binded to the property with FieldName = "your property name here".
    3.    "Isn't there some useful documentation of what properties exist on what objects and what they mean" - you can use our help and search for any property that you want. For example - http://www.telerik.com/help/wpf/telerik.windows.controls.charting-telerik.windows.controls.charting.datapoint-legendlabel.html gives you detail information about the namespace this property belongs to, short description for it and etc.
    4.     There is no need to specify the DefaultSeriesDefinition for the chart this way:
    <telerikChart:RadChart.DefaultSeriesDefinition>
            <telerikCharting:LineSeriesDefinition />
        </telerikChart:RadChart.DefaultSeriesDefinition>
    You can just specify the SeriesDefinition for the SeriesMapping like this;
    <telerik:SeriesMapping >
                 <telerik:SeriesMapping.SeriesDefinition>
                     <telerik:LineStickSeriesDefinition />
                 </telerik:SeriesMapping.SeriesDefinition>

    Kind regards,
    Evgenia
    the Telerik team
    Let us know about your Windows Phone 7 application built with RadControls and we will help you promote it. Learn more>>
  7. Morten Nilsen
    Morten Nilsen avatar
    32 posts
    Member since:
    Jun 2010

    Posted 19 Jan 2011 Link to this post

    Hi

    1. it is a dictionary because that is what I am extracting from the source..
    Whether it is an IEnumerable<SomeRandomObject> or a Dictionary<DateTime,int> shouldn't make much difference.. in data binding terms it will just be obj.Something/obj.SomethingElse vs. obj.Key/obj.Value I managed that bit to work just fine.
    The only issue I had was with being able to tell RadChart to get Series data from a property rather than the object itself.
    I didn't like my solution of exposing IEnumerable from the dictinary object as an interface on my object.

    3. That seems rather well hidden to me.. I would hope some intellisense could be added for these things, or at least make VS help work with these things.. Pressing F1 on some xaml element takes me to "XAML and Code in the WPF Designer" on msdn.

    4. yes, I had that at first, but I was trying to get rid of the SeriesMapping entirely..

    I've taken into account your reply, but have been unable to solve my issue..
    To summarize;
    I have a collection of objects that contain a collection.
    Each object is a series.
    I do not want to pollute the data with telerik chart specifics.

    My chart now looks like this:
    <telerikChart:RadChart Grid.Row="1" Margin="4,0" Background="#66ffffff" ItemsSource="{Binding Prognoses}">
        <telerikChart:RadChart.DefaultView>
            <telerikCharting:ChartDefaultView>
                <telerikCharting:ChartDefaultView.ChartLegend>
                    <telerikCharting:ChartLegend x:Name="chartLegend" UseAutoGeneratedItems="True" HeaderStringFormat="d" />
                </telerikCharting:ChartDefaultView.ChartLegend>
                <telerikCharting:ChartDefaultView.ChartArea>
                    <telerikCharting:ChartArea LegendName="chartLegend">
                        <telerikCharting:ChartArea.AxisX>
                            <telerikCharting:AxisX IsDateTime="True" DefaultLabelFormat="d" />
                        </telerikCharting:ChartArea.AxisX>
                    </telerikCharting:ChartArea>
                </telerikCharting:ChartDefaultView.ChartArea>
            </telerikCharting:ChartDefaultView>
        </telerikChart:RadChart.DefaultView>
    <telerikChart:RadChart.SeriesMappings>
            <telerikCharting:SeriesMapping>
                <telerikCharting:SeriesMapping.SeriesDefinition>
                    <telerikCharting:LineSeriesDefinition />
                </telerikCharting:SeriesMapping.SeriesDefinition>
                <telerikCharting:SeriesMapping.ItemMappings>
                    <telerikCharting:ItemMapping DataPointMember="LegendLabel" FieldName="PartNumber" />
                    <telerikCharting:ItemMapping DataPointMember="XValue" FieldName="Prognosis.Key" />
                    <telerikCharting:ItemMapping DataPointMember="YValue" FieldName="Prognosis.Value.Forecast" />
                </telerikCharting:SeriesMapping.ItemMappings>
            </telerikCharting:SeriesMapping>
        </telerikChart:RadChart.SeriesMappings>
    </telerikChart:RadChart>

    I now get a NullReferenceException in c:\Builds\WPF_Scrum\Chart_WPF_2010_Q1\Sources\Development\Controls\Chart\Chart\Data\DataBindingHelper.cs:line 364
    If I add CollectionIndex="0" to the SeriesMapping object, that goes away, but I then appear to get no series.

    I simply see no way of dynamically binding a collection of series to a RadChart, with a complex data model.. :/
    If this sort of scenario isn't supported, then how can one add a chart to an existing application without writing a whole bunch of converter code that rewraps data in different objects?

    As I understand it, your recommended solution is for me to replace my data model with one such as this:
    class SimpleData
    {
        public string PartNumber { get; set; }
        public DateTime Key { get; set; }
        public int Value { get; set; }
    }
    then bind RadChart to a List<SimpleData>
    I can appreciate that, but I just won't accept it as a solution, I'm sorry to say..

    I have since I made this post finsihed and installed the application I was building.
    What I ended up doing was using a simple ListBox with all the data as text.
    Along the way, I have changed the data model to be a little richer, my dictionary has a complex type for value rather than int - otherwise, it is the same.

    Just to try, I've modified my inner class and made my outer class implement IEnumerable, but I keep getting NullReferenceExceptions as detailed above.

    If you would like, I could create a small mockup application that you can have a look at.

       // Regards, Morten
  8. Morten Nilsen
    Morten Nilsen avatar
    32 posts
    Member since:
    Jun 2010

    Posted 19 Jan 2011 Link to this post

    I just went ahead and created a small application.. It is attached to ticket number 385806

      // Cheers, Morten
  9. Evgenia
    Admin
    Evgenia avatar
    1407 posts

    Posted 24 Jan 2011 Link to this post

    Hello Morten,

    As this conversation was moved to the Support thread opened by you - Ticket ID: 385806 this forum post will be closed so that both sides are following one and the same thread. If you have more questions - please start a new post or continue writing in the Support ticket that you've opened.

    Regards,
    Evgenia
    the Telerik team
    Let us know about your Windows Phone 7 application built with RadControls and we will help you promote it. Learn more>>
Back to Top
UI for WPF is Visual Studio 2017 Ready