wpf 使用 MVVM 在中继命令中运行异步方法

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/31582931/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-13 13:17:41  来源:igfitidea点击:

Running an async method inside a relaycommand using MVVM

c#wpfmvvmasync-await

提问by pvdev

I'm developing an asynchronous application using WPF and MVVM, but I can't seem to get an async method to run inside my relaycommand.

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

I have a button on my WPF viewhooked up to a relaycommand in my viewmodel, which trys to call an async method in my modelto return a list of results:

我的 WPF视图上有一个按钮连接到我的viewmodel 中的中继命令,它尝试调用我的模型中的异步方法以返回结果列表:

/// <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;
    }
}

However I get the following exception when the relaycommand tries to execute:

但是,当中继命令尝试执行时,我收到以下异常:

An unhandled exception of type 'System.AggregateException' occurred in mscorlib.dll

Additional information: A Task's exception(s) were not observed either by Waiting on the Task or accessing its Exception property. As a result, the unobserved exception was rethrown by the finalizer thread.

mscorlib.dll 中发生类型为“System.AggregateException”的未处理异常

附加信息:等待任务或访问其异常属性都未观察到任务的异常。结果,未观察到的异常被终结器线程重新抛出。

I've tried a number of things in this threadto try and resolve the issue with no luck. Does anyone know how to resolve this?

我在这个线程中尝试了很多东西来尝试解决这个问题,但没有运气。有谁知道如何解决这个问题?

采纳答案by pvdev

Ok so I actually managed to resolve the issue by making a change to the way I run my async method.

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

I changed this:

我改变了这个:

var results = await DockFindModel.SearchAsync();

To this:

对此:

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

Although i'm a bit confused as to why I need to await Task.Run() when relaycommand is already accepting async lambda. I'm sure it'll become clear with time however.

尽管我对为什么在relaycommand 已经接受异步lambda 时需要等待Task.Run() 感到有些困惑。不过,我相信随着时间的推移它会变得清晰。

Thanks to those who commented.

感谢评论的人。

回答by Cameron MacFarland

Not sure where your RelayCommand is coming from (MVVM framework or custom implementation) but consider using an async version.

不确定您的 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();
        }
    }
}

回答by Tom

That RelayCommandclass is probably accepting a parameter of type Action. Since you are passing an async lambda, we are having an async voidscenario here. Your code will be fired and forgot. Consider using an ICommand implementation that takes Func<Task>as a parameter instead of Action.

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