0

I'm writing a GUI application to display/graph a series of graphs. When the application starts up it automatically reads the raw data from a specified location and automatically graphs and display them. I'm adding the capability to automatically save the graphs to file after they are displayed on screen.

The way I'm doing the save is to grab the onscreen pixels into a bitmap and save the bitmap:

Bitmap memoryImage = new Bitmap(picCube.Size.Width, picCube.Size.Height);
Graphics g = Graphics.FromImage(memoryImage);
Point plotLoc = picBox.PointToScreen(Point.Empty);
g.CopyFromScreen(plotLoc.X, plotLoc.Y, 0, 0, picBox.Size);
memoryImage.Save(savePath, ImageFormat.Jpeg);

The restriction is this method only works after the graphs have already been displayed on screen. I have a menu item to do the save and it works without problem since the application has already been displayed on screen in order to get to the menu. Now that I'm adding an automatic save capability I need to be able to trigger this save action only AFTER the form has been fully displayed on screen.

I'm currently putting in a timer in the form's Load event that goes off in N seconds, which trigger the save action. But for some reason it doesn't work, probably because the timer is in a different thread than the GUI thread which means the timer thread cannot access the GUI elements.

Is there a better way to do this simple task without resorting to overly complicated approaches like background worker?

John Saunders
  • 160,644
  • 26
  • 247
  • 397
Thomas Nguyen
  • 464
  • 1
  • 5
  • 16
  • 2
    The better way is to get the chart objects to save image files directly instead of taking screenshots! – David Heffernan Oct 17 '13 at 17:54
  • Which Timer? there are like 3 timers, the UI timer object (draggable from tools) fires on the UI Thread and should work – Alan Oct 17 '13 at 17:58
  • @SLaks - I'm on .Net 4.0, cannot move to 4.5 :( – Thomas Nguyen Oct 17 '13 at 17:58
  • I have edited your title. Please see, "[Should questions include “tags” in their titles?](http://meta.stackexchange.com/questions/19190/)", where the consensus is "no, they should not". – John Saunders Oct 17 '13 at 17:59
  • @DavidHeffernan - these are picture boxes not chart objects (if that makes a difference). Can you post sample code on how to get the picture boxes to save themselves (instead of taking screenshots)? – Thomas Nguyen Oct 17 '13 at 18:01
  • 1
    If you have code that can paint the picture box, you can make that code paint alternatively to a different graphics device, e.g. a bitmap. Or always paint first to a bitmap and if you need to show to picture box then blit the bitmap onto it. Double buffering will help reduce flicker. – David Heffernan Oct 17 '13 at 18:02
  • @Alan - I'm using System.Timers.Timer. You mean I should use System.Windows.Forms.Timer? – Thomas Nguyen Oct 17 '13 at 18:04
  • Yes, use `System.Windows.Forms.Timer`. Or use `System.Timers.Timer` and set the [SynchronizingObject](http://msdn.microsoft.com/en-us/library/system.timers.timer.synchronizingobject.aspx) equal to the form. That will cause the timer elapsed event to execute on the UI thread. – Jim Mischel Oct 17 '13 at 18:15
  • I think, you need to flip your logic. Definitely, save the bitmap first, then display it in the picture box. You can create and save bitmap on background thread, and let know the app thread when it is done, so the bitmaps can get displayed. – T.S. Oct 17 '13 at 18:16
  • @ThomasNguyen Yeah for something like that it would be easier, and the tick event happens on the UI thread for you – Alan Oct 17 '13 at 18:16
  • Just checked and my code already created the bitmap first which is used to set the picture box. But when I do a picCube.Image.Save(...) it comes out wrong. Certain colors are inverted (white background becomes black, gray axes become white, etc.) while other colors are the same (captions, plot colors, etc.). Any idea why? BTW, now I know why the previous developer did a screen grab instead of saving the picCube.Image directly :( – Thomas Nguyen Oct 17 '13 at 18:37
  • @DavidHeffernan - your solution is the most elegant and would be best but unfornately it doesn't work in my case :( – Thomas Nguyen Oct 17 '13 at 21:59
  • @JimMischel - your solution System.Timers.Timer with SynchronizingObject does work very well for me. Thanks! – Thomas Nguyen Oct 17 '13 at 22:00
  • Of course it works. You just have to do it right. – David Heffernan Oct 17 '13 at 22:01

1 Answers1

0

The form's Load is not the last even to fire when a form loads up.

Maybe try System.Windows.Forms.Form.Shown

Check out winforms event loading order: Winforms - order of Load and Activated events

That way you won't have to deal with issues where you depend on the local machine's timing.

Community
  • 1
  • 1
L_7337
  • 2,650
  • 28
  • 42
  • thanks for the link, that's good read. I experimented with the Shown event and it doesn't do what I need, which is taking a screenshot of the fully displayed form. When I took a screenshot from inside the OnShown() event handler I got a blank image which means the Shown event is fired before the form is fully displayed onscreen. – Thomas Nguyen Oct 17 '13 at 23:16