I have a WPF window that needs to react to SizeChanged
events. However, it should only perform processing when there are no further SizeChanged
events for a 500 ms period (similar to the behaviour offered by BindingBase.Delay
).
private CancellationTokenSource lastCts;
private async void Window_SizeChanged(object sender, SizeChangedEventArgs e)
{
if (lastCts != null)
lastCts.Cancel();
lastCts = new CancellationTokenSource();
try
{
await Task.Delay(500, lastCts.Token);
}
catch (OperationCanceledException)
{
return;
}
myTextBox.Text = string.Format("({0}, {1})", this.Width, this.Height);
}
However, I have noticed that, when compiled as x64 under Debug mode, this code causes to UI to start lagging when being resized; there are perceptible delays in the window getting redrawn. I assume that this is due to the OperationCanceledException
getting serialized, thrown, and caught on the UI thread. The code below eliminates the problem:
Task.Delay(500, lastCts.Token).ContinueWith(
_ =>
{
myTextBox.Text = string.Format("({0},{1})", this.Width, this.Height);
},
lastCts.Token,
TaskContinuationOptions.NotOnCanceled,
TaskScheduler.FromCurrentSynchronizationContext());
My question is: Is there a clean way of configuring an async method to only resume processing on the UI thread if the awaited task wasn't cancelled? Or is this one of the border cases where, due to the frequency of the SizeChanged
events, we should not use await, but revert to the old ContinueWith
pattern which affords more control (like TaskContinuationOptions.NotOnCanceled
)?