在我的WPF应用程序中:
using System;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using Microsoft.Win32;
using System.Diagnostics;
namespace CloudKey
{
/// <summary>
/// Interaction logic for Page1.xaml
/// </summary>
public partial class Page1 : Page
{
public Page1()
{
InitializeComponent();
AuthText.Visibility = Visibility.Hidden;
}
private async void button_Click(object sender, RoutedEventArgs e)
{
AuthText.Visibility = Visibility.Visible;
await Task.Run(() => Authenticate());
Task.Factory.StartNew(() => Authenticate());
Task.Run(() => Authenticate());
Authenticate();
}
void Authenticate()
{
//Do Stuff
}
}
}
无论我尝试用Authenticate
拨打Tasks
哪个方式,它都无法运行。我使用Task
错了吗?
使用await(和async)会导致抛出异常:
System.InvalidOperationException未处理 消息:mscorlib.dll中发生了未处理的“System.InvalidOperationException”类型异常 附加信息:调用线程无法访问此对象,因为其他线程拥有它。
仅使用Task.Run或Task.Factory.StartNew会导致Authenticate方法根本不运行。如果我在Authenticate方法中添加断点,则不会到达。
使用Authenticate()调用该方法可以毫无问题地运行整个方法,但它会冻结UI,使“AuthText.Visibility = Visibility.Visible;”无用的。
老实说,我真的只想让UI更新消息“Authenticating ...”,然后单击按钮时运行方法中的所有内容。是否有更简单的方法可以做到这一点?
这是参考的工作代码:
using System;
using System.Windows;
using System.Windows.Controls;
using Microsoft.Win32;
using System.Diagnostics;
using System.Threading.Tasks;
namespace CloudKey
{
/// <summary>
/// Interaction logic for Page1.xaml
/// </summary>
public partial class Page1 : Page
{
public Page1()
{
InitializeComponent();
//private void PasswordBox_KeyDown(object sender, KeyEventArgs e) { if (e.KeyCode == Keys.Enter) { button_Click } }
AuthText.Visibility = Visibility.Hidden;
}
private void button_Click(object sender, RoutedEventArgs e) //ON CONTINUE BUTTON CLICK
{
AuthText.Visibility = Visibility.Visible;
Task.Run(() => Authenticate());
}
void Authenticate()
{
Dispatcher.Invoke(
() =>
{
//ALL MY CODE HERE;
});
}
}
}
答案 0 :(得分:2)
问题在于你并没有等待异步任务完成,所以看起来像#34;没有任何反应&#34; - 事实上确实发生了一些事情。当您调用Task.Run
或Task.Factory.StartNew
时,除非您正确处理Task
,否则您基本上会执行“即发即弃”。
private async void button_Click(object sender, RoutedEventArgs e)
{
await Task.Run(() => Authenticate()); // Stuff happens
}
void Authenticate()
{
// DO STUFF
}
在上面的示例中,将关键字async
添加到事件处理程序允许该方法使用await
关键字。 await
关键字是真正发生所有魔法的地方......但它会按照您的预期运作,即; &#34;东西发生了#34;。
当我确实击败Stephen Cleary时,我通常会将人们指向他的博客,this one尤其应该帮助您澄清这一点。
注意强>
强烈建议不要写async void
!唯一的例外是您将示例应用于事件处理程序的示例。最后,当使用Task
和Task<T>
与async / await
关键字时 - 在整个堆栈中执行此操作。我会更改您的Authenticate
方法以返回Task
,例如,可以等待它。尝试尽可能在最低级别调用Task.Run
。
private async void button_Click(object sender, RoutedEventArgs e)
{
await Authenticate(); // Stuff happens
}
Task Authenticate()
{
return _authModule.Authenticate();
}
<强>更新强>
根据您的评论,执行以下操作:
private void button_Click(object sender, RoutedEventArgs e)
{
bool authenticated = false;
try
{
AuthText = "Authenticating...";
authenticated = Authenticate(); // Stuff happens
}
finally
{
AuthText = authenticated ? "Authenticated" : "Oops!";
}
}
bool Authenticate()
{
// Return if auth was successful
}
答案 1 :(得分:0)
在新线程中修改UI内容时,您需要使用Dispatcher.Invoke
,或者可以使用InvokeAsync
private void Button_Click( object sender, RoutedEventArgs e ) { Task.Run( () => Authenticate() ); }
public void Authenticate()
{
Dispatcher.Invoke(
() =>
{
ClickButton.Content = "Text Changed";
} );
}
使用Dispatcher
您告诉WPF在主线程上运行此代码块,该主线程有权更新您的GUI控件。