views:

289

answers:

2

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 ?

A: 

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;
}
Ray Burns
Hi RayThanks for your answer ,I will try it soon.
Michael Hao
I need use hittesting to highlight every visual . If I display them using a single DrawingVisual , I think that it maybe not to be done.
Michael Hao
In that case, put each data item in its own DrawingVisual and use RenderOpen() and DrawingContext. I've edited my answer to show how this would be done.
Ray Burns
Your Update looks like my existing code .It's performance is very poor.
Michael Hao
I want to know if I can highlight one Geometry in the hittested visual .
Michael Hao
Ive been experimenting with drawing large numbers of polylines, and found that StreamGeometry was the _slowest_ performing option. Fastest was to create a Shape for each polyline, and slower was creating a DrawingVisual with each polyline drawn into it using DrawingContext.DrawGeometry.Mind you, this is a competition where all options are painfully slow. WPF just doesnt seem to be capable of drawing a lot of squiggly lines (hoping to be corrected).
mackenir
A: 

Hi Ray:

I need use hittesting to highlight every visual . If I display them using a single DrawingVisual , I think that it maybe not to be done.

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

Michael Hao