Hi All,
我目前正在用C#开发我的第一个WPF程序(将文件自动移动到结构化文件夹路径的基本工具),并且几乎完成了。但是我遇到了一个问题。我的用户界面由三个不同的选项卡组成,它们显示一个主页(带有用户控件),一个日志页面和一个关于页面。在日志文本框上启用自动滚动后,切换到“日志”选项卡时,UI冻结。程序使用基本的textboxoutputter.
输出到日志启用自动滚动的代码包括:
TXTBX_Log Xaml:
<TextBox x:Name="TXTBX_Log" Grid.Column="1" Grid.ColumnSpan="7" Grid.Row="1" Grid.RowSpan="7" Background="#FF3F3F46" Foreground="White" VerticalContentAlignment="Bottom" IsReadOnly="True" TextChanged="ScrollToEnd" />
TXTBX_Log TextChanged事件:
private void ScrollToEnd(object sender, TextChangedEventArgs e)
{
TXTB_MainWindow.ScrollToEnd();
}
UI看起来像这样:
当用户按下开始按钮时,我将启动后台工作人员。这个想法是,在用户按下Start之后,他/他可以切换到“日志”选项卡以关注程序的运行情况。我用来完成此操作的代码粘贴在下面:
开始按钮,fullAutomation设置为TRUE:
private void BTN_Start_Click(object sender, RoutedEventArgs e)
{
Console.WriteLine("Opdracht wordt uitgevoerd");
if (fullAutomation == false)
{
archivingWorker = new BackgroundWorker();
archivingWorker.DoWork += ArchivingWorker_DoWork;
archivingWorker.RunWorkerCompleted += ArchivingWorker_RunWorkerCompleted;
archivingWorker.RunWorkerAsync();
}
else
{
fullAutomationWorker = new BackgroundWorker();
fullAutomationWorker.DoWork += FullAutomationWorker_DoWork;
fullAutomationWorker.RunWorkerCompleted += FullAutomationWorker_RunWorkerCompleted;
fullAutomationWorker.RunWorkerAsync();
}
}
我的问题是,后台线程运行时UI线程冻结,并且启用自动滚动时用户切换到日志选项卡。有人知道为什么会这样吗?
@@ pot
RunWorkerAsync()
在不同的线程上启动工作程序,因此UI不会冻结。 ArchivingWorker尚不包含任何代码,因为它在程序的当前状态下没有使用。
猜测它不是在不同的Thread
上启动,而是异步地使用Event
处理程序。因此,您需要async
事件处理程序才能实现真正的异步效果。
1)将事件处理程序类型更改为async void
private void FullAutomationWorker_DoWork(object sender, DoWorkEventArgs e)
to
private async void FullAutomationWorker_DoWork(object sender, DoWorkEventArgs e)
2)将延迟更改为异步延迟
Thread.Sleep(waitTime);
to
await Task.Delay(waitTime);
精确地await
使方法调用异步,并且await
中只能使用awaitable方法。
注: async void
仅对事件处理程序有用,而对任何不好的async
方法都没有用。建议将async Task
用于其他方法。
// returns nothing
private async Task MyMethodAsync()
{
// do async job
await Task.Delay(10); // awaitable call
}
// returns string
private async Task<string> MyMethod2Async()
{
string result = "example result";
// do async job
await Task.Delay(10);
return string;
}
async
方法中的用法
await MyMethodAsync();
string text = await MyMethod2Async();
在常规同步代码中使用,甚至不需要这种方法,它称为“异步同步”。
MyMethodAsync().GetAwaiter().GetResult();
string text = MyMethod2Async().GetAwaiter().GerResult();
并在与同步代码不同的线程中运行异步方法。
Task task = Task.Run(MyMethodAsync); // run it
// do job here while Task is running
task.Wait(); // wait for completion
Task<string> task2 = Task.Run(MyMethodAsync2); // run it
// do job here while Task is running
text = task2.GetAwaiter().GetResult(); // wait for completion and get result