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

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

调用线程必须是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应用程序

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遇到了错误的语法(它不在我的脑海中),但无论如何你都去了……

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((res) => { // Working with UI TreeView tv = new TreeView(); }), result); } 

没有人详细讨论单独的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"); } 

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

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

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

请尝试以下代码:

 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 } 
 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); }; } 

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

看到这个问题的答案: 如何在STA线程中运行一些东西?

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

 thread.SetApartmentState(ApartmentState.STA); 

这应该做的伎俩!