WPF CompositionTarget.Rendering/Animation Responsiveness

Check out the following small program:

  public partial class App : Window {

    [STAThread]

    public static void Main(string[] args) {

      var win = new App();

      Application app = new Application();

      app.Run(win);

    }

    public App() {

      var canvas = new Canvas() { Background = Brushes.White };

      Content = canvas;

      initA(canvas);

    }

    public void initA(Canvas canvas) {

      var rect = new Rectangle() { Width = 50, Height = 50, Fill = Brushes.Red };

      canvas.Children.Add(rect);

      canvas.MouseMove += (x, y) => {

        if (y.RightButton == MouseButtonState.Pressed) {

          var p = y.GetPosition(canvas);

          Canvas.SetLeft(rect, p.X - 25);

          Canvas.SetTop(rect, p.Y - 25);

          y.Handled = true;

        }

      };

      CompositionTarget.Rendering += (x,y) => {};

    }

  }

If you comment out the last line, the red rectangle will mostly stay synched with the mouse when the mouse is moving in the canvas with its right button down. With the last line in, the slack is considerable and very noticeable.

I did some instrumentation in the empty Rendering handler. It’s being called 100 times every 800-1000 milliseconds (one update per ~8-10 milliseconds, computed in chunks of 100). If I replace CompositionTarget.Rendering with DispatchTimer set at 10 milliseconds interval, then mouse tracking is much better (the mouse pointer will stay inside the rectangle) but the Tick handler is only being called once per ~15 milliseconds.

It gets worse:

I just tried running an animation..say spinning the rectangle while it is moving. It seems like the animation has the same effect as CompositionTarget.Rendering, so…if I want to keep the rectangle in synch with the mouse, you have to not do any animations, yikes! Actually, you could do some animation using a dispatch timer, as long as its cranking along at one update per 15 milliseconds, the rectangle will still spin and the mouse pointer will stay inside the rectangle boundaries.

So to maintain responsiveness and eliminate slack:

  1. <!--[if !supportLists]-->Don’t add a CompositionTarget.Rendering event handler.
  2. Don't do any frame-rate independent animation either.
  3. Only use DispatchTimer, but don’t do too much work in there either.