我有一个非常复杂的用户控件,需要在运行时创建。此创建会使 GUI 卡住大约 5 秒(这是 Not Acceptable )。
我尝试将此操作移至后台工作程序中,但最终出现此异常:
The calling thread must be STA, because many UI components require this.
我知道我无法使用 MTA 线程来创建 UserControl/UI 元素。我尝试结合使用BackgroundWorker 和Dispatcher,但没有成功。
第一次尝试
private void LetsGo()
{
var backgroundWorker = new BackgroundWorker();
backgroundWorker.DoWork += backgroundWorker_DoWork;
backgroundWorker.RunWorkerCompleted += backgroundWorker_RunWorkerCompleted;
backgroundWorker.RunWorkerAsync();
}
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
DispatcherOperation dispatcherOperation = Dispatcher.CurrentDispatcher.BeginInvoke(new Action(this.GenerateControlAsync), DispatcherPriority.Background);
}
private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled)
{
// Cancelled
}
else if (e.Error != null)
{
//Exception Thrown
}
else
{
//Completed
}
}
private void GenerateControlAsync () {
this.Control = new TimeConsumingUserControl();
}
上面的代码不起作用,this.GenerateControlAsync 方法未执行。
第二次尝试
private async void GenerateControl()
{
this.Control = await Dispatcher.CurrentDispatcher.InvokeAsync<UserControl>(this.GenerateControlAsync);
}
private UserControl GenerateControlAsync()
{
return new TimeConsumingUserControl();
}
这个示例正在工作,但它一直卡住 GUI 线程。
我使用的是 WPF 和 .net Framework 4.5。
请注意,GenerateControlAsync() 方法不仅仅创建 UserControl 的实例,还涉及更多逻辑。
回答@HighCore的问题: 事实上,UserControl 的 XAML 代码是通过 XSLT 从 xml 文件转换而来的,并且代码隐藏是使用 CodeDOM 生成的。整个事情需要被编译并包装成一个程序集。我使用 assembly.CreateInstance() 来获取 UserControl 的实例。该行抛出引用的异常。在我的 GUI 中,我有一个 ContentControl,它绑定(bind)到我的 ViewModel 中的 UserControl。生成所需的数据(例如需要转换的 xml 文件)是从 Web 服务中检索的。
这就是为什么此方法的执行时间比人们预期的要长一些。
最佳答案
从您对创建控件所涉及的所有步骤的描述来看,您似乎将许多不需要在同一线程上完成的工作集中在一起,并尝试在 UI 或 UI 上完成所有工作。后台线程。您应该在 UI 线程(实际的 UserControl 实例化)上执行最少量的必要工作,并在工作线程上执行其他所有操作。您应该执行 2 个步骤,而不是单个异步工作单元,这对于 async-await 来说非常简单。它应该看起来更像这样:
var dynamicAssembly = await this.GenerateControlAssemblyAsync();
this.Control = this.GenerateControlFromAssembly(dynamicAssembly);
由于等待的工作方式,第二行将自动在原始 (UI) 线程上运行,因此不需要任何 Dispatcher
调用。在 GenerateControlAssemblyAsync
中,您应该使用 Task.Run
并在其中执行所有其他代码。 GenerateControlFromAssembly
除了实例化 UC 实例之外什么都不做。
关于c# - 在运行时创建复杂的用户控件而不卡住 GUI,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16568574/