首页 文章

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

提问于
浏览
1

我有一个非常复杂的UserControl需要在运行时创建 . 这个创建冻结了GUI大约5秒钟(这是不可接受的) .

我试图将此操作移动到后台工作器并最终得到此异常:

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

我知道我不能使用MTA线程来创建UserControl / UI元素 . 我尝试使用BackgroundWorker和Dispatcher的组合,但它不起作用 .


First try

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不执行 .


Second try

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绑定 . 从web服务中检索需要转换的xml文件的生成数据 .

这就是为什么这种方法的执行需要比人们预期的要长一些的原因 .

2 回答

  • 1

    根据您对创建控件所涉及的所有步骤的描述,您似乎需要在同一个线程上完成并尝试在UI或后台线程上完成所有操作 . 您应该在UI线程上执行必要的最少工作量(实际的 UserControl 实例化)并在工作线程上执行其他所有操作 . 你应该做两个步骤而不是单个异步工作单元,而使用async-await非常简单 . 它应该看起来更像这样:

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

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

  • 1

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

    有一种很酷的方法可以从流中加载Xaml并以块的形式进行,而不会占用UI . 看看:http://msdn.microsoft.com/en-us/library/aa346591.aspx

相关问题