C# 在运行时创建复杂的UserControl,而不冻结GUI

C# 在运行时创建复杂的UserControl,而不冻结GUI,c#,wpf,multithreading,backgroundworker,dispatcher,C#,Wpf,Multithreading,Backgroundworker,Dispatcher,我有一个非常复杂的用户控件,需要在运行时创建。此创建将GUI冻结约5秒(这是不可接受的) 我试图将此操作移动到后台工作程序中,结果出现以下异常: 调用线程必须是STA,因为许多UI组件都需要它 我知道我不能使用MTA线程来创建UserControl/UI元素。我尝试使用BackgroundWorker和Dispatcher的组合,但没有成功 第一次尝试 private void LetsGo() { var backgroundWorker = new BackgroundWorke

我有一个非常复杂的用户控件,需要在运行时创建。此创建将GUI冻结约5秒(这是不可接受的)

我试图将此操作移动到后台工作程序中,结果出现以下异常:

调用线程必须是STA,因为许多UI组件都需要它

我知道我不能使用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();
}
private async void GenerateControl()
{
    this.Control = await Dispatcher.CurrentDispatcher.InvokeAsync<UserControl>(this.GenerateControlAsync);
}

private UserControl GenerateControlAsync()
{
    return new TimeConsumingUserControl();
}
上面的代码无效,未执行this.GenerateControlAsync方法


第二次尝试

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();
}
private async void GenerateControl()
{
    this.Control = await Dispatcher.CurrentDispatcher.InvokeAsync<UserControl>(this.GenerateControlAsync);
}

private UserControl GenerateControlAsync()
{
    return new TimeConsumingUserControl();
}
private async void GenerateControl()
{
this.Control=await Dispatcher.CurrentDispatcher.InvokeAsync(this.GenerateControlAsync);
}
私有用户控制GenerateControlAsync()
{
返回新的TimeConsumingUserControl();
}
此示例正在运行,但它一直冻结GUI线程。
我正在使用WPF和.NETFramework 4.5

请注意,方法GenerateControlAsync()不仅创建UserControl的实例,还涉及更多的逻辑

要回答@HighCore的问题: 事实上,UserControl的XAML代码是通过XSLT从xml文件中转换出来的,Codebehind是使用CodeDOM生成的。所有的东西都需要编译并打包到一个程序集中。我使用assembly.CreateInstance()获取UserControl的实例。此行抛出引用的异常。在我的GUI中,我有一个ContentControl,它绑定到我的ViewModel中的UserControl。生成的数据(如需要转换的xml文件)是从Web服务检索的


这就是为什么此方法的执行时间比人们预期的要长。

从您对创建控件所涉及的所有步骤的描述来看,您似乎将许多不需要在同一线程上完成的工作集中在一起,并尝试在UI或后台线程上完成所有这些工作。相反,您应该在UI线程(实际的
UserControl
实例化)上完成所需的最小工作量,并在工作线程上完成其他所有工作。与单个异步工作单元不同,您应该执行两个步骤,使用async await非常简单。它应该更像这样:

var dynamicAssembly = await this.GenerateControlAssemblyAsync();
this.Control = this.GenerateControlFromAssembly(dynamicAssembly);

由于wait的工作方式,第二行将自动在原始(UI)线程上运行,因此不需要任何
Dispatcher
调用。在
generateControlassemblayasync
中,您应该使用
任务。运行
并执行该任务中的所有其他代码
GenerateControlFromAssembly
应该什么都不做,只是实例化UC实例。

您需要分块生成程序集。程序集生成需要花费太多的时间,您需要将所有内容放在另一个线程中,并且只在同一个线程中直接生成Ui组件


有一种很酷的方法可以从流中加载Xaml并分块执行,而无需占用UI。看一看:

涉及的逻辑更多。
-什么逻辑?您应该将UI与数据和业务逻辑分开,这样您就不会再有这些问题了。顺便说一下,从MVVM的角度来看,在过程代码中创建UI元素并不是一个好主意。这就是XAML的作用。我只是想避免讨论。事实上,UserControl的XAML代码是通过XSLT从xml文件中转换出来的,Codebehind是使用CodeDOM生成的。所有的东西都需要编译并打包到一个程序集中。我使用assembly.CreateInstance()获取UserControl的实例。此行抛出引用的异常。在我的GUI中,我有一个ContentControl,它绑定到我的ViewModel中的UserControl。在WPF中,这些都没有必要,你可以用一些
DataTemplate
s和
XAMLReader
来解决你的需求,但是如果你已经有了它(在我看来,这是完全错误的),那么我认为你没有什么可以做来改进它。只能在UI线程中创建/操作UI元素。因此,当这种情况发生时,UI的其余部分将变得不负责任。人们似乎不理解WPF的“动态UI”概念与大多数古老的恐龙UI框架完全不同,WPF中完全不需要这些黑客。让我澄清一下这个概念背后的想法:用户界面和逻辑是用通用抽象语言定义的。有几种不同平台的客户端,如Linux、Windows或Web。客户机知道如何将通用语言转换为本机GUI。结果是,您不必为每个平台定义相同的GUI/逻辑。在通用格式中只指定一次就足够了。然后,您将不得不提出一些预编译机制,而不是在运行时动态执行此操作。谢谢,它可以工作。我的解决方案如下所示:var token=wait Task.Factory.StartNew(this.GenerateControlAsync);