Reputation: 51
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.
Upvotes: 2
Views: 1103
Reputation: 51
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.
Upvotes: 3