这是我的问题:
我在属性Filter上绑定了一个WPF TextBox。它用作过滤器:每次TextBox.Text更改时,都会设置Filter属性。
<TextBox Text="{Binding Filter, UpdateSourceTrigger=PropertyChanged, Mode=OneWayToSource}" />
现在在ViewModel上有我的Filter属性:每次过滤器更改时我都会更新我的值。
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}");
}
}
由于不允许使用异步属性,如果需要调用异步方法,我该怎么做才能使绑定工作?
答案 0 :(得分:2)
我将假设DisplayValues方法实现正在更改绑定到UI的属性,对于演示,我将假设它是import shutil
import datetime
import fnmatch
import pandas as pd
DF = pd.DataFrame(columns=['Text'])
DF.at[1,'Text'] = "##################"
DF.at[2,'Text'] = "#"
DF.at[3,'Text'] = "##################"
DF.at[4,'Text'] = " "
DF.at[5,'Text'] = "Headers = Y"
DF.at[6,'Text'] = "Compress = Zip"
DF.at[7,'Text'] = "Column_Delimter =,"
DF.at[8,'Text'] = "Progress = 10000"
DF.at[9,'Text'] = "Filename = test.txt"
DF.at[10,'Text'] = " "
DF.at[11,'Text'] = "SQL: MultiStatement"
DF.at[12,'Text'] = " "
DF.at[13,'Text'] = "SQL QUERY GOES HERE"
print(DF.at[7,'Text'])
DF.to_csv('VCI.txt', index = False, header=False)
:
List<string>
它的绑定:
private List<string> _values;
public List<string> Values
{
get
{
return _values;
}
private set
{
_values = value;
OnPropertyChange();
}
}
现在正如你所说,不允许将属性设置器设置为异步,因此我们必须使其同步,而我们可以做的是将Values属性更改为某种类型,这将隐藏它的数据传递的事实从异步方法作为实现细节,并以同步方式构造此类型。
来自Stephen Cleary的NotifyTask
库的Mvvm.Async将帮助我们,我们将做的是将Values属性更改为:
<ListBox ItemsSource="{Binding Values}"/>
改变它的约束力:
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}}"/>
类似,包括忙碌指标和错误传播,this MSDN articale中有关Task
用法的更多信息(通知NotifyTask
将其视为NotifyTask
)。
现在最后一部分是更改Filter属性setter,以便每次更改过滤器时将notifyValuesTask设置为新的NotifyTaskCompletion
,并使用相关的异步操作(不需要NotifyTask
任何内容,所有监视已嵌入await
):
NotifyTask
您还应该注意到GetValuesFromWebApi方法会阻止它,并且会冻结您的用户界面,在调用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();
}
}
两次使用Result
后,您不应该使用GetAsync
属性:
await
答案 1 :(得分:0)
你可以这样做。请注意,在“async void”中,您需要处理所有异常。如果不这样做,应用程序可能会崩溃。
public class MyClass: INotifyPropertyChanged
{
private string _filter;
public string Filter
{
get { return _filter; }
set
{
RaisePropertyChanged("Filter");
_filter = value;
}
}
public MyClass()
{
this.PropertyChanged += MyClass_PropertyChanged;
}
private async void MyClass_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(Filter))
{
try
{
// call to an async WEB API to get values from the filter
var values = await GetValuesFromWebApi(Filter);
DisplayValues(values);
}
catch(Exception ex)
{
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}