我有创建后台线程的方法来做一些动作。在这个后台线程中我创建了对象。但是在运行时创建时这个对象给了我一个例外:
调用线程必须是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 :(得分: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);
这应该可以解决问题!