这是我想要做的简化版本:
onClick button
,aNewMethod()
将异步运行以保持UI响应。那就是它!
我已经阅读了一些答案,这就是我能想到的:
private async void button_Click(object sender, RoutedEventArgs e)
{
Task task = Task.Run(() => aNewMethod());
await task;
}
private void aNewMethod()
{
if (progress.Value == 0)
{
//Heavy work
for (int i = 1; i < 1000000000; i++) { }
progress.Value = 100;
}
}
正如您可能想到的那样,这会在System.InvalidOperationException
if(progress.Value== 0)
处抛出Dispatcher.BeginInvoke()
:
经过一些谷歌搜索,我已经读过我需要一个调用线程无法访问此对象,因为它不同 线程拥有它。
private void aNewMethod()
{
Dispatcher.BeginInvoke((Action)delegate {
if (progress.Value == 0)
{
//Heavy work
for (int i = 1; i < 1000000000; i++) { }
progress.Value = 100;
}
});
}
方法来更新/使用UI控件,所以我这样做了:
System.InvalidOperationException
这解决了for loop
,但它没有异步运行,因为用户界面仍然冻结在aNewMethod();
所以问题是:如何异步运行import UIKit
class ViewController: UIViewController {
@IBOutlet weak var button: UIButton!
@IBOutlet weak var textView: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
textView.delegate = self
if (textView.text.isEmpty) {
button.enabled = false
}
}
}
extension ViewController: UITextViewDelegate {
func textView(textView: UITextView, range: NSRange, replacementText text: String) -> Bool
{
if (!textView.text.isEmpty) {
button.enabled = true
} else {
button.enabled = false
}
return true
}
}
并仍然更新并与UI控件交互?
答案 0 :(得分:2)
Dispatcher在UI线程中运行。它处理您的UI,并执行您通过BeginInvoke等传递给它的操作。 但它一次只能处理一件事;当它忙于处理您的操作时,它不会在此期间更新UI,因此UI会在此期间冻结。
如果要保持UI响应,则需要在单独的非UI线程中运行重载函数。在另一个线程上运行的那些函数中,您可以在需要访问UI时调用调度程序 - 理想情况下,只是为了更新UI元素而非常简短。
换句话说,你想要在一个单独的线程中运行你的sleep函数,然后在你需要设置进度值时从你自己的线程调用Dispatcher。像
这样的东西private void button_Click(object sender, RoutedEventArgs e)
{
Task task = Task.Run(() => aNewMethod()); // will call aNewMethod on the thread pool
}
private void aNewMethod()
{
double progressValue = 0;
Dispatcher.Invoke(() => progressValue = progress.Value);
if (progressValue == 0)
{
Thread.Sleep(3000); // still executes on the threadpool (see above), so not blocking UI
Dispatcher.Invoke(() => progress.Value = 100 ); // call the dispatcher to access UI
}
}
答案 1 :(得分:2)
使用当前的实现,不需要使用Thread.Start
,因为在那个方法中你只是睡了一段时间并访问那里不允许的UI线程对象
在你的场景中,一个更好的方法是你不应该使用Thread.Sleep
而不是你应该使用Task.Delay
这样做:
private async void button_Click(object sender, RoutedEventArgs e)
{
if (progress.Value == 0)
{
await Task.Delay(3000);
progress.Value = 100;
}
}
现在你不需要Dispatcher.Invoke
,并且在等待将在我们所做的同一个调用同步上下文中执行之后,信用转到async
和await
关键字作为语句异步调用,在这种情况下是UI线程。
答案 2 :(得分:2)
onClick一个按钮,aNewMethod()将异步运行以保持UI响应。
实际上,它在后台线程上同步运行 。 Task.Run
非常适合此。
经过一些谷歌搜索,我已经读过我需要一个Dispatcher.BeginInvoke()方法来更新/使用UI控件
不幸的是,这是谷歌肯定会误导你的一个方面。 Dispatcher
不是这里的最佳解决方案。
相反,您应该使用IProgress<T>
/ Progress<T>
,因此:
private async void button_Click(object sender, RoutedEventArgs e)
{
var progress = new Progress<int>(value => { this.progress.Value = value; });
await Task.Run(() => aNewMethod(progress));
}
private void aNewMethod(IProgress<int> progress)
{
//Heavy work
for (int i = 1; i < 1000000000; i++) { }
progress.Report(100);
}