如何在视图模型中调用异步方法

本文关键字:调用 异步方法 模型 视图 | 更新日期: 2023-09-27 18:06:45

我正在尝试学习WPF应用程序中的MVVM模式。我在我的视图模型中编写了这个异步方法(它必须是异步的,因为我使用HttpClient,它的方法是异步的):

public async Task<Dictionary<int, BusStop>> GetBusStops()
    {
        var busStopDict = new Dictionary<int, BusStop>();
        var url = "my url";
        using (HttpClient client = new HttpClient())
        using (HttpResponseMessage response = await client.GetAsync(url))
        using (HttpContent content = response.Content)
        {
            string data = await content.ReadAsStringAsync();
            var regularExpression = Regex.Match(data, "''[(.)*'']");
            var result = regularExpression.Groups[0];
            var json = JValue.Parse(result.ToString());
            var jsonArray = json.ToArray();
            foreach (var a in jsonArray)
            {
                // irrelevant logic
                busStopDict.Add(nr, bs);
            }
        }
        return busStopDict;
    }

这个方法返回一个包含公交车站(我的模型)的字典。我想绑定这个字典与组合框在视图,但我不能得到它的工作,因为我不能调用这个异步方法在我的视图模型的构造器,我不知道在哪里我可以调用它。你有什么建议吗?

如何在视图模型中调用异步方法

我不建议在视图模型构造函数中编写逻辑。相反,我会在你的视图中创建一个Loaded事件触发器,以确保你不会干扰视图的加载过程。

xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"

<i:Interaction.Triggers>
    <i:EventTrigger EventName="Loaded">
        <i:InvokeCommandAction Command="{Binding LoadedCommand}" />
    </i:EventTrigger>
</i:Interaction.Triggers>

然后在您的视图模型中,我建议执行以下操作:

为已加载的事件添加以下属性

public DelegateCommand LoadedCommand { get; }

然后在构造函数

中赋值
LoadedCommand = new DelegateCommand(async () => await ExecuteLoadedCommandAsync());

添加加载的方法并在其中调用你的方法

private async Task ExecuteLoadedCommandAsync()
{
    var busStops = await GetBusStops();
    //TODO: display the busStops or do something else
}

此外,添加"Async"作为后缀到异步方法名称是一个很好的命名模式。它使您能够快速查看哪些方法是异步的。(将GetBusStops重命名为GetBusStopsAsync)

这是一个简单的DelegateCommand实现

public class DelegateCommand : ICommand
{
    private readonly Predicate<object> _canExecute;
    private readonly Action<object> _execute;
    public event EventHandler CanExecuteChanged;
    public DelegateCommand(Action<object> execute) 
               : this(execute, null)
    {
    }
    public DelegateCommand(Action<object> execute, 
               Predicate<object> canExecute)
    {
        _execute = execute;
        _canExecute = canExecute;
    }
    public override bool CanExecute(object parameter)
    {
        if (_canExecute == null)
        {
            return true;
        }
        return _canExecute(parameter);
    }
    public override void Execute(object parameter)
    {
        _execute(parameter);
    }
    public void RaiseCanExecuteChanged()
    {
        if( CanExecuteChanged != null )
        {
            CanExecuteChanged(this, EventArgs.Empty);
        }
    }
}

使用此实现时,您需要将视图模型构造函数中DelegateCommand的初始化更改为以下

LoadedCommand = new DelegateCommand(async (param) => await ExecuteLoadedCommandAsync());

应该使用异步数据绑定(我有一篇关于这个主题的文章)。

使用NotifyTask从我的Mvvm。Async库,它看起来像这样:

public async Task<Dictionary<int, BusStop>> GetBusStopsAsync() { ... }
public NotifyTask<Dictionary<int, BusStop>> BusStops { get; }
MyViewModelConstructor()
{
  BusStops = NotifyTask.Create(() => GetBusStopsAsync());
}

然后您的视图可以模型绑定到BusStops.Result以获得字典(或null,如果还没有检索到),并且还可以数据绑定到BusStops.IsNotCompleted/BusStops.IsFaulted用于繁忙的旋转器/错误指示器。

在构造函数中启动async方法,并定义一个要继续执行的操作。

//Constructor
public ViewModel()
{
    GetBusStops().ContinueWith((BusStops) =>
    {
        //This anonym method is called async after you got the BusStops
        //Do what ever you need with the BusStops
    });
}

如果你想用

访问View的属性,不要忘记调用UI线程
Application.Current.Dispatcher.BeginInvoke(() =>
{
    //Your code here
});

我会检查 asynclazy 或检查AsyncCommands并创建一个基于"LoadCommand"的异步任务。你不应该在构造函数中加入太多的逻辑,因为这会使调试变得更加困难,迫使你进行强耦合,并且会使为视图模型编写单元测试变得非常困难。如果可以的话,我倾向于让一切都变得懒惰。

AsyncLazy
http://blog.stephencleary.com/2012/08/asynchronous-lazy-initialization.html

AsyncCommand
http://mike-ward.net/2013/08/09/asynccommand-implementation-in-wpf/

微软的MVVM社区工具包有一个实现,并引用了名称NotifyTaskCompletion,与Stephen Cleary 2014年关于异步数据绑定的微软文章中的名称相同。

(Stephen Cleary也维护了他自己的实现,链接到他对同一问题的回答)