Saving chart as image

Charts for WPF Forum

Posted 6 years ago by Chris Ray
Version: 13.1.0580
Avatar

Hello gents. I need to be able to generate a chart in code and subsequently export it as an image. I figured I could do this just using WPF even if there was no explicit support for it in Actipro Charts. I'm very close, as I have it rendering a basic chart as a PNG on my file system. Problem is that to make it work I have to set a timer, wait a random amount of time, and then run the code to export. If I try to export immediately after creation, I get an image with a border, but no data. I'm thinking it might have something to do with animations, based on the stackoverflow question that I got the render code from.

My goal is to be able to generate charts in code, then immediately obtain a MemoryStream of the PNG image of that chart, no hokey timers... Is this possible using the current Actipro Charts?

I have a fully operational project demonstrating the problem which I will send to the support email address right after creating this thread. Here is the code, however, so that others can see.

<Window x:Class="ChartToPngTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid x:Name="mainGrid">
        
    </Grid>
</Window>

 

using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using ActiproSoftware.Windows.Controls.Charts;

namespace ChartToPngTest
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        XYChart chart;
        public MainWindow()
        {
            InitializeComponent();

            chart = new XYChart();
            chart.Width = 500;
            chart.Height = 200;
            chart.IsAxisBaselineVisible = true;
            chart.GridLineMajorVisibility = GridLineVisibility.Y;
            chart.LabelCollisionMode = LabelCollisionMode.Stacked;

            var xaxis = new XYDoubleAxis();
            xaxis.AreMajorTicksVisible = true;
            xaxis.TickMajorInterval = 1000;

            var yaxis = new XYDoubleAxis();
            yaxis.AreMajorTicksVisible = true;
            yaxis.TickMajorInterval = 1;

            chart.XAxes.Add(xaxis);
            chart.YAxes.Add(yaxis);

            var ls = new LineSeries();
            var lsl = new List<XvsY>();
            for (int i = 0; i < 10; i++)
                lsl.Add(new XvsY(i * 1000, 9 + ((double)i / 10 * (i % 2 == 0 ? 1 : -1)))); //dont ask...

            ls.ItemsSource = lsl;
            ls.XPath = "X";
            ls.YPath = "Y";
            chart.Series.Add(ls);

            mainGrid.Children.Add(chart);

            //WritePng(); //generates a png that looks like it has a border, but no data
            this.Loaded += new RoutedEventHandler(Window1_Loaded); //waits 2 seconds, then runs WritePng()... this works.
        }

        System.Windows.Threading.DispatcherTimer snapshotTimer;
        void Window1_Loaded(object sender, RoutedEventArgs e)
        {
            this.snapshotTimer = new System.Windows.Threading.DispatcherTimer();
            this.snapshotTimer.Interval = TimeSpan.FromSeconds(2);
            this.snapshotTimer.Tick += new EventHandler(snapshotTimer_Tick);
            this.snapshotTimer.IsEnabled = true;
        }

        void snapshotTimer_Tick(object sender, EventArgs e)
        {
            this.snapshotTimer.IsEnabled = false;
            WritePng();
        }

        private void WritePng()
        {
            chart.Measure(new Size(chart.Width, chart.Height));
            chart.Arrange(new Rect(new Size(chart.Width, chart.Height)));
            chart.UpdateLayout();
            //chart.Refresh();
            RenderTargetBitmap rtb = new RenderTargetBitmap((int)chart.Width, (int)chart.Height, 96, 96, PixelFormats.Pbgra32);
            rtb.Render(chart);
            PngBitmapEncoder png = new PngBitmapEncoder();
            png.Frames.Add(BitmapFrame.Create(rtb));
            string file = System.IO.Path.GetTempFileName() + ".png";
            using (System.IO.Stream stream = System.IO.File.Create(file))
            {
                png.Save(stream);
            }
            System.Diagnostics.Process.Start(file);
        }
    }

    struct XvsY
    {
        public double X { get; set; }
        public double Y { get; set; }

        public XvsY(double X, double Y)
            : this()
        {
            this.X = X;
            this.Y = Y;
        }
    }
}

 

Thank you,

Chris.

Comments (2)

Answer - Posted 6 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar

Hi Chris,

Unfortunately I don't have a workaround for the current release -- however, in the next maintenance release, due to some restructuring with our Measure / Arrange logic, you'll be able to just wait for the XYChart.Loaded event and render your bitmap without the use of timers.


Actipro Software Support

Posted 6 years ago by Chris Ray
Avatar

Alright well, until this release comes out, as well as the reverse Y axis issue I posted about earlier are resolved I'll be forced to continue with the other control set. :-/ I'll be waiting patiently.

The latest build of this product (v2019.1 build 0681) was released 11 days ago, which was after the last post in this thread.

Add Comment

Please log in to a validated account to post comments.