2

Can anyone shed some light on why a WPF window lags when you move your mouse quickly over it? Is there any way around this? A change to the window render settings perhaps?

The following code runs a smooth animation of the rectangle, until you start moving your mouse over the window where the framerate will drop drastically (confirmed using the Application Profiler in VS). Also happens in a Release version with no debugger.

I've read Why does the Dispatcher Timer in WPF lag when i hover over my Application Window? which suggests using a different timer to update the underlying data. I've tried System.Threading.Timer, System.Timers.Timer and System.Windows.Threading.DispatcherTimer, along with creating a new thread to update the value in a loop with a Thread.Sleep. All of these provide the same result, so I don't think it actually has anything to do with the timers per se.

CodeBehind:

    public partial class MainWindow : Window, INotifyPropertyChanged
{
    private int _value;
    public int Value { get => _value; set { _value = value; RaisePropertyChangedEvent(); } }
    public event PropertyChangedEventHandler PropertyChanged;
    public Timer Timer { get; set; }

    public MainWindow()
    {
        InitializeComponent();
        DataContext = this;

        Timer = new Timer((o) =>
        {
            Value = Value > 100 ? -100 : Value + 1;
        }, null, 0, 10);
    }

    protected void RaisePropertyChangedEvent([CallerMemberName] String propertyName = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

XAML:

<Window x:Class="MouseLagTest.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:MouseLagTest"
    mc:Ignorable="d"
    Title="MainWindow" Height="450" Width="800">
<Grid>
    <Rectangle Width="50" Height="50" Fill="Red">
        <Rectangle.RenderTransform>
            <TranslateTransform X="{Binding Value}"/>
        </Rectangle.RenderTransform>
    </Rectangle>
</Grid>

EDIT This issue seems to occur when built against .Net Framework 4.X and .Net Core. If built using Framework 3.X it runs super smooth irrespective of mouse movements. I'd prefer a solution as using 3.X isn't an option.

Necrov
  • 51
  • 6
  • 1
    try set IsHitTestVisible="False" on your Rectange and remove grid (since you dont need it in current example) – Demon Sep 17 '21 at 06:49
  • @Demon Thanks but that doesn't change anything. The same thing also occurs if you use a textbox with the text bound to Value, it'll lag update when moving the mouse. Given how basic this example is, surely this issue affects all WPF applications??? – Necrov Sep 19 '21 at 21:00
  • Added to OP, "This issue seems to occur when built against .Net Framework 4.X and .Net Core. If built using Framework 3.X it runs super smooth irrespective of mouse movements. I'd prefer a solution as using 3.X isn't an option." – Necrov Sep 19 '21 at 21:29
  • while not directly an answer to your question, using periodic `Task` looks like it is not affected from mouse movements. – mcy Sep 24 '21 at 09:02
  • @mcy Thanks for that tip, it seems if you create a periodic Task on the main GUI thread (ie, in a loop with .ConfigureAwait(true)) then it remains smooth. Same as if you use System.Windows.Threading.DispatcherTimer. Any update to values from a different thread (ie, System.Threading.Timer, running the task in it's own thread or using ConfigureAwait(false)) then you get laggy updates of the window. Considering the data in my application is actually updated asynchronously via a TCP socket on a background thread (not a timer at all), I don't see any easy way to resolve this :( – Necrov Oct 05 '21 at 22:00

1 Answers1

3

So it seems that when you call PropertyChanged from a background thread, the WPF binding automatically marshals the update onto the GUI thread for the update.

WPF Databinding thread safety?

For some reason, I think this is where the lag occurs when quickly moving the mouse, probably due to event priority in the Dispatcher queue.

If you manually marshall the update onto the GUI thread it fixes the issue.

Ie for a specific property update:

Timer = new Timer((o) =>
{
    Application.Current.Dispatcher.BeginInvoke(new Action(() =>
    {
        Value = Increment(Value);
    }));
}, null, 0, 10);

Or in general:

protected void RaisePropertyChangedEvent(String propertyName = "")
{
    Application.Current.Dispatcher.BeginInvoke(new Action(() =>
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }));
}

Performing either of the above will give smooth updates irrespective of mouse movements. If using a timer, you could also just use a DispatcherTimer so the call is performed at the top of the Dispatcher loop on the main thread. https://learn.microsoft.com/en-us/dotnet/api/system.windows.threading.dispatchertimer?view=windowsdesktop-5.0

Happy to hear some advice if there is a better way to achieve this, or any caveats involved.

Necrov
  • 51
  • 6
  • I was using the DispatcherTimer which also caused a lag. However, using the System.Threading.Timer with Application.Current.Dispatcher.BeginInvoke() worked great - thanks. – LoC Jan 19 '23 at 16:42