R – Improve drawingvisual render’s speed

performancewpf

I create my own FrameworkElement and override VisualChildrenCount{get;} and GetVisualChild(int index) by returning my own DrawingVisual collection instance.I have override OnRender .

I will add 20-50 DrawingVisuals in this FrameworkElement ,every DrawingVisual will have 2000 line segments.The logic value of these points between 0 to 60000.when I zoom into 1:1 the FrameworkElement 's Height will be 60000, the rending time will be 15 minutes!!

How do I improve the rending performance ?

Best Answer

For this kind of data volume, I would suggest you build a GeometryDrawing and StreamGeometry containing a single PolyLine for each of your points. Then combine them all together in a single DrawingGroup and display it using a single DrawingVisual.

This would be the XAML:

<DrawingVisual Drawing="{Binding CurrentDrawing}" />

and this would be the code to update CurrentDrawing:

var group = new DrawingGroup();
foreach(var data in myData)
{
  StreamGeometry geo = new StreamGeometry();
  using(geoContext = geo.Open())
  {
    geoContext.BeginFigure(myData.StartPoint, false, false);
    geoContext.PolyLineTo(myData.AdditionalPoints, true, false);
  }
  group.Add(new GeometryDrawing
  {
    Geometry = geo,
    Pen = myData.Pen,
  });
}
CurrentDrawing = group;
...

If your data is changing it may be advantageous to create store each GeometryDrawing object separately so it is only necessary to recreate those GeometryDrawings whose source data has changed.

Update

You mention in your comment that you need to separately hittest each of the 20-50 data items. In that case, you probably do want to use a separate DrawingVisual for each. For maximum performance you will want to use RenderOpen() with DrawingContext:

IEnumerable<Visual> BuildVisuals()
{
  return
    from var data in myData
    select BuildVisualForData(data);
}

void BuildVisualForData(MyDataType data)
{
  var geo = new StreamGeometry();
  using(geoContext = geo.Open())
  {
    geoContext.BeginFigure(myData.StartPoint, false, false);
    geoContext.PolyLineTo(myData.AdditionalPoints, true, false);
  }

  var visual = new DrawingVisual();
  using(drawingContext = visual.RenderOpen())
  {
    drawingContext.DrawGeometry(null, myData.Pen, geo);
  }
  return visual;
}