Create chart in code behind

8 posts, 0 answers
  1. VPS
    VPS avatar
    10 posts
    Member since:
    Jan 2015

    Posted 01 Feb Link to this post

    I’m developing a service where I need to create a chart using only code behind. The chart will then be exported to a bitmap and inserted into a RadDocument.

    My code is:

    Grid grid = new Grid();
    grid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) });
    grid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
     
    RadCartesianChart chart = new RadCartesianChart();
    chart.Grid = new CartesianChartGrid
                 {
                     MajorLinesVisibility = GridLineVisibility.Y,
                     StripLinesVisibility = GridLineVisibility.Y
                 };
     
    chart.HorizontalAxis = new CategoricalAxis { PlotMode = AxisPlotMode.BetweenTicks };
    chart.VerticalAxis = new LinearAxis { Minimum = 0, MajorTickLength = 5, Title = "Y axis title" };
     
    // create series
    for (int i = 0; i < series.Count; i++)
    {
        LineSeries lineSeries = new LineSeries
                                {
                                    Stroke = new SolidColorBrush(series[i].Color),
                                    LegendSettings = new SeriesLegendSettings { Title = series[i].Title }
                                };
     
        foreach (var value in series[i].Values)
        {
            lineSeries.DataPoints.Add(new CategoricalDataPoint { Category = value.X, Value = value.Y });
        }
     
        chart.Series.Add(lineSeries);
    }
     
    RadLegend legend = new RadLegend
                       {
                           HorizontalAlignment = HorizontalAlignment.Center,
                           VerticalAlignment = VerticalAlignment.Bottom,
                           Items = chart.LegendItems
                       };
     
    string panelTemplate = @"<ItemsPanelTemplate xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation"">" +
                           @"<StackPanel Orientation=""Horizontal""/>" +
                           @"</ItemsPanelTemplate>";
    legend.ItemsPanel = (ItemsPanelTemplate)XamlReader.Parse(panelTemplate);
     
    chart.SetValue(Grid.RowProperty, 0);
    grid.Children.Add(chart);
     
    legend.SetValue(Grid.RowProperty, 1);
    grid.Children.Add(legend);
     
    Size chartSize = new Size(width, height);
    grid.Measure(chartSize);
    grid.Arrange(new Rect(new Point(0, 0), chartSize));
     
    grid.UpdateLayout();
     
    RenderTargetBitmap bitmapRender = new RenderTargetBitmap(width, height, 96, 96, PixelFormats.Pbgra32);
    bitmapRender.Render(grid);
     
    var encoder = new PngBitmapEncoder();
    encoder.Frames.Add(BitmapFrame.Create(bitmapRender));
     
    encoder.Save(stream);

    If I set a breakpoint where the RadLegend is created, I can see that the series I’ve created have been added to chart.Series but chart.LegendItems is empty.

    If I continue and export the chart to a bitmap I get an empty image.

    All the code seems to be alright, what am I doing wrong?


     
     
  2. Petar Marchev
    Admin
    Petar Marchev avatar
    968 posts

    Posted 02 Feb Link to this post

    Hi,

    I suspect that the chart is not printed because you do not call the begin and end init methods. The legend is not exported because it requires an additional layout pass. This is so that the legend markers can get the adequate brushes (which may be delayed when bindings are used). So, the chart really waits for an additional frame, and at the time of the export, the chart has not yet produced legend items. Both of these can be easily worked around. I am attaching a project and a snapshot of the result. See in the project how the PrepareElementForExport method is used to address both of these peculiarities.

    Let us know if all goes well or if you need further assistance.

    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
  3. UI for WPF is Visual Studio 2017 Ready
  4. VPS
    VPS avatar
    10 posts
    Member since:
    Jan 2015

    Posted 02 Feb in reply to Petar Marchev Link to this post

    Thank you for your reply. It has partially solved my problem: I was using the assemblies from the Binaries.NoXaml folder, using those files I get a transparent image. When I changed the references in my project to use the assemblies in the Binaries folder the chart showed up in the image.

    However, I could not make the legend show. The workaround in your sample project does not work since I am using a Console Application (during developement, it wll be moved to a service) and your example is using a WPF Application. I have created and a project using your sample code inside a Console Application that does not show the legend:

     

    static void Main(string[] args)
    {
        using (FileStream stream = File.Create("chart.png"))
        {
            Task createTask = StartSTATask(() => BuildSTAChart(stream));
            createTask.Wait();
        }
    }
     
    [STAThread]
    private static Grid BuildSTAChart(Stream stream)
    {
        Grid host = new Grid();
     
        RadCartesianChart chart = BuildChart();
        host.Children.Add(chart);
     
        RadLegend legend = BuildChartLegend(chart);
        host.Children.Add(legend);
     
        PrepareElementForExport(host, 500, 300);
        Telerik.Windows.Media.Imaging.ExportExtensions.ExportToImage(host, stream, new PngBitmapEncoder());
     
        return host;
    }
     
    public static Task<T> StartSTATask<T>(Func<T> func)
    {
        var tcs = new TaskCompletionSource<T>();
        Thread thread = new Thread(() =>
        {
            try
            {
                tcs.SetResult(func());
            }
            catch (Exception ex)
            {
                tcs.SetException(ex);
            }
        });
        thread.SetApartmentState(ApartmentState.STA);
        thread.Start();
        return tcs.Task;
    }

    The rest of the code is the same as in your sample project.

    Can I try anything else to try to make the legend show, or should I start looking for alternatives?

    Regrads,

     Luis Barreira

  5. Petar Marchev
    Admin
    Petar Marchev avatar
    968 posts

    Posted 03 Feb Link to this post

    Hi Luis,

    I tried running the code in a console application and still the legend gets exported. I am attaching the project I tested with and an image that was output. Let us know if you get the same behavior on your side or if you need further assistance.

    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
  6. VPS
    VPS avatar
    10 posts
    Member since:
    Jan 2015

    Posted 03 Feb in reply to Petar Marchev Link to this post

    Hi Petar, 

    After looking at your code, which is the same as mine, I was finally able to find the difference: the version of the .Net Framework used. Your code uses 4.0, mine uses 4.5.1.

    If I change the framework version on my code to 4.0, it works as expected; if I change the framework version on your code to 4.5.1 then the legend does not show (even when using the libs from the WPF40 folder).

    This is an acceptable solution only for the test project. The project where I'm including the charts is part of a very large solution and changing it back to 4.0 is not an option. Looks like for now I'm having to come up with some alternative to the legend.

    Regards,

    Luis Barreira

  7. Petar Marchev
    Admin
    Petar Marchev avatar
    968 posts

    Posted 04 Feb Link to this post

    Hello Luis,

    Indeed when I change the target framework and the legend does not get exported. I can see that the LegendItems collection of the chart is empty, so it seems that we were unable to force the chart to produce legend items. We will look into this and see if we can improve this part of the chart. The only work around I can suggest at this moment is for you not to use the LegendSettings. You can manually create the legend items and feed the RadLegend yourself. Let us know how it goes.

    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
  8. Milena
    Admin
    Milena avatar
    201 posts

    Posted 05 Feb Link to this post

    Hello Luis,

    I'm happy to inform you that we will include this improvement in the ChartView with the upcoming Q2 2016 SP1 Release, which is scheduled for this month. I hope that you will be able to upgrade to the latest version and get the desired behavior. 

    Regards,
    Milena
    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
  9. VPS
    VPS avatar
    10 posts
    Member since:
    Jan 2015

    Posted 05 Feb in reply to Milena Link to this post

    Thank you both for your help.

    I have come up with a temporary solution that works for now. I will try using the RadLegend again when the next version is available.

    Regards,

    Luis Barreira

Back to Top
UI for WPF is Visual Studio 2017 Ready