我有这个场景,在我的Windows Phone App中。
我有一个函数,当提供PhoneNumber时,需要返回联系人的DisplayName。
public string GetDisplayName(string number)
{
Contacts contactsBook = new Contacts();
contactsBook.SearchCompleted += contactsBook_SearchCompleted;
contactsBook.SearchAsync(number, FilterKind.PhoneNumber, null);
//return .....; How will I be able to return the display name here?
}
由于SearchAsync返回类型为void,我不能在这里使用await关键字。那我怎么能在这里设计这个功能呢?
这里推荐使用一些线程信令方法的模式吗? (我会在SearchAsync方法之后等待事件,我将在SearchCompleted事件中触发事件)
原因,我想要这样一个功能是因为,我从服务器获取了一组数字,我会在UI中显示相应的名称。
<phone:LongListSelector ItemsSource="{Binding PhoneNumberCollection}">
<phone:LongListSelector.ItemTemplate>
<DataTemplate>
<Grid>
<TextBlock Text="{Binding PhoneNumber, Converter={StaticResource PhoneNumberToNameConverter}}" />
</Grid>
</DataTemplate>
</phone:LongListSelector.ItemTemplate>
</phone:LongListSelector>
我可以使用下面的IValueConverter转换器
public class PhoneNumberToNameConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return ContactsManager.Instance.GetDisplayName((string)value);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotSupportedException();
}
}
答案 0 :(得分:3)
由于SearchAsync返回类型为void,我不能在这里使用await关键字。那我怎么能在这里设计这个功能呢?
SearchAsync
返回void
,因为它是EAP方法。您可以convert it to a TAP method等待。我更喜欢将这样的TAP包装器转换为扩展方法,如下所示:
public static Task<IEnumerable<Contact>> SearchTaskAsync(this Contacts contacts, string filter, FilterKind filterKind)
{
var tcs = new TaskCompletionSource<IEnumerable<Contact>>();
EventHandler<ContactsSearchEventArgs> subscription;
subscription = (_, args) =>
{
contacts.SearchComplete -= subscription;
try
{
tcs.TrySetResult(args.Results);
}
catch (Exception ex)
{
tcs.TrySetException(ex);
}
}
contacts.SearchComplete += subscription;
contacts.SearchAsync(filter, filterKind, null);
return tcs.Task;
}
编写SearchTaskAsync
TAP扩展方法后,您可以这样调用它:
private async Task<string> GetDisplayNameAsync(string number)
{
Contacts contactsBook = new Contacts();
var matches = await contactsBook.SearchTaskAsync(number, FilterKind.PhoneNumber);
...
return name;
}
但是,这并没有真正解决如何在UI中显示异步数据的问题。答案(如果你考虑的话)就是你当然不能。
相反,您必须设计您的UI,以便它在尚未获得结果时理解(例如,显示微调器),然后在搜索完成时更新它。
请注意,使用值转换器启动是很棘手的。这是有道理的;你真的不希望值转换器开始后台操作! (你可以 hack it to work,但结果不是很好。)
因此,我建议您使用单独的VM属性作为显示名称。您可以使用NotifyTaskCompletion
from my AsyncEx library来简化此操作。以下是一个如何运作的例子:
public string Number
{
get { return _number; }
set
{
_number = value;
NumberDisplayName = NotifyTaskCompletion.Create(GetDisplayNameAsync(value));
RaisePropertyChanged("Number");
RaisePropertyChanged("NumberDisplayName");
}
}
public INotifyTaskCompletion<string> NumberDisplayName
{
get; private set;
}
然后您的绑定可以使用NumberDisplayName.Result
来显示结果(如果搜索尚未完成,则使用空字符串)。如果你想要一个微调器或诸如此类的东西,你可以使用其他路径,例如NumberDisplayName.IsCompleted
;如果你想要正确的错误处理,也有相应的路径(例如,NumberDisplayName.ErrorMessage
)。