How to resolve synchronisation problem with Helix Toolkit rendering on WPF composite render thread

江枫思渺然 提交于 2021-01-29 06:37:44

问题


C#, WPF, Helix Toolkit. I am trying to generate a bitmap from a HelixViewport3D and have encountered a few problems.

The first problem is that I cannot find a way to render off-screen. There are a few references to this online (e.g. here) and as far as I can see it does not have a built-in solution.

As a somewhat sub-optimal workaround I have proceeded to render to the screen where the user can see it, with the intention of creating a bitmap from that rendered image. I now have the problem that an image exported from the content on screen (e.g. using Viewport3DHelper.SaveBitmap) is blank if I call it immediately. I understand that this is because Helix Toolkit is rendering the image in the WPF composite render thread, so there is no image to grab at the time I try to grab it, because it has not yet been rendered.

I am not aware of a 'render complete' event that I can subscribe to. Is there one?

If not, would a workaround perhaps be to use thread priorities in order to make my code lower priority, so that it waits for the rendering to complete before continuing?

<Window x:Class=".MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:Test"
        xmlns:HelixToolkit="clr-namespace:HelixToolkit.Wpf;assembly=HelixToolkit.Wpf" xmlns:h="http://helix-toolkit.org/wpf"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Grid>
            <h:HelixViewport3D x:Name="helixPlot" Width="450" Height="450"/>
        </Grid>
        <StackPanel Orientation="Horizontal">
            <Button Name ="btnGo" Height="25" Content="Render" VerticalAlignment="Bottom" Click="Go_Click"/>
            <Button Name ="btnTemp" Height="25" Content="Write PNG" VerticalAlignment="Bottom" Click="Test_Click"/>
        </StackPanel>
    </Grid>
</Window>

using System.Collections.Generic;
using System.Windows;
using HelixToolkit.Wpf;
using System.Windows.Media.Media3D;
using System.Windows.Media;

namespace Test
{

    class Foo
    {
        public List<Point3D> points;
        public Foo()
        { // constructor creates three arbitrary 3D points
            points = new List<Point3D>() { new Point3D(0, 0, 0), new Point3D(1, 0, 0), new Point3D(0, 0, 1) };
        }
    }

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
        private void renderImages()
        {

            Foo bar = new Foo(); // create object with three 3D points

            DrawStuff(bar.points); // plot to helixViewport3D control ('points' = list of 3D points)
            helixPlot.CameraController.ZoomExtents();

            // This results in a blank image because image not yet rendered...
            Viewport3DHelper.SaveBitmap(helixPlot.Viewport, @"E:\test.png", null, 4, BitmapExporter.OutputFormat.Png);


        }
        private void DrawStuff(List<Point3D> points)
        {
            
            Point3DCollection dataList = new Point3DCollection();
            PointsVisual3D cloudPoints = new PointsVisual3D { Color = Colors.Red, Size = 5.0f };
            foreach (Point3D p in points)
            {
                dataList.Add(p);
            }
            cloudPoints.Points = dataList;

            // Add geometry to helixPlot. It renders asynchronously in the WPF composite render thread...
            helixPlot.Children.Add(cloudPoints);

        }

        // When this is clicked we render image and (try to) save to file...
        private void Go_Click(object sender, RoutedEventArgs e)
        {
            renderImages();
        }

        // To demonstrate that the image export is not the problem.
        // This works if the image has been rendered already...
        private void Test_Click(object sender, RoutedEventArgs e)
        {
            Viewport3DHelper.SaveBitmap(helixPlot.Viewport, @"E:\test.png", null, 4, BitmapExporter.OutputFormat.Png);
        }
    }
}

回答1:


I managed to find a solution thanks to this answer:

Dispatcher.BeginInvoke(new Action(() => DoSomething()), DispatcherPriority.ContextIdle, null);

The Action will be called once the rendering is done.

So in your case you could use it like this:

private void renderImages()
{

    Foo bar = new Foo(); // create object with three 3D points

    this.DrawStuff(bar.points, SaveHelixPlotAsBitmap /*the action you want to perform once the rendering is done*/);    
}

private void DrawStuff(List<Point3D> points, Action renderingCompleted)
{
    //Draw everyting you need ...

    Dispatcher.BeginInvoke(renderingCompleted, DispatcherPriority.ContextIdle);
}

private void SaveHelixPlotAsBitmap()
{
    helixPlot.CameraController.ZoomExtents();
    Viewport3DHelper.SaveBitmap(helixPlot.Viewport, @"E:\test.png", null, 4, BitmapExporter.OutputFormat.Png);
}


来源:https://stackoverflow.com/questions/63339910/how-to-resolve-synchronisation-problem-with-helix-toolkit-rendering-on-wpf-compo

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!