c# - 如何在 SelectionChanged 上运行昂贵的进程,但在快速滚动时却不行。(延迟事件处理程序)?
问题描述
每次触发 DataGrid 的 SelectionChanged 事件时,我都必须运行一个相当慢的任务。
我遇到的问题是我需要保持应用程序的响应性,如果用户使用箭头键快速滚动,那么我不想为每个项目执行任务。只有他们停下来的项目。(我希望这是有道理的!)
我已经设置了一个非常基本的示例来演示,它在 DataGrid 中显示单词列表,然后当您滚动浏览它们时,它会将它们添加到 ListView。
这是我到目前为止所尝试的:
CancellationTokenSource cts;
private bool loading;
private async void dgData_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (loading)
{
cts.Cancel();
return;
}
cts = new CancellationTokenSource();
loading = true;
var x = dgData.SelectedItem.ToString();
await Task.Run(async () =>
{
Thread.Sleep(1000); //Wait a second to see if scrolling quickly...
await ExpensiveProcess(x);
});
loading = false;
}
private async Task ExpensiveProcess(string text)
{
if (cts.IsCancellationRequested)
{
loading = false;
return;
}
await Task.Factory.StartNew(() =>
{
//Expensive process will be done here...
});
Application.Current.Dispatcher.Invoke(() =>
{
lvwItems.Items.Add(text);
});
loading = false;
}
这似乎起作用的事实是,如果箭头快速向下它会错过项目,但是当我停在一个并希望它运行时它不起作用?
我哪里错了?这甚至是最好的方法吗?非常感谢任何建议,并乐于提供更多信息。先感谢您。
更新:
我在 YouTube 上找到了一个视频,它建议这样做,这正如我所期望的那样工作,所以现在我要这样做,但现在将问题留待任何反馈。
创建一个计时器,它将运行昂贵的过程并将间隔设置为低但不会太慢以便按键。
var myTimer = new DispatcherTimer();
myTimer.Interval = TimeSpan.FromSeconds(1);
myTimer.Tick += MyTimer_Tick
在计时器的滴答事件上运行长时间运行的进程。
private void MyTimer_Tick(object sender, EventArgs e)
{
var x = dgData.SelectedItem.ToString();
Task.Run(async () =>
{
Thread.Sleep(1000); //Needs to be removed
await ExpensiveProcess(x);
});
}
然后在常规的 SelectionChanged 事件中简单地停止和启动计时器。也不要忘记在漫长的过程结束时停止计时器。
解决方案
您可以在事件处理程序中启动一个计时器,SelectionChanged
然后在计时器结束时检查该项目是否仍处于选中状态。
如果是,则使用 a 调用长时间运行的方法,CancellationToken
如果发生另一个选择,则取消该方法。
下面的示例代码应该给你的想法:
private CancellationTokenSource _cts = null;
...
dataGrid.SelectionChanged += async(ss, ee) =>
{
//cancel any previous long running operation
if (_cts != null)
{
_cts.Cancel();
_cts.Dispose();
}
_cts = new CancellationTokenSource();
//store a local copy the unique id or something of the currently selected item
var id = (dataGrid.SelectedItem as TestItem).Id;
//wait a second and a half before doing anything...
await Task.Delay(1500);
//if no other item has been selected since {id} was selected, call the long running operation
if (_cts != null && id == (dataGrid.SelectedItem as TestItem).Id)
{
try
{
await LongRunningOperation(id, _cts.Token);
}
finally
{
_cts.Cancel();
_cts.Dispose();
_cts = null;
}
}
};
推荐阅读
- javascript - Invariant Violation:试图注册两个同名的视图RNCAndroidDropdownPicker
- python - BeautifulSoup 元素解析器
- wpf - 从 Datagrid 组合框单元格中获取所选项目
- microsoft-graph-api - Microsoft Graph - 使用 delta 跟踪驱动器更改不返回共享文件的人
- delphi - Indy TIdFTP SSL 未知证书
- java - 带有警报 showAndWait 的应用程序启动方法中的异常
- python - 使用 callproc 调用存储过程并没有真正在 postgresql 中实现?
- angular - 访问外部的路由参数
- reactjs - 反应:TypeError:无法读取未定义的属性“renderInput”
- typescript - WebStorm 调试器没有接受一些 TypeScript 更改