2010-05-04 31 views
10

我有创建后台线程做一些行动的方法。在这个后台线程中,我创建了对象。但是这个对象在运行时创建时给我一个例外:如何在后台线程中创建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应用

+0

还有一个问题:Background Worker方法有可能返回某些特定类型的值吗? – Polaris 2010-05-04 07:08:37

+2

检查RunWorkerCompleted方法中的e.Result属性。 http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx。 – Amsakanna 2010-05-04 07:18:55

回答

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(这是把我的头顶部),但你去那里反正...

+0

我从Web服务获取一些数据,运行时花费大量时间。这就是为什么我想在后台获取数据并在数据准备就绪时生成我的treeView。 – Polaris 2010-05-04 07:01:17

+0

我已经更新了我的答案,并提供了一些关于如何从工作线程在UI线程中执行某些操作的注释。 – 2010-05-04 07:14:59

0

为了使您的代码只是工作,您必须致电Thread.SetApartmentState(ApartmentState.STA)加入STA COM公寓。由于BackgroundWorker可能使用某个共享线程池,因此加入特定公寓可能会影响此线程池的其他用户,或者如果它已被设置为例如“ MTA之前。即使全部解决,新创建的TreeView也会被锁定到该工作线程。您将无法在主UI线程中使用它。

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

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

我解决我的问题。我只是使用RunWorkerCompleted方法的e.Result属性。我在后台线程中获取数据,然后在线程完成时使用这些数据。感谢所有有用的方法。特别感谢Veer提供关于e.Result财产的建议。

+0

如果要以某些间隔更新UI,可以使用ReportProgress方法的UserState参数发送数据,并通过将e.UserState转换为所需的类型在ProgressChanged方法中使用它们。 – Amsakanna 2010-05-04 08:56:15

0

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

原因当然现在我们保持视觉树在同原来的线程。

相关问题