I am making simple graph control in wpf
. And I can't explain nor fix performance problem: it's too slow compared to winforms. Perhaps I am doing something wrong.
I prepare demo to demonstrate the problem.
Here is test control:
public class Graph : FrameworkElement
{
private Point _mouse;
private Point _offset = new Point(500, 500);
public Graph()
{
Loaded += Graph_Loaded;
}
private void Graph_Loaded(object sender, RoutedEventArgs e)
{
// use parent container with background to receive mouse events too
var parent = VisualTreeHelper.GetParent(this) as FrameworkElement;
if (parent != null)
parent.MouseMove += (s, a) => OnMouseMove(a);
}
protected override void OnRender(DrawingContext context)
{
// designer bugfix
if (DesignerProperties.GetIsInDesignMode(this))
return;
Stopwatch watch = new Stopwatch();
watch.Start();
// generate some big figure (try to vary that 2000!)
var radius = 1.0;
var figures = new List<LineSegment>();
for (int i = 0; i < 2000; i++, radius += 0.1)
{
var segment = new LineSegment(new Point(radius * Math.Sin(i) + _offset.X, radius * Math.Cos(i) + _offset.Y), true);
segment.Freeze();
figures.Add(segment);
}
var geometry = new PathGeometry(new[] { new PathFigure(figures[0].Point, figures, false) });
geometry.Freeze();
var pen = new Pen(Brushes.Black, 5);
pen.Freeze();
context.DrawGeometry(null, pen, geometry);
// measure time
var time = watch.ElapsedMilliseconds;
Dispatcher.InvokeAsync(() =>
{
Window.GetWindow(this).Title = string.Format("{0:000}ms; {1:000}ms", time, watch.ElapsedMilliseconds);
}, DispatcherPriority.Loaded);
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
var mouse = e.GetPosition(this);
if (e.LeftButton == MouseButtonState.Pressed)
{
// change graph location
_offset.X += mouse.X - _mouse.X;
_offset.Y += mouse.Y - _mouse.Y;
InvalidateVisual();
}
// remember last mouse position
_mouse = mouse;
}
}
Here is how to use it in xaml:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525" WindowState="Maximized">
<Grid Background="White">
<local:Graph/>
</Grid>
</Window>
Some remarks: control will draw figure, which can be moved by mouse:
It will display 2 measurements in title: first is how long it took for OnRender()
to complete and second one is how long actual rendering took (first invoke after render).
Try to vary that 2000
: setting 1000
makes moving comfortable, 3000
is like half-second delay before figure is redrawn (on my PC).
Questions:
- Is it good to use
InvalidateVisual()
to update graph offset in MouseMove
? And if bad, what is the right technique to invalidate?
- Freezes, there are many of them without any noticeable effect. Do I need to use them or not?
- It looks like it takes only
5ms
to complete render, but moving subjectively takes much longer (200ms+). Why is that?
And main question is of course performance, why is it so terrible? I could draw few hundred thousands of lines in winform control until it become as sloppy, as mine wpf control does with just 1000... =(
I found an answer on last question. Measuring of rendering time doesn't works correctly when moving with mouse. But if window is resized, then second time become 300ms
(on my PC with 2000
figures). So it's not a wrong mouse invalidate (first question), but indeed very slow rendering.
See Question&Answers more detail:
os