This project is read-only.

Issue with Plotter.SaveScreenshot method

Apr 3, 2011 at 11:24 PM

Hi All,

I was going through an article on the usage of Dynamic Data Display http://msdn.microsoft.com/en-us/magazine/ff714591.aspx just to play around with plotting data programmatically and saving them.
I tried to programmatically save the screenshot of the generated image for the BugGraph code in the above link using plotter.SaveScreenshot(string filename) method.
It is saving the control but, not the data points and the corresponding graph when I try saving programmatically
I tried the Plotter.SaveScreenShotToStream(Stream stream, String extension), but that too produced the same output as the above method.

However, when I save the image by doing a right click on the generated Graph and choose the SaveScreenshot menuitem, it saves the data points and the generated graph.
I downloaded the source code to see if the right click is invoking a different command, but doesn’t seem so.

I have tried searching the forums but couldnt find a similar issue.
I am new to WPF and could you tell me if there is some intermediate command that I am missing before I invoke SaveScreenshot?
OR is there any other way to save the screenshot using Dynamic Data Display?

Here is the code:

using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Media;

using System.IO;
using Microsoft.Research.DynamicDataDisplay; // Core functionality
using Microsoft.Research.DynamicDataDisplay.DataSources; // EnumerableDataSource
using Microsoft.Research.DynamicDataDisplay.PointMarkers;
using System.Globalization; // CirclePointMarker

namespace WpfApplication1
{
    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
            Loaded += new RoutedEventHandler(Window1_Loaded);
        }

        private void Window1_Loaded(object sender, RoutedEventArgs e)
        {
            List<BugInfo> bugInfoList = LoadBugInfo("..\\..\\BugInfo.txt");

            DateTime[] dates = new DateTime[bugInfoList.Count];
            int[] numberOpen = new int[bugInfoList.Count];
            int[] numberClosed = new int[bugInfoList.Count];

            for (int i = 0; i < bugInfoList.Count; ++i)
            {
                dates[i] = bugInfoList[i].date;
                numberOpen[i] = bugInfoList[i].numberOpen;
                numberClosed[i] = bugInfoList[i].numberClosed;
            }

            var datesDataSource = new EnumerableDataSource<DateTime>(dates);
            datesDataSource.SetXMapping(x => dateAxis.ConvertToDouble(x));

            var numberOpenDataSource = new EnumerableDataSource<int>(numberOpen);
            numberOpenDataSource.SetYMapping(y => y);

            var numberClosedDataSource = new EnumerableDataSource<int>(numberClosed);
            numberClosedDataSource.SetYMapping(y => y);

            CompositeDataSource compositeDataSource1 = new
              CompositeDataSource(datesDataSource, numberOpenDataSource);
            CompositeDataSource compositeDataSource2 = new
              CompositeDataSource(datesDataSource, numberClosedDataSource);

            plotter.AddLineGraph(compositeDataSource1,
              new Pen(Brushes.Blue, 2),
              new CirclePointMarker { Size = 10.0, Fill = Brushes.Red },
              new PenDescription("Number bugs open"));

            plotter.AddLineGraph(compositeDataSource2,
              new Pen(Brushes.Green, 2),
              new TrianglePointMarker
              {
                  Size = 10.0,
                  Pen = new Pen(Brushes.Black, 2.0),
                  Fill = Brushes.GreenYellow
              },
              new PenDescription("Number bugs closed"));

            plotter.Viewport.FitToView();
            plotter.SaveScreenshot(@"C:\Users\arvind\Desktop\1.png");
            //using (Stream screenshotStream = File.Create(@"C:\Users\arvindsu\Desktop\1.png"))
            //{
            //    plotter.SaveScreenshotToStream(screenshotStream, "png");
            //}
        }

        private static List<BugInfo> LoadBugInfo(string fileName)
        {
            var result = new List<BugInfo>();
            FileStream fs = new FileStream(fileName, FileMode.Open);
            StreamReader sr = new StreamReader(fs);
            DateTimeFormatInfo dtfi = CultureInfo.CreateSpecificCulture("en-US").DateTimeFormat;

            string line = "";
            while ((line = sr.ReadLine()) != null)
            {
                if (String.IsNullOrEmpty(line)) continue;

                string[] pieces = line.Split(':');
                
                DateTime d = DateTime.Parse(pieces[0], dtfi);
                int numopen = int.Parse(pieces[1]);
                int numclosed = int.Parse(pieces[2]);
                BugInfo bi = new BugInfo(d, numopen, numclosed);
                result.Add(bi);
            }
            sr.Close();
            fs.Close();
            return result;
        }

    }

    public class BugInfo
    {
        public DateTime date;
        public int numberOpen;
        public int numberClosed;

        public BugInfo(DateTime date, int numberOpen, int numberClosed)
        {
            this.date = date;
            this.numberOpen = numberOpen;
            this.numberClosed = numberClosed;
        }
    }
}

