Search code examples
c#wpfhelix-3d-toolkit

Saving an image from Helix Tookit


C#, WPF, Helix Toolkit. I am trying to save an image from a HelixViewport3D viewport (as described here) and am still having problems.

Intended method: Render image to viewport and simply save that using Viewport3DHelper.SaveBitmap() or similar method.

Viewport3DHelper.SaveBitmap(helixPlot.Viewport, @"E:\test.bmp", null, 4, BitmapExporter.OutputFormat.Png);

The problem with that: It runs but the captured image is blank. My best guess is that Helix Toolkit is rendering asynchronously(?) There is no image there to grab at the time that I am saving it.

Possible workaround 1: Respond to an event once rendering is complete.

The problem with that: There is no suitable event to subscribe to.

Possible workaround 2: Add an in-line delay to allow the image to render before saving it. (Clunky and unreliable, but would be a step in the right direction).

Thread.Sleep(2000);
Viewport3DHelper.SaveBitmap(helixPlot.Viewport, @"E:\test.bmp", null, 4, BitmapExporter.OutputFormat.Png);

The problem with that: The image doesn't get displayed until my code execution completes. If I add a delay of 10 seconds, I just get a blank screen for an additional 10 seconds.

Possible workaround 3: Add a timer so that the image will be rendered and I can then save the image from the timer elapsed event.

System.Timers.Timer timer = new System.Timers.Timer();
...
timer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
timer.Interval = 2000; // two seconds
timer.Start();
....
private void OnTimedEvent(object source, ElapsedEventArgs e)
{
    Viewport3DHelper.SaveBitmap(helixPlot.Viewport, @"E:\test.bmp", null, 4, BitmapExporter.OutputFormat.Png);
}

The problem with that: Although the image is rendered successfully and is there for the taking, the timer is running in a different thread to I cannot reference the HelixPlot control from the event handler. I get:

System.InvalidOperationException: 'The calling thread cannot access this object because a different thread owns it.'

Possible workaround 4: Run the timer in the UI thread using Timer.SynchronizingObject

The problem with that: It doesn't seem to be possible to do this easily/simply in WPF. I can do it with a lot of additional code and at the moment this may be my best available option, but it seems strongly sub-optimal for reasons of verbosity and lack of robustness.

Is there not some reasonably straightforward way of achieving what I am trying to do?

The root of the problem seems to be that the image is not rendered to the viewport until the calling method has finished executing. I don't really understand why this is.

EDIT: I have been told on another forum that Helix Toolkit is rendering on the WPF composite render thread. However, after some Googling I am still none the wiser as to how I can wait for this rendering to complete.


Solution

  • I got an answer to this here:

    Asynchronous operations within a loop - how to keep control of execution?

    There doesn't seem to be a completion event available, so the only way around it that I have been able to find is to use async.