1

我有这种情况,在我的 Windows Phone 应用程序中。

当提供电话号码时,我有一个函数需要返回联系人的 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();
    }
}
4

1 回答 1

3

由于 SearchAsync 返回类型为 void,我不能在这里使用 await 关键字。那么我怎么能在这里设计这个功能呢?

SearchAsync返回void,因为它是一个EAP方法。您可以将其转换为等待的 TAP 方法。我更喜欢将这样的 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;
}

一旦你写了一个SearchTaskAsyncTAP 扩展方法,你可以这样调用它:

private async Task<string> GetDisplayNameAsync(string number)
{
  Contacts contactsBook = new Contacts();
  var matches = await contactsBook.SearchTaskAsync(number, FilterKind.PhoneNumber);
  ...
  return name;
}

但是,这并不能真正解决如何在 UI 中显示异步数据的问题。答案(如果你仔细想想)当然是你不能。

相反,您必须设计您的 UI,以便它能够理解何时还没有结果(例如,显示一个微调器),然后在搜索完成时更新它。

请注意,使用值转换器来实现这一点很棘手。这是有道理的;您真的不希望值转换器启动后台操作!(你可以 破解它来工作,但结果并不漂亮)。

因此,我建议您使用单独的 VM 属性,即显示名称。您可以使用NotifyTaskCompletion我的 AsyncEx 库来简化此操作。这是如何工作的一个示例:

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)。

于 2013-11-09T14:30:00.737 回答