如何在后台线程中创建WPF控件?

时间:2010-05-04 06:44:12

标签: c# wpf multithreading backgroundworker

我有创建后台线程的方法来做一些动作。在这个后台线程中我创建了对象。但是在运行时创建时这个对象给了我一个例外:

  

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

我知道我必须使用Dispatcher来向UI反映内容。但在这种情况下,我只是创建一个对象,而不是用UI迭代。这是我的代码:

    public void SomeMethod()
      {
         BackgroundWorker worker = new BackgroundWorker();
         worker.DoWork += new DoWorkEventHandler(Background_Method);
         worker.RunWorkerAsync();
      }

   void Background_Method(object sender, DoWorkEventArgs e)
      {
         TreeView tv = new TreeView();
      }

如何在后台线程中创建对象?

我使用WPF应用程序

8 个答案:

答案 0 :(得分:6)

TreeView是一个UI控件。您只能在UI线程上创建和操作UI控件,因此您无法尝试执行此操作。

您要做的是在后台线程上完成所有耗时的工作,然后“回调”到UI线程来操作UI。这实际上非常简单:

void Background_Method(object sender, DoWorkEventArgs e)
{
    // ... time consuming stuff...

    // call back to the window to do the UI-manipulation
    this.BeginInvoke(new MethodInvoker(delegate {
        TreeView tv = new TreeView();
        // etc, manipulate
    }));
}

我的BeginInvoke语法可能有问题(它不在我的脑海中),但无论如何你都去了......

答案 1 :(得分:3)

HTH:

    void Background_Method(object sender, DoWorkEventArgs e)
    {
        // Time Consuming operations without using UI elements
        // Result of timeconsuming operations
        var result = new object();
        App.Current.Dispatcher.Invoke(new Action<object>((res) =>
            {
                // Working with UI
                TreeView tv = new TreeView();
            }), result);
    }

答案 2 :(得分:2)

没有人详细讨论单独的STA线程的情况(即使概念完全相同)。

所以让我们想象一下,点击按钮时添加一个简单的标签控件

    private void button_Click(object sender, RoutedEventArgs e)
    {
        TabItem newTab = new TabItem() { Header = "New Tab" };
        tabMain.Items.Add(newTab);
    }

如果我们将它移动到另一个STA线程

    private void button_Click(object sender, RoutedEventArgs e)
    {
        Thread newThread = new Thread(new ThreadStart(ThreadStartingPoint));
        newThread.SetApartmentState(ApartmentState.STA);
        newThread.IsBackground = true;
        newThread.Start();
    }
    private void ThreadStartingPoint()
    {
        TabItem newTab = new TabItem() { Header = "New Tab" };
        tabMain.Items.Add(newTab);
    }

当然我们得到System.InvalidOperationException

现在,如果我们添加控件

会发生什么
    private void AddToParent(string header)
    {
        TabItem newTab = new TabItem() { Header = header };
        tabMain.Items.Add(newTab);
    }

使用委托方法?

    public void DelegateMethod(string header)
    {
        tabMain.Dispatcher.BeginInvoke(
                new Action(() => {
                    this.AddToParent(header);
                }), null);
    }

如果你打电话,它确实有用

    private void button_Click(object sender, RoutedEventArgs e)
    {
        Thread newThread = new Thread(new ThreadStart(ThreadStartingPoint));
        newThread.SetApartmentState(ApartmentState.STA);
        newThread.IsBackground = true;
        newThread.Start();
    }
    private void ThreadStartingPoint()
    {
        DelegateMethod("new tab");
    }

因为现在我们将可视树保持在同一原始线程中。

答案 3 :(得分:0)

要简化您的代码,您必须致电STA COM加入Thread.SetApartmentState(ApartmentState.STA)公寓。由于BackgroundWorker可能正在使用某个共享线程池,因此加入特定公寓可能会影响该线程池的其他用户,或者如果已经将其设置为例如,则可能会失败。 MTA之前。即使一切顺利,您新创建的TreeView也会被锁定到此工作线程。您将无法在主UI线程中使用它。

如果你更详细地解释一下你的真实意图,你一定会得到更好的帮助。

答案 4 :(得分:0)

尝试以下代码:

public void SomeMethod() 
{ 

System.ComponentModel.BackgroundWorker myWorker = new  System.ComponentModel.BackgroundWorker();

myWorker.DoWork += myWorker_DoWork;

myWorker.RunWorkerAsync();

}

private void myWorker_DoWork(object sender,
   System.ComponentModel.DoWorkEventArgs e)
{
   // Do time-consuming work here
}

答案 5 :(得分:0)

void Background_Method(object sender, DoWorkEventArgs e) 
{ 
    TreeView tv = new TreeView(); 
    // Generate your TreeView here
    UIDispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() => 
    { 
        someContainer.Children.Add(tv);
    }; 
}

答案 6 :(得分:0)

我解决了我的问题。我刚刚使用了RunWorkerCompleted方法的e.Result属性。我在后台线程中获取数据,然后在线程完成时使用此数据。感谢每个人的有用方法。特别感谢Veer提出有关e.Result财产的建议。

答案 7 :(得分:0)

请参阅此问题的答案: How to run something in the STA thread?

定义线程时,将ApartmentState设置为STA:

thread.SetApartmentState(ApartmentState.STA);

这应该可以解决问题!