May 23, 2011 at 5:56 PM

Hi,

I've run into same problem today, but after going through code and websites I've found a solution for this. Helpful was post about similar problem with other library: http://news.infragistics.com/forums/p/12901/48251.aspx#48251
I've done this like that, it's not elegant but I hadn't more time to test other options:

{ .... some function ... }

ChartPlotter plot = new ChartPlotter();
 InitPlotter(plot); // this one is important
 ChartHelper.GenerateFitnessChart(viewModel, plot); // it's my function which is only adding LineGraph to plotter
 filePath = "{path to file}";
 SaveScreenshot(plot, filePath); // important

// I'm cleaning up this object because after only removing elements and adding new ones
// rendering were, how to say that without bad words :P messed up
plot.RemoveUserElements();
plot = null;

// after that i'm assigning "plot = new ChartPlotter();" if i need more plots to files

{ ... some other function lines ... }

// Initializing ChartPlotter without window, we have to assign width and height
// imgWidth and imgHeight is your desired format
private void InitPlotter(ChartPlotter plot)
        {
            plot.Width = imgWidth;
            plot.Height = imgHeight;
            Size size = new Size(imgWidth, imgHeight);
            plot.RenderSize = size;
        }

// It's modified function similar to the one in DDD source
private void SaveScreenshot(ChartPlotter plot, string filepath)
        {
            Size size = new Size(imgWidth, imgHeight);

            Grid grid = new Grid();
            grid.Children.Add(plot);

// Don't ask me if something is unnecessary here or what order should be,
// after many test this configuration worked for me
            plot.Measure(new Size(imgWidth, imgHeight));
            plot.Arrange(new Rect(0, 0, imgWidth, imgHeight));
            plot.PerformLoad();
            plot.UpdateLayout();
            plot.InvalidateVisual();

            RenderTargetBitmap bmp = new RenderTargetBitmap((int)size.Width, (int)size.Height,
                96d, 96d, PixelFormats.Pbgra32);
            grid.Measure(size);
            grid.Arrange(new Rect(0, 0, size.Width, size.Height));
            bmp.Render(grid);

            PngBitmapEncoder png = new PngBitmapEncoder();
            png.Frames.Add(BitmapFrame.Create(bmp));

            using (Stream str = File.Create(filepath))
            {
                png.Save(str);
            }

            grid.Children.Remove(plot);
        }
Jack

Oct 14, 2011 at 9:47 PM

Jack, your post here is a godsend.  I've been banging my head against the wall trying to get the ChartPlotter to render offscreen so I could capture bitmaps for a PDF report.  The key calls I was missing, was "PerformLoad" and "InvalidateVisual".  After many hours of failure to get this working, your solution works like a champ!  Thanks!!!

Robin

Oct 15, 2011 at 9:18 AM

I'm glad that it helped you :)

Jack

Mar 19, 2015 at 1:23 PM
i got following error
{"Specified element is already the logical child of another element. Disconnect it first."} System.Exception {System.InvalidOperationException}
Grid grid = new Grid();
Grid.Children.Add(plot); here i get error.
Mar 20, 2015 at 6:33 AM
Hi

Detaching the items from prarent gird and attaching again to parent grid, these are little bit difficult to do,

the easy way is use printdialog.PrintVisual(gridName, "docName");

before that you may need to scale it based on (use scale transform and layoutTransform).

Regards
Ravi
Oct 29, 2015 at 9:30 AM
Hi

I'm having a similar problem with multiple graphs used in a tab control and I'm calling the SaveScreenshot method for each ChartPlotter but the only screenshots that work are when the current tab control view is set to the graphs.
I saw the plotter.PerformLoad() method but it that method doesn't seem to be in any part of the library.
I've tried various things like UpdateLayout(), InvalidateVisual(), Focus() and FitToView() but can't seem to get it working

Am I going wrong anywhere? Any help would be very much appreciated.

Regards
Iain