我有一个需要对SizeChanged
事件作出反应的WPF窗口。但是,它应该仅在500毫秒时段内没有其他SizeChanged
事件时执行处理(类似于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);
}
但是,我注意到,当在调试模式下编译为x64时,此代码会导致UI在调整大小时开始滞后;重新绘制的窗口中有明显的延迟。我假设这是由于OperationCanceledException
被序列化,抛出并被UI线程捕获。下面的代码消除了这个问题:
Task.Delay(500, lastCts.Token).ContinueWith(
_ =>
{
myTextBox.Text = string.Format("({0},{1})", this.Width, this.Height);
},
lastCts.Token,
TaskContinuationOptions.NotOnCanceled,
TaskScheduler.FromCurrentSynchronizationContext());
我的问题是:如果等待的任务没有被取消,是否有一种干净的方法来配置异步方法以仅恢复UI线程上的处理?或者这是边界情况之一,由于SizeChanged
事件的频率,我们不应该使用等待,而是恢复到提供更多控制的旧ContinueWith
模式(如{{1} })?
答案 0 :(得分:4)
它应该只在500毫秒期间没有进一步的SizeChanged事件时执行处理
一旦你有"时间"要求,这是一个非常明确的标志,你应该使用Rx。这样的事情应该有效:
Observable.FromEventPattern<SizeChangedEventHandler, SizeChangedEventArgs>(h => SizeChanged += h, h => SizeChanged -= h)
.Throttle(TimeSpan.FromMilliseconds(500))
.ObserveOn(SynchronizationContext.Current)
.Subscribe(_ =>
{
myTextBox.Text = string.Format("({0}, {1})", this.Width, this.Height);
});