我现在对显示为I was having的人物/联系人没有任何问题 - 现在连续几次都显示了它们 - 但是检索联系人对象的调用永远不会返回 - 这一行有断点:
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();
好的,现在我真的很喜欢:
此代码位于按钮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();
因此,SelectContactForASlot()的调用时间比我想象的要晚,但一旦调用它就无法继续调用。
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;
}
...如果我将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&GT;'“
我想我的问题实际上是我不希望调用SelectAContactForASlot()返回它直到它检索联系人对象(调用Contact应用程序并且用户选择联系人);我想,它希望它是异步的。但我不认为PickSingleContactAsync()有异步版本 - IOW,没有“PickSingleContact”。
所以我真正想知道的是:我如何调用Contact应用程序并允许用户选择一个Contact(已经这样做)并从该对象中获取我可以在我的代码中访问的值?显然,我认为这一切都是错的。
答案 0 :(得分:1)
我猜测在您的调用层次结构中的某个时刻,您阻止了Task
返回的SelectAContactForASlot
或调用它的方法。
这将导致死锁,因为SelectAContactForASlot
完成时必须在UI上下文中恢复await
,并阻止UI线程等待SelectAContactForASlot
完成。
为避免此死锁,您应该await
Task
,而不是Wait
或Result
,如下所示:
private async void PersonButton_OnClick(object sender, RoutedEventArgs args)
{
KeyValuePair<string, string> kvpContactNameEmail = await SelectAContactForASlot();
...
}
我详细解释了这个场景on my blog。
答案 1 :(得分:0)
在您的Update 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;
解释:你正在做的是将异步世界与同步世界绑定,原始调用将只获得任务但不会对它做任何事情,而不是等待它完成(这就是为什么你的电话立即返回)。