在内存中渲染LiveCharts图表并保存为图像



我正在尝试执行以下操作:

  • 在内存中创建livechart笛卡尔图
  • 将图表添加到网格
  • 将标签添加到同一网格
  • 将网格添加到Viewbox
  • 将视图框渲染为PNG
  • 将PNG保存到磁盘

以上应从背景中的另一个线程运行以允许UI重新负责。

尽管这看起来很简单,但我一直在努力获得适当的工作解决方案。以下问题相关:

  • livechart(位于查看框内(需要时间渲染
  • 因此,在尝试将其保存为图像之前,需要给出图表以完成渲染的时间
  • 我找到了使用hwndsource的代码,但是它并非始终工作(大约有95%的时间工作(。没有hwndsource修改,它永远无法工作(总是没有任何图表,上面没有任何图表(
  • 在不同的UI线程中运行Run((函数不起作用,因为我会收到以下错误消息:WPF调度程序{"调用线程无法访问此对象,因为不同的线程拥有它。"}

所以我的问题是:

  • 等待LiveChart/Grid/Viewbox组合的正确方法是在保存作为图像之前完成渲染的方法?也许使用加载事件?请注意,我试图推动它,但在我遇到"线程"问题时无法使其起作用。
  • 如何在其他UI线程中运行整个过程?

请参阅下文有关代码

public void Run()
(
   //Create Livechart which is a child of a Grid control
   Grid gridChart = Charts.CreateChart();
   //Creates a ViewBox control which has the grid as its child
   Viewbox viewBox = WrapChart(gridChart,1400,700);
   //Creates and saves the image
   CreateAndSaveImage(viewBox ,path,name);
)

下面是创建视图框并添加网格为子

的函数
public Viewbox viewBox WrapChart(Grid grid,int width,int height)
{
    chart.grid.Width = width;
    chart.grid.Height = height;
    viewbox.Child = chart.grid;
    viewbox.Width = width;
    viewbox.Height = height;
    viewbox.Measure(new System.Windows.Size(width, height));
    viewbox.Arrange(new Rect(0, 0, width, height));
    viewbox.UpdateLayout();
}

函数下面创建并保存图像

public void CreateAndSaveImage(Viewbox viewbox,string folderPath,string fileName)
{
    var x = HelperFunctions.GetImage(viewbox);
    System.IO.FileStream stream = System.IO.File.Create(folderPath + fileName);
    HelperFunctions.SaveAsPng(x, stream);
    stream.Close();   
}

以下代码将视图框呈现到图像。请注意,这是我能找到的唯一等待图表完成加载的代码。我不知道它是如何工作的,但是有95%的时间。有时图表仍未完成加载。

public static RenderTargetBitmap GetImage(Viewbox view)
{
      using (new HwndSource(new HwndSourceParameters())
      {
          RootVisual =
                           (VisualTreeHelper.GetParent(view) == null
                                ? view
                                : null)
      })
      {
          Size size = new Size(view.ActualWidth, view.ActualHeight);
          if (size.IsEmpty)
              return null;

          int actualWidth = Convert.ToInt32(size.Width);
          int requiredWidth = Convert.ToInt32(size.Width * 1);
          int actualHeight = Convert.ToInt32(size.Height);
          int requiredHeight = Convert.ToInt32(size.Height * 1);
          // Flush the dispatcher queue
          view.Dispatcher.Invoke(DispatcherPriority.SystemIdle, new Action(() => { }));
          var renderBitmap = new RenderTargetBitmap(requiredWidth, requiredHeight,
                                                          96d * requiredWidth / actualWidth, 96d * requiredHeight / actualHeight,
                                                          PixelFormats.Pbgra32);

          DrawingVisual drawingvisual = new DrawingVisual();
          using (DrawingContext context = drawingvisual.RenderOpen())
          {
              context.DrawRectangle(new VisualBrush(view), null, new Rect(new Point(), size));
              context.Close();
          }
          renderBitmap.Render(view);
          renderBitmap.Freeze();
          return renderBitmap;
      }

}

以下代码保存位图作为文件

的图片
public static void SaveAsPng(BitmapSource src, Stream outputStream)
{
      PngBitmapEncoder encoder = new PngBitmapEncoder();
      encoder.Frames.Add(BitmapFrame.Create(src));
      encoder.Save(outputStream);
}

以下代码是我用来在其他线程中运行整个内容的方法。请注意,它无法正常工作,因为我收到以下错误消息:

WPF调度程序{"调用线程无法访问此对象,因为 一个不同的线程拥有它。"}。

请注意,如果我正常执行Run(((没有任何单独的线程(,则有时该图表无法正确渲染(如前所述(。

Thread thread = new Thread(() =>
{
       Run();
       System.Windows.Threading.Dispatcher.Run();
});
       thread.SetApartmentState(ApartmentState.STA);
       thread.Start();

尝试将此行拨打为图表:

    this.chart.Model.Updater.Run(false, true);

此行更新图表,并且保存到图像时始终可见。

相关内容

  • 没有找到相关文章