如何在ViewModel中调用异步方法

r9s*_*r9s 1 c# wpf asynchronous mvvm

我正在尝试在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;
    }
Run Code Online (Sandbox Code Playgroud)

此方法返回一个充满公交车站的字典(我的模型)。我想将此字典与view中的combobox绑定,但是我无法使其工作,因为我无法在viewmodel的构造函数中调用此异步方法,并且我不知道在哪里可以调用它。你有什么建议吗?

NtF*_*reX 5

我不建议在您的viewmodel构造函数中编写逻辑。相反,我还将在您的视图中创建一个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>
Run Code Online (Sandbox Code Playgroud)

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

为您的Loaded事件添加以下属性

public DelegateCommand LoadedCommand { get; }
Run Code Online (Sandbox Code Playgroud)

然后在您的构造函数中分配它

LoadedCommand = new DelegateCommand(async () => await ExecuteLoadedCommandAsync());
Run Code Online (Sandbox Code Playgroud)

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

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

此外,将“ 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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

使用此实现时,您需要将DelegateCommandviewmodel构造函数中的初始化更改为以下内容

LoadedCommand = new DelegateCommand(async (param) => await ExecuteLoadedCommandAsync());
Run Code Online (Sandbox Code Playgroud)