Saving chart as image

Charts for WPF Forum

Posted 7 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 7 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 7 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 0683) was released 1 month ago, which was after the last post in this thread.

Add Comment

Please log in to a validated account to post comments.