如何从属性设置器调用异步方法 [英] How to call an async method from a property setter

查看:59
本文介绍了如何从属性设置器调用异步方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是我的问题:
我在属性Filter上绑定了WPF TextBox.它用作过滤器:每次更改TextBox.Text时,都会设置Filter属性.

Here is my problem:
I have a WPF TextBox binded on the property Filter. It works as a filter: each time the TextBox.Text changes, the Filter property is set.

<TextBox Text="{Binding Filter, UpdateSourceTrigger=PropertyChanged, Mode=OneWayToSource}" />

现在,在ViewModel上有我的Filter属性:每次过滤器更改时,我都会更新我的值.

Now on the ViewModel there is my Filter property: each time the filter changes I update my values.

private string _filter;
public string Filter
{
    get { return _filter; }
    set
    {
        _filter = value;
        // call to an async WEB API to get values from the filter
        var values = await GetValuesFromWebApi(_filter);
        DisplayValues(values);
    }
}

public async Task<string> GetValuesFromWebApi(string query)
{
    var url = $"http://localhost:57157/api/v1/test/result/{query}";
    // this code doesn't work because it is not async
    // return await _httpClient.GetAsync(url).Result.Content.ReadAsStringAsync();
    // better use it this way
    var responseMessage = await _httpClient.GetAsync(url);
    if (responseMessage.IsSuccessStatusCode)
    {
        return await responseMessage.Content.ReadAsStringAsync();
    }
    else
    {
        return await Task.FromResult($"{responseMessage.StatusCode}: {responseMessage.ReasonPhrase}");
    }
}

由于不允许使用async属性,如果需要调用async方法,我该怎么做才能使绑定起作用?

As it is not allowed to use async property, what could I do to make my binding working if it needs to call an async method?

推荐答案

我将假定DisplayValues方法实现正在更改绑定到UI的属性,并且为了进行演示,我将假定它是List<string>:

I will assume that DisplayValues method implementation is changing a property that is bound to the UI and for the demonstration I will assume it's a List<string>:

private List<string> _values;

public List<string> Values
{
    get
    {  
        return _values;
    }
    private set 
    {
        _values = value;
        OnPropertyChange();
    }
}

它是绑定:

<ListBox ItemsSource="{Binding Values}"/>

现在,正如您所说的那样,不允许属性设置器异步,因此我们必须使其同步,而我们可以做的是将Values属性更改为某种类型,这将隐藏来自异步方法的数据实现细节,并以同步方式构造此类型.

Now as you said it is not allowed to make property setters async so we will have to make it sync, what we can do instead is to change Values property to some type that will hide the fact it's data comming from asynchronous method as an implementation detail and construct this type in a sync way.

NotifyTask Mvvm.Async 库中的a>可以帮助我们,我们要做的是将Values属性更改为:

NotifyTask from Stephen Cleary's Mvvm.Async library will help us with that, what we will do is change Values property to:

private NotifyTask<List<string>> _notifyValuesTask;

public NotifyTask<List<string>> NotifyValuesTask
{
    get
    {  
        return _notifyValuesTask;
    }
    private set 
    {
        _notifyValuesTask = value;
        OnPropertyChange();
    }
}

并更改其绑定:

<!-- Busy indicator -->
<Label Content="Loading values" Visibility="{Binding notifyValuesTask.IsNotCompleted,
  Converter={StaticResource BooleanToVisibilityConverter}}"/>
<!-- Values -->
<ListBox ItemsSource="{Binding NotifyValuesTask.Result}" Visibility="{Binding
  NotifyValuesTask.IsSuccessfullyCompleted,
  Converter={StaticResource BooleanToVisibilityConverter}}"/>
<!-- Exception details -->
<Label Content="{Binding NotifyValuesTask.ErrorMessage}"
  Visibility="{Binding NotifyValuesTask.IsFaulted,
  Converter={StaticResource BooleanToVisibilityConverter}}"/>

这样,我们创建了一个属性,该属性表示为数据绑定而定制的Task相似类型,包括忙碌指示器和错误传播,以及NotifyTask用法的更多信息. microsoft.com/en-us/magazine/dn605875.aspx"rel =" nofollow noreferrer>此MSDN文章(请注意,NotifyTask被认为是NotifyTaskCompletion).

This way we created a property that represents a Task alike type that is customized for databinding, including both busy indicator and errors propagation, more info about NotifyTask usage in this MSDN articale (notice that NotifyTask is consider there as NotifyTaskCompletion).

现在,最后一部分是更改Filter属性设置器,以在每次更改过滤器时将notifyValuesTask设置为新的NotifyTask,并进行相关的异步操作(无需执行任何操作,所有监视都已嵌入在其中) NotifyTask):

Now the last part is to change Filter property setter to set notifyValuesTask to a new NotifyTask every time the filter is changed, with the relevant async operation (no need to await anything, all the monitoring is already embedded in NotifyTask):

private string _filter;

public string Filter
{
    get 
    { 
        return _filter; 
    }
    set
    {
        _filter = value;
        // Construct new NotifyTask object that will monitor the async task completion
        NotifyValuesTask = NotifyTask.Create(GetValuesFromWebApi(_filter));
        OnPropertyChange();
    }
}

您还应该注意,GetValuesFromWebApi方法将阻塞,这将冻结您的UI,在两次调用GetAsync使用await之后,您不应使用Result属性:

You should also notice that GetValuesFromWebApi method blocks and it will make your UI freeze, you shouldn't use Result property after calling GetAsync use await twice instead:

public async Task<string> GetValuesFromWebApi(string query)
{
    var url = $"http://localhost:57157/api/v1/test/result/{query}";
    using(var response = await _httpClient.GetAsync(url))
    {
        return await response.Content.ReadAsStringAsync();
    }
}

这篇关于如何从属性设置器调用异步方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