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

Displaying a list of RadCartesianCharts in a DataTemplate

1 Answer 160 Views
Chart
This is a migrated thread and some comments may be shown as answers.
Ryan
Top achievements
Rank 1
Ryan asked on 25 Mar 2020, 09:39 PM

Is there a way to create multiple charts in XAML using the DataTemplate tag and a collection of collections (e.g., ObservableCollection<Dictionary<string,int>>)  as the ItemsSource?  I have no need of displaying multiple series on one chart via SeriesProvider, since the keys/categorical variables in each Dictionary are not related.  Below was my attempt to implement it in XAML:

01.<ItemsControl Name="MyCategoryCharts">
02.    <ItemsControl.ItemTemplate>
03.        <DataTemplate x:DataType="dghelper:CategoryGroupCollection">
04.            <controls:Expander ExpandDirection="Down"
05.                                Header="{Binding CategoryName}"
06.                                FontWeight="SemiBold">
07. 
08.                <GridView>
09.                    <GridView.ItemTemplate>
10.                        <DataTemplate x:DataType="Dictionary">
11.                            <telerikChart:RadCartesianChart>
12. 
13.                                <telerikChart:RadCartesianChart.HorizontalAxis>
14.                                    <telerikChart:LinearAxis />
15.                                </telerikChart:RadCartesianChart.HorizontalAxis>
16.                                <telerikChart:RadCartesianChart.VerticalAxis>
17.                                    <telerikChart:CategoricalAxis />
18.                                </telerikChart:RadCartesianChart.VerticalAxis>
19. 
20.                                <telerikChart:RadCartesianChart.Grid>
21.                                    <telerikChart:CartesianChartGrid MajorLinesVisibility="X" StripLinesVisibility="X"/>
22.                                </telerikChart:RadCartesianChart.Grid>
23. 
24.                                <telerikChart:BarSeries ItemsSource="{Binding GroupDict}">
25.                                    <telerikChart:BarSeries.CategoryBinding>
26.                                        <telerikChart:PropertyNameDataPointBinding PropertyName="Key"/>
27.                                    </telerikChart:BarSeries.CategoryBinding>
28.                                    <telerikChart:BarSeries.ValueBinding>
29.                                        <telerikChart:PropertyNameDataPointBinding PropertyName="Value"/>
30.                                    </telerikChart:BarSeries.ValueBinding>
31.                                </telerikChart:BarSeries>
32. 
33. 
34.                            </telerikChart:RadCartesianChart>
35.                        </DataTemplate>
36.                    </GridView.ItemTemplate>
37.                </GridView>
38.            </controls:Expander>                                       
39.        </DataTemplate>
40.    </ItemsControl.ItemTemplate>
41.</ItemsControl>

 

If there's no XAML solution for this problem, could you provide a C#/Code-behind way of creating a RadCartesianChart, i.e., a C#-equivalent of this tutorial (Getting Started with Telerik UI for UWP).  I've implemented this as a workaround/alternative solution to the yet-to-be-shown XAML solution but have encountered a roadblock in assigning the CategoryBinding and ValueBinding.  Below is my current progress to programmatic solution:

 

01.private RadCartesianChart CreateChart(CategoryGroupCollection coll)
02.{           
03.    // Set up chart
04.    RadCartesianChart retChart = new RadCartesianChart();
05.    retChart.PaletteName = PredefinedPaletteName.DefaultDark;
06.    retChart.DataContext = coll;
07.    retChart.HorizontalAxis = new LinearAxis();
08.    retChart.VerticalAxis = new CategoricalAxis();
09.             
10.    // Set up the data series for the chart
11.    BarSeries series = new BarSeries();
12.    series.ItemsSource = coll.GroupDict;           
13. 
14.    // TODO: assigning CategoryBinding and ValueBinding (PropertyNameDataPointBinding)
15. 
16.    // Add the series to the chart
17.    retChart.Series.Add(series);
18.    return retChart;
19.}
20. 
21.private void CreateCharts(IEnumerable<CategoryGroupCollection> dataCollection)
22.{
23.    foreach (CategoryGroupCollection item in dataCollection)
24.    {
25.        ChartsStackPanel.Children.Add(CreateChart(item));
26.    }
27.}
28. 
29.public class CategoryGroupCollection
30.{
31.    public string CategoryName { get; set; }
32.    public Dictionary<string, int> GroupDict { get; set; }
33.}

1 Answer, 1 is accepted

Sort by
0
Lance | Manager Technical Support
Telerik team
answered on 26 Mar 2020, 07:21 PM

Hi Ryan,

Unfortunately, the code example is a bit confusing with the parent ItemsControl because there is no parent data collection in the data models. To eliminate the confusion and get you an answer, I'll remove the code feature unrelated to the Telerik Chart and focus on the fundamental part of your question: "How to use the GroupDict as the ItemsSource for a BarSeries?". 

At this time, dynamic getters are not supported, so directly binding to Dictionary<string,int> won't work. However, I can still prove you with a solution, let's dig in.

Solution

You can still achieve the dynamic approach you're looking (all in XAML) by adding a simple data model that will support using the KeyValuePair<string, int> in the GroupDict property.

First, add a new model class that can take a string and an int:

public class SeriesDataPoint
{
    public string Category { get; set; }
    public int Value { get; set; }
}

