3

现在对显示的人员/联系人没有问题- 连续几次现在它们都显示了 - 但是检索联系人对象的调用永远不会返回 - 断点在这一行:

var contact = await contactPicker.PickSingleContactAsync();

...我从来没有到达那里 - 虽然我可以选择一个联系人,然后选择“选择”按钮。控制永远不会从模拟器切换到断点。

为了充分披露,这里是整个方法:

private async Task<System.Collections.Generic.KeyValuePair<string, string>> SelectAContactForASlot()
{
    KeyValuePair<string, string> kvp;
    var contactPicker = new Windows.ApplicationModel.Contacts.ContactPicker();
    contactPicker.CommitButtonText = "Select";
    var contact = await contactPicker.PickSingleContactAsync();
    if (contact != null)
    {
        kvp = new KeyValuePair<string, string>(contact.Name, contact.Emails[0].Value);
        if (null != kvp.Value) // If there is no email, there's no use in passing it back, as the person cannot be contacted
        {
            return kvp;
        }
        else
        {
            return kvp = new KeyValuePair<string, string>(contact.Name, "No email found");
        }
    }
    return kvp = new KeyValuePair<string, string>("No Name found", "No email found");
}

那么为什么对 PickSingleContactAsync() 的调用永远不会返回呢?

更新

某个功能的所有按钮共享一个 OnClick 处理程序(我应该进入 21.08 世纪并使用 OnTapped 处理程序);这个处理程序像这样调用异步方法:

Task<System.Collections.Generic.KeyValuePair<string, string>> kvpContactNameEmail = SelectAContactForASlot();

更新 2

好吧,现在我真的被迷住了:

这段代码,在按钮 Onclick 中:

private void PersonButton_OnClick(object sender, RoutedEventArgs args)
{
    Task<System.Collections.Generic.KeyValuePair<string, string>> kvpContactNameEmail = SelectAContactForASlot();
    Button btn = null;
    if (args.OriginalSource is Button)
    {
        btn = args.OriginalSource as Button;
    }
    if (null == btn)
    {
        return;
    }
    //btn1_1 to btn3_4
    if (btn.Name == "btn1_1")
    {
        ApplicationData.Current.LocalSettings.Values["PersonSection1Slot1"] = kvpContactNameEmail.Result.Key;
        btn1_1.Foreground = new SolidColorBrush(Windows.UI.Colors.Gray);
    . . .

...确实通过了对 SelectAContactForASlot() 的调用——单步执行第一行会立即返回——但它会在人员/联系人选择器显示之前返回。因此,KVP 中没有键或值,因为它返回的 SelectAContactForASlot() 应该返回:

    private async Task<System.Collections.Generic.KeyValuePair<string, string>> SelectAContactForASlot()
    {
            KeyValuePair<string, string> kvp;
            var contactPicker = new Windows.ApplicationModel.Contacts.ContactPicker();
            contactPicker.CommitButtonText = "Select";
            var contact = await contactPicker.PickSingleContactAsync();
            if (contact == null)
            {
                kvp = new KeyValuePair<string, string>("No Name found", "No email found");
            }
            else // contact != null
            {
                if (string.IsNullOrWhiteSpace(contact.Emails[0].Value))
                {
                    kvp = new KeyValuePair<string, string>(contact.Name, "No email found");
                }
                else
                {
                    kvp = new KeyValuePair<string, string>(contact.Name, contact.Emails[0].Value);
                }
            }
            return kvp;
}

...由于某种原因,人员/联系人选择器在到达此行时显示(假设我单击了 btn1_1):

ApplicationData.Current.LocalSettings.Values["PersonSection1Slot1"] = kvpContactNameEmail.Result.Key;

...但是,实际上没有返回任何内容,因为它停止在线:

var contactPicker = new Windows.ApplicationModel.Contacts.ContactPicker();

所以 SelectAContactForASlot() 被调用的时间比我想象的/应该的要晚,但是一旦它被调用,它就无法继续。

更新 3

    private void PersonButton_OnClick(object sender, RoutedEventArgs args)
    {
        Task<System.Collections.Generic.KeyValuePair<string, string>> kvpContactNameEmail = SelectAContactForASlot();
    . . .

    private async Task<KeyValuePair<string, string>> SelectAContactForASlot()
    {
        KeyValuePair<string, string> kvp;
        var contactPicker = new Windows.ApplicationModel.Contacts.ContactPicker();
        contactPicker.CommitButtonText = "Select";
        var contact = await contactPicker.PickSingleContactAsync();
        if (contact == null)
        {
            kvp = new KeyValuePair<string, string>("No Name found", "No email found");
        }
        else // contact != null
        {
            if (string.IsNullOrWhiteSpace(contact.Emails[0].Value))
            {
                kvp = new KeyValuePair<string, string>(contact.Name, "No email found");
            }
            else
            {
                kvp = new KeyValuePair<string, string>(contact.Name, contact.Emails[0].Value);
            }
        }
        return kvp;
    }

更新 4

...如果我将 buttonClick 代码更改为:

private async void PersonButton_OnClick(object sender, RoutedEventArgs args)
{
    Task<System.Collections.Generic.KeyValuePair<string, string>> kvpContactNameEmail = await SelectAContactForASlot();
    . . .

(在这里添加了“async”和“await”),我得到,“不能将类型'System.Collections.Generic.KeyValuePair'隐式转换为'System.Threading.Tasks.Task>'

我想我的问题确实是我不希望调用 SelectAContactForASlot() 直到它检索到联系人对象(调用联系人应用程序并且用户选择联系人);IOW,我希望它是异步的。但我认为没有 PickSingleContactAsync() 的异步版本 - IOW,没有“PickSingleContact”。

所以我真正想知道的是:我怎样才能调用联系人应用程序并允许用户选择一个联系人(已经这样做了)并从该对象中取回我可以在我的代码中访问的值?显然,我对这一切都是错误的。

4

2 回答 2

3

我猜在您的调用层次结构中的某个时刻,您正在阻塞Task返回SelectAContactForASlot的方法或调用它的方法。

这将导致死锁,因为完成SelectAContactForASlot时必须在 UI 上下文上恢复await,并且 UI 线程被阻塞等待SelectAContactForASlot完成。

为避免这种死锁,您应该await使用Tasks 而不是使用Waitor Result,如下所示:

private async void PersonButton_OnClick(object sender, RoutedEventArgs args)
{
  KeyValuePair<string, string> kvpContactNameEmail = await SelectAContactForASlot();
  ...
}

我在我的博客上详细解释了这种情况。

于 2012-11-22T19:15:03.020 回答
0

在您的更新 2 代码中更改此行:

Task<System.Collections.Generic.KeyValuePair<string, string>> kvpContactNameEmail = SelectAContactForASlot();

为了这:

Task<System.Collections.Generic.KeyValuePair<string, string>> kvpContactNameEmail = SelectAContactForASlot().AsTask();
KeyValuePair<string, string> tempKeyValuePair = FileTask.Result;

解释:您正在做的是将异步世界与同步世界绑定,原始调用只会获取任务但不会对其执行任何操作,而不是等待它完成(这就是您的调用立即返回的原因)。

于 2012-11-23T20:35:55.300 回答