在创建/添加新的 wpf 用户控件时“调用线程必须是 STA,因为许多 UI 组件都需要这个”
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/45187938/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
"The calling thread must be STA, because many UI components require this" while creating/adding new wpf usercontrol
提问by buks
I have a usertaskpane in VSTO add-in. I'm adding there winformshost and elementhost to be able to use wpf controls inside usertaskpane.
我在 VSTO 加载项中有一个 usertaskpane。我正在添加 winformshost 和 elementhost 以便能够在 usertaskpane 中使用 wpf 控件。
I managed to add a main wpf control, but I am failing with adding child user control to that.
我设法添加了一个主要的 wpf 控件,但是我无法向其添加子用户控件。
I have such method that initiates adding new wpf control:
我有这样的方法来启动添加新的 wpf 控件:
private void MasterCheck()
{
this.pnlProgress.Visibility = System.Windows.Visibility.Visible;
//I'm using progress bar functionality in ReturnMasters method
Thread myNewThread = new Thread(() => Auditor.AuditMasterSlides(Globals.ThisAddIn.Application.ActivePresentation, this.pnlMaster, this, token));
token = new CancellationTokenSource();
myNewThread.Start();
this.pnlProgress.Visibility = System.Windows.Visibility.Collapsed;
}
public static void AuditMasterSlides(PPT.Presentation pres, Panel panel, MainProofingTaskPaneControl control, CancellationTokenSource cancToken)
{
IDictionary<string,MasterSlide> masterSlides = ReturnMasters(pres, cancToken, control);
control.ShowAndCollapse(panel);
control.RemovePanelChildren(panel);
if (masterSlides.Count>1)
{
//control.AddControlToPanel(panel, new MasterCheckControlOK());
}
else
{
control.AddControlToPanel(panel, new MasterCheckControlOK());
}
}
internal void RemovePanelChildren(Panel panel)
{
this.Dispatcher.Invoke(() =>
{
for (int i = panel.Children.Count - 1; i >= 0; i--)
{
panel.Children.RemoveAt(i);
}
});
}
internal void AddControlToPanel(Panel panel, Control control)
{
MasterCheckControlOK newControl = new MasterCheckControlOK();
this.Dispatcher.Invoke(() =>
{
panel.Children.Add(newControl);
});
}
And I'm getting error here:
我在这里遇到错误:
public MasterCheckControlOK()
{
InitializeComponent();
}
How can I solve it to be able to:
我该如何解决它才能:
- use progress bar functionality (currently works)
- add new wpf controls (does not work)
- modify/remove controls (currently works)
- 使用进度条功能(目前有效)
- 添加新的 wpf 控件(不起作用)
- 修改/删除控件(当前有效)
回答by mm8
- You can only create UI controls on STA (single-threaded apartment) threads:
- 您只能在 STA(单线程单元)线程上创建 UI 控件:
The calling thread must be STA, because many UI components require this
- You can only access a control on the thread on which it was originally created. For example, you cannot create a control on a thread B and then try to add it to the
Childrencollection of a control that was created on thread A.
- 您只能访问最初创建它的线程上的控件。例如,您不能在线程 B 上创建控件,然后尝试将其添加到
Children在线程 A 上创建的控件的集合中。
So it makes no sense to create a control on a background thread if you intend to interact with it one way or another from the main thread. Then you will get this exception.
因此,如果您打算从主线程以一种或另一种方式与其交互,那么在后台线程上创建控件是没有意义的。然后你会得到这个异常。
Bottom line: You should create all controls on the same thread and this thread should in most cases be the main UI/dispatcher thread. This will save you a whole lot of trouble.
底线:您应该在同一个线程上创建所有控件,并且该线程在大多数情况下应该是主 UI/调度程序线程。这将为您省去很多麻烦。
回答by Sami Kuhmonen
When you create a control it has to happen in the main UI thread. Currently you are creating the control in another thread and then adding it to another. This will cause an exception.
创建控件时,它必须发生在主 UI 线程中。当前,您正在另一个线程中创建控件,然后将其添加到另一个线程中。这将导致异常。
You need to move the creation of the control to happen inside the invoke so it happens on the main UI thread.
您需要将控件的创建移动到 invoke 内部,以便它发生在主 UI 线程上。
回答by Ortund
You can't create UI controls in separate threads. The control needs to exist on the UI thread.
您不能在单独的线程中创建 UI 控件。该控件需要存在于 UI 线程上。
You might try having your threaded function do its work through your window's Dispatcherusing its .Invoke()methods.
您可以尝试让线程函数使用其方法通过窗口的Dispatcher完成其工作.Invoke()。
You probably want to make sure that ONLYthe manipulation of your UI controls is done with the dispatcher otherwise you'll probably lock up the UI anyway.
您可能希望确保只有您的 UI 控件的操作是由调度程序完成的,否则无论如何您都可能会锁定 UI。
public static void AuditMasterSlides(PPT.Presentation pres, Panel panel, MainProofingTaskPaneControl control, CancellationTokenSource cancToken)
{
IDictionary<string,MasterSlide> masterSlides = ReturnMasters(pres, cancToken, control);
this.Dispatcher.Invoke((() => control.ShowAndCollapse(panel));
...
}
As for the STA thread issue, you need to specify that your thread is an STA thread before you start it.
至于STA线程问题,需要在启动前指定你的线程是STA线程。
I did this by calling .SetApartmentState()on my thread:
我通过调用.SetApartmentState()我的线程来做到这一点:
thread1.SetApartmentState(ApartmentState.STA);
thread1.Start();

