使用MVVM在relaycommand中运行异步方法

本文关键字:运行 异步方法 relaycommand MVVM 使用 | 更新日期: 2023-09-27 18:04:07

我正在使用WPF和MVVM开发异步应用程序,但我似乎无法在我的relaycommand中运行异步方法。

我有一个按钮在我的WPF 视图连接到一个relaycommand在我的视图模型,它试图调用异步方法在我的模型返回结果列表:

/// <summary>
/// Search results 
/// </summary>
private ObservableCollection<string> _searchResults = new ObservableCollection<string>(); 
public IList<string> SearchResults
{
    get { return _searchResults; }
}
/// <summary>
/// Search button command
/// </summary>
private ICommand _searchCommand;
public ICommand SearchCommand
{
    get
    {
        _searchCommand = new RelayCommand(
            async() =>
            {
                SearchResults.Clear();
                var results = await DockFindModel.SearchAsync(_selectedSearchableLayer, _searchString);
                foreach (var item in results)
                {
                    SearchResults.Add(item);
                }                    
                //notify results have changed
                NotifyPropertyChanged(() => SearchResults);
            },
            () => bAppRunning); //command will only execute if app is running
        return _searchCommand;
    }
}

然而,当relaycommand试图执行时,我得到以下异常:

类型为"System"的未处理异常。AggregateException'发生在mscorlib.dll

附加信息:也未观察到任务异常通过等待任务或访问其Exception属性。作为一个结果,未观察到的异常被终结器线程重新抛出。

我在这篇文章中尝试了很多事情,试图解决这个问题,但没有运气。有人知道怎么解决这个问题吗?

使用MVVM在relaycommand中运行异步方法

不确定你的RelayCommand来自哪里(MVVM框架或自定义实现),但考虑使用异步版本。

public class AsyncRelayCommand : ICommand
{
    private readonly Func<object, Task> execute;
    private readonly Func<object, bool> canExecute;
    private long isExecuting;
    public AsyncRelayCommand(Func<object, Task> execute, Func<object, bool> canExecute = null)
    {
        this.execute = execute;
        this.canExecute = canExecute ?? (o => true);
    }
    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }
    public void RaiseCanExecuteChanged()
    {
        CommandManager.InvalidateRequerySuggested();
    }
    public bool CanExecute(object parameter)
    {
        if (Interlocked.Read(ref isExecuting) != 0)
            return false;
        return canExecute(parameter);
    }
    public async void Execute(object parameter)
    {
        Interlocked.Exchange(ref isExecuting, 1);
        RaiseCanExecuteChanged();
        try
        {
            await execute(parameter);
        }
        finally
        {
            Interlocked.Exchange(ref isExecuting, 0);
            RaiseCanExecuteChanged();
        }
    }
}

好的,所以我实际上设法通过改变我运行异步方法的方式来解决这个问题。

我改了这个:

var results = await DockFindModel.SearchAsync();

:

var results = await QueuedTask.Run(() => DockFindModel.SearchAsync());

虽然我有点困惑,为什么我需要等待Task.Run()时,relaycommand已经接受async lambda。我相信随着时间的推移,一切都会变得清晰。

感谢那些评论的人。

RelayCommand类可能接受类型为Action的参数。由于您正在传递一个异步lambda,因此我们在这里有一个async void场景。您的代码将被触发并忘记。考虑使用以Func<Task>作为参数而不是以Action作为参数的iccommand实现。