我有一个按钮,当我点击它时,我希望它用值
更新UI这是我的按钮
private async void Button_Click(object sender, RoutedEventArgs e)
{
await DoItAsync();
}
这是DoIt!
public async Task DoSomethingAsync()
{
await Task.Run(()=>{
for(int i = 0; i < 2000; i++)
{
//labelName.Content = i.ToString();
Console.WriteLine(i);
}
});
}
所以,如果我运行上面的代码,点击我的按钮,调用DoItAysnc,所有数字都显示在控制台中。
如果我取消评论
labelName.Content = i.ToString();
我得到一个例外,事实如下:调用线程无法访问此对象,因为另一个线程拥有它。
我知道Async并等待不为你解决线程问题,但我确实认为这会有效。我想知道如何使其工作,以便为每个i值更新UI,而不会使UI无响应。
对此有任何好的资源也会有所帮助。
一如既往,种类问候
答案 0 :(得分:8)
在这种特殊情况下,你所做的一切似乎都是尽可能快地在标签上计算到2000。这不太可能是您的实际用例。
如果您想在多个不同的异步操作(例如,等待一段时间)中更新UI,那么您可以像这样构建代码:
private async void Button_Click(object sender, RoutedEventArgs e)
{
for (int i = 0; i < 2000; i++)
{
await Task.Delay(TimeSpan.FromSeconds(1));
labelName.Content = i.ToString();
}
}
如果您在概念上正在做的是使用长时间运行的非UI操作来更新UI,那么Progress
类旨在专门处理:
private async void Button_Click(object sender, RoutedEventArgs e)
{
Progress<int> progress = new Progress<int>(
i => labelName.Content = i.ToString());
await Task.Run(() => DoWork(progress));
}
private void DoWork(IProgress<int> progress)
{
for (int i = 0; i < 2000; i++)
{
//Do Stuff
progress.Report(i);
}
}
通常,如果您可以将问题分解为一组小的异步操作,其中UI在每个操作之间更新,则第一个选项是有意义的。如果有一个长的非UI操作无法轻易分解为较小的操作,则完成第二种方法。
答案 1 :(得分:0)
看一下答案here。无论您如何启动工作线程(Thread.Start,Task.Run,BackgroundWorker.RunWorkerAsync),除非您在UI线程上运行,否则不能影响对UI的更改。
正如@ t-mckeown所述,解决方案是强行进入UI线程。如何做到这一点取决于技术(WFP = Dispatcher,WinForms = InvokeRequired / BeginInvoke)。