2

我有一个非常复杂的 UserControl 需要在运行时创建。此创建将 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();
}

上面的代码不起作用,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 文件中转换出来的,而Codebehind 是使用CodeDOM 生成的。整个事情都需要编译并包装到一个程序集中。我使用 assembly.CreateInstance() 来获取 UserControl 的一个实例。此行引发引用的异常。在我的 GUI 中,我有一个 ContentControl,它绑定到我的 ViewModel 中的 UserControl。生成的数据(如需要转换的 xml 文件)是从 Web 服务中检索的。

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

4

2 回答 2

1

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

有一种很酷的方法可以从流中加载 Xaml 并分块执行,而不会阻塞 UI。看看:http: //msdn.microsoft.com/en-us/library/aa346591.aspx

于 2013-05-15T16:45:38.760 回答
1

从您对创建控件所涉及的所有步骤的描述来看,您似乎将许多不需要在同一个线程上完成的工作混为一谈,并试图在 UI 或后台线程上完成所有工作。UserControl相反,您应该在 UI 线程(实际实例化)上做最少的必要工作,并在工作线程上做所有其他事情。而不是一个单一的异步工作单元,您应该执行 2 个步骤,使用 async-await 非常简单。它应该看起来更像这样:

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

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

于 2013-05-15T15:35:47.973 回答