Next, in  the CategoryGroupCollection model class, you can add an ObservableCollection<SeriesDataPoint> and expand the setter for GroupDict:

public class CategoryGroupCollection
{
    public string CategoryName { get; set; }

    private Dictionary<string, int> groupDict;
    public Dictionary<string, int> GroupDict
    {
        get => groupDict;
        set
        {
            groupDict = value;
        }
    }

    public ObservableCollection<SeriesDataPoint> ChartSeriesData { get; private set; }
}

 

Next, add a helper method that will automatically populate the collection when GroupDict is set:

public class CategoryGroupCollection
{
    public string CategoryName { get; set; }

    private Dictionary<string, int> groupDict;
    public Dictionary<string, int> GroupDict
    {
        get => groupDict;
        set
        {
            groupDict = value;
            CreateChartData();
        }
    }

    public ObservableCollection<SeriesDataPoint> ChartSeriesData { get; private set; }

    private void CreateChartData()
    {
        ChartSeriesData = new ObservableCollection<SeriesDataPoint>();

        foreach (var keyValPair in GroupDict)
        {
            ChartSeriesData.Add(new SeriesDataPoint
            {
                Category = keyValPair.Key,
                Value = keyValPair.Value
            });
        }
    }
}

Notice the important takeaway in the darker orange highlight. This will create data points for the keys and values that can be rendered in the BarSeries.

XAML

Finally, lets go back to the XAML. Instead of binding GroupDict to the ItemsSource, use the new ChartSeriesData property (don't forget to change property name from "Key" to "Category"):

<Page x:Class="App1.MainPage"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:local="using:App1"
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
      xmlns:chart="using:Telerik.UI.Xaml.Controls.Chart"
      xmlns:toolkitControls="using:Microsoft.Toolkit.Uwp.UI.Controls"
      mc:Ignorable="d"
      Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

    <Page.DataContext>
        <local:MainViewModel />
    </Page.DataContext>

    <Grid>
        <GridView ItemsSource="{Binding Items}">
            <GridView.ItemTemplate>
                <DataTemplate>
                    <toolkitControls:Expander Header="{Binding CategoryName}"
                                              IsExpanded="True">
                        <chart:RadCartesianChart Width="400"
                                                 Height="400">
                            <chart:RadCartesianChart.HorizontalAxis>
                                <chart:LinearAxis />
                            </chart:RadCartesianChart.HorizontalAxis>
                            <chart:RadCartesianChart.VerticalAxis>
                                <chart:CategoricalAxis />
                            </chart:RadCartesianChart.VerticalAxis>
                            <chart:RadCartesianChart.Grid>
                                <chart:CartesianChartGrid MajorLinesVisibility="X"
                                                          StripLinesVisibility="X" />
                            </chart:RadCartesianChart.Grid>

                            <chart:BarSeries ItemsSource="{Binding ChartSeriesData}">
                                <chart:BarSeries.CategoryBinding>
                                    <chart:PropertyNameDataPointBinding PropertyName="Category" />
                                </chart:BarSeries.CategoryBinding>
                                <chart:BarSeries.ValueBinding>
                                    <chart:PropertyNameDataPointBinding PropertyName="Value" />
                                </chart:BarSeries.ValueBinding>
                            </chart:BarSeries>
                        </chart:RadCartesianChart>
                    </toolkitControls:Expander>
                </DataTemplate>
            </GridView.ItemTemplate>
        </GridView>
    </Grid>
</Page>

Runtime

I've attached a runnable example so you can see it working on your side and review the same data creation.

For your convenience, here's a screenshot:

Note: Because this is the Telerik Forums, the demo is using the commercial assembly reference for UI for UWP. If you're using the open source UI for UWPversion, StackOverflow is the location for support. Ultimately, there is no difference in code between the two, you'd just have to update the project references to the one you're using.

Summary

As I mentioned in the beginning, I've removed the complexity of the unrelated-to-Telerik UIElements so I could clearly demonstrate that you can use the chart in the DataTemplate like you've requested.

If your real application has a different data hierarchy, carefully consider where the data bindings are and what is available at that level. For example, in your initial code snippet, there is no ItemsSource for the GridView, so there will never be any items and the GroupDict property is always null. Think about where the GroupDict coming from and is it null inside the GridView ItemTemplate?

Further Assistance

If you do find yourself stuck, please update the attached example so that it recreates the problem at runtime. With these kinds of questions, we really do need to see the data at runtime to give you a directly applicable answer.

Here are some of the things that will need to added/updated int he demo

  • Update the DataModel classes to fully represent the real ones
  • Populate the ViewModel with sample data that represents the real data (see my sample data helper class)
  • Set all of the XAML element bindings (i.e. we should at least be able to see the CategoryName in each item)

I hope I was able to help in regards to the Series. If you would like additional assistance with data hierarchy, StackOverflow would be a good place as well.

Regards,
Lance | Team Lead - US DevTools Support
Progress Telerik

Progress is here for your business, like always. Read more about the measures we are taking to ensure business continuity and help fight the COVID-19 pandemic.
Our thoughts here at Progress are with those affected by the outbreak.
Tags
Chart
Asked by
Ryan
Top achievements
Rank 1
Answers by
Lance | Manager Technical Support
Telerik team
Share this question
or