dharmatech
dharmatech

Reputation: 9527

Mouse activity slowing down WPF graphics demo

Below is a small graphics demo in WPF. It looks something like this:

enter image description here

When it's running, if I move the mouse around the main area of the window, the animation slows down (at least on my system). If I move the mouse pointer out of the window entirely, the animation come back up to speed.

Any suggestions for how to prevent the mouse movement interfering with the animation speed?

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Threading;
using System.Windows.Threading;

namespace WPF_Golden_Section
{
    class DrawingVisualElement : FrameworkElement
    {
        public DrawingVisual visual;

        public DrawingVisualElement() { visual = new DrawingVisual(); }

        protected override int VisualChildrenCount { get { return 1; } }

        protected override Visual GetVisualChild(int index) { return visual; }
    }

    public static class CanvasUtils
    {
        public static Canvas SetCoordinateSystem(this Canvas canvas, Double xMin, Double xMax, Double yMin, Double yMax)
        {
            var width = xMax - xMin;
            var height = yMax - yMin;

            var translateX = -xMin;
            var translateY = height + yMin;

            var group = new TransformGroup();

            group.Children.Add(new TranslateTransform(translateX, -translateY));
            group.Children.Add(new ScaleTransform(canvas.ActualWidth / width, canvas.ActualHeight / -height));

            canvas.RenderTransform = group;

            return canvas;
        }
    }

    public static class ColorUtils
    {
        public static Color Write(this Color color)
        {
            Console.Write("Color[{0} {1} {2} {3}]", color.A, color.R, color.G, color.B);

            return color;
        }

        static byte ColorComponentToByte(double n)
        {
            return 
                (byte)
                    Math.Round(Math.Min(Math.Max(n, 0), 1) * 255);
        }

        public static Color Rgb(double r, double g, double b)
        {
            return
                Color.FromRgb(
                    ColorComponentToByte(r),
                    ColorComponentToByte(g),
                    ColorComponentToByte(b));
        }

        public static Color SetArgb(this Color color, double a, double r, double g, double b)
        {
            color.A = ColorComponentToByte(a);
            color.R = ColorComponentToByte(r);
            color.G = ColorComponentToByte(g);
            color.B = ColorComponentToByte(b);

            return color;
        }
    }

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            Width = 600;
            Height = 600;

            var dockPanel = new DockPanel();

            Content = dockPanel;

            var slider =
                new Slider()
                {
                    Minimum = 0.1,
                    Maximum = 10,
                    LargeChange = 0.001,
                    Value = 1
                };

            dockPanel.Children.Add(slider);

            DockPanel.SetDock(slider, Dock.Top);

            var incrementSlider =
                new Slider()
                {
                    Maximum = 0.001,
                    Minimum = 0.00001,
                    Value = 0.0001
                };

            dockPanel.Children.Add(incrementSlider);


            DockPanel.SetDock(incrementSlider, Dock.Top);

            var pauseButton = new Button() { Content = "Pause" };

            dockPanel.Children.Add(pauseButton);

            DockPanel.SetDock(pauseButton, Dock.Top);

            var canvas = new Canvas();

            dockPanel.Children.Add(canvas);

            DockPanel.SetDock(canvas, Dock.Top);

            SizeChanged += (s, e) => canvas.SetCoordinateSystem(-400, 400, -400, 400);

            var element = new DrawingVisualElement();

            Action draw = () =>
                {
                    canvas.Children.Clear();

                    // var element = new DrawingVisualElement();

                    using (var dc = element.visual.RenderOpen())
                    {
                        for (var i = 0.0; i < 720.0; i += slider.Value)
                        {
                            var radius = Math.Sin(i / 720.0 * Math.PI) * 30;

                            var phi = (1 + Math.Sqrt(5)) / 2;

                            var omega = 2 * Math.PI * (phi - 1) * i;

                            var x = Math.Cos(omega) * 0.5 * i;
                            var y = Math.Sin(omega) * 0.5 * i;

                            dc.DrawEllipse(
                                new SolidColorBrush(ColorUtils.Rgb(i / 360.0, i / 360.0, 0.25)),
                                new Pen(Brushes.Black, Math.Max(radius / 6, 1)),
                                new Point(x, y),
                                radius / 2,
                                radius / 2);
                        }
                    }

                    canvas.Children.Add(element);
                };

            slider.ValueChanged += (s, e) =>
            {
                Title = slider.Value.ToString() + "     " + incrementSlider.Value;
                draw();
            };

            var timer = new DispatcherTimer();

            timer.Tick += (s, e) =>
                {
                    if (slider.Value < 10.0)
                        slider.Value += incrementSlider.Value;
                };

            timer.Start();

            pauseButton.Click += (s, e) =>
                {
                    if (timer.IsEnabled)
                        timer.Stop();
                    else
                        timer.Start();
                };

            slider.Value = 3.5;
            incrementSlider.Value = 0.00001;
        }
    }
}

Upvotes: 0

Views: 1300

Answers (2)

Grafix
Grafix

Reputation: 726

If you use the third Animation option in WPF, CompositionTarget.Rendering event, then I found no slowdown. I have adjusted the code in your sample from the line with slider.ValueChanged:

        slider.Value = 3.5;
        incrementSlider.Value = 0.00001;

        EventHandler renderer = (s, e) =>
        {
            if (slider.Value < 10.0)
                slider.Value += incrementSlider.Value;

            //Title = slider.Value.ToString() + "     " + incrementSlider.Value;
            draw();
            _frameCounter++;
        };

        CompositionTarget.Rendering += renderer;
        pauseButton.Click += (sender, args) => CompositionTarget.Rendering -= renderer;

        var timer = new DispatcherTimer();
        timer.Interval = TimeSpan.FromMilliseconds(1000);
        timer.Tick += (s, e) =>
                          {
                              Title = string.Format("fps: {0}", _frameCounter);
                              _frameCounter = 0;
                          };

        timer.Start();

CompositionTarget.Rendering fires at a fixed 60 fps rate unless heavy machine load. You can find code on the internet to adjust for framedrops and avoid visible slow downs.

I have no good explanation for the slow down due to the mouse activity in your example. My first thought would be that it is a bottleneck in the routed event infra, but adjusting the timer interval or refactoring by extracting the draw method from the ValueChanged event did not seem to solve the problem.

BTW I liked your sample.

Upvotes: 3

AndrewS
AndrewS

Reputation: 6094

Could be the hit testing. You could try setting the IsHitTestVisible to false on the Canvas.

Upvotes: 0

Related Questions