c# - 在运行时创建复杂的用户控件而不卡住 GUI

标签 c# wpf multithreading backgroundworker dispatcher

我有一个非常复杂的用户控件,需要在运行时创建。此创建会使 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/

相关文章:

c# - 如何在列具有单元格模板的情况下以编程方式创建 GridView?

wpf - 在ViewModel中更改TextBlock的文本时运行动画

java - 单事务跨多线程解决方案

java - 用于生成、转换和计数频率的线程

c# - 将 xml 嵌入 json

c# - 加号在 nopcommerce 41 中转换为 + 但解析为 42beta

c# - 使用 Linq Select 创建新的继承对象

c# - 调用者的编译时验证,或者是否可以扩展 C# 编译器?

c# - 跨线程操作无效

wpf - 从另一个ViewModel引发MVVM属性的最佳方法是什么?