具有取消/重叠呼叫处理的WPF MVVM异步工作方法

时间:2016-09-23 15:29:53

标签: .net wpf asynchronous backgroundworker

我有一个从数据库加载数据的详细视图(用于列表选择),我不想通过将数据库逻辑从UI线程移开来避免任何阻塞。

的问题:

  • 使其成为异步(简单部分,在完成后调度在UI部件上更新viewmodel属性的逻辑)
  • 当用户(例如按住向下键)在后一个线程完成最后一个条目之前发出加载新条目时处理
    • 在这种情况下,以前的加载方法应该是可取消的(例如CanellationToken)
    • 必须放弃为旧选择启动的调用的结果,并且不会到达UI,特别是因为如果调用在稍后启动的加载操作之后完成,则可能会覆盖最新数据

如何使用WPF执行此操作?如果没有内置的方法,我的想法就是为它创建一个类,并将它用于viewmodel中的这些加载方法。例如。想到两个变体的伪代码

    public class ViewModelBackgroundLoader<TInput, TResult>
    {
        public ViewModelBackgroundLoader(Func<TInput, CancellationToken, TResult> loadFunc, Action<TResult> uiContinuation)
        {
        }
        public void Load(TInput input)
        {
            // set cancellation for previous loadFunc
            // async await loadAction on threadpool thread
            // If not cancelled... 
            //   uiContinuation() on UI thread
        }
    }

    public class ViewModelBackgroundLoadedProperty<TInput, TResult> : INotifyPropertyChanged
    {
        public ViewModelBackgroundLoadedProperty(Func<TInput, CancellationToken, TResult> loadFunc)
        {
        }
        public TInput Input
        {
            set
            {
                // set cancellation for running loadFunc
                // async await loadAction on threadpool thread
                // If not cancelled... 
                //   Update Result property and fire propertychanged (in UI thread)
            }
        }
        public TResult Result { get; }
    }

1 个答案:

答案 0 :(得分:0)

如果您使用我编写的名为NotifyTask<T>的辅助类:

,这非常简单
public class ViewModel<TInput, TResult>
{
  private CancellationTokenSource _cts;

  public NotifyTask<TResult> Operation { get; private set; }

  public void Load(TInput input)
  {
    if (_cts != null)
      _cts.Cancel();
    _cts = new CancellationTokenSource();
    Operation = NotifyTask.Create(loadFunc(input, _cts.Token));
  }
}

可以对Operation.Result进行数据绑定(还有其他数据绑定属性可以轻松显示/隐藏加载指示符等)。

如您所述CancellationTokenSource,取消之前的操作(如果有的话)。没有必要显式检查并避免旧操作​​的UI更新,因为Operation一旦新的启动就会被覆盖(因此永远不会显示旧数据 - 即使未被取消,也只会被忽略)。