很长的帖子很抱歉,但是我试图非常详细地解释这个问题,以免引起混淆.最后一句包含实际问题.
我正在用C#/.NET编写多线程应用程序.
该应用程序包含一个主窗口,该窗口可可视化来自压力传感器的数据.传感器数据在自己的线程中获取.
数据也记录在类ListView的实例中:
可以通过“保存”按钮将记录的数据保存到磁盘上的文件(应打开.NET类SaveFileDialog的实例).
此SaveFileDialog也在自己的线程中运行.
现在调用方法SaveFileDialog.ShowDialog()时出现问题:
System.InvalidOperationException was unhandled
Message=”Cross-thread operation not valid: Control ‘tlpMain’ accessed from a thread other than the thread it was created on.”
Source=”System.Windows.Forms”
出现问题是因为SaveFileDialog的所有者(主窗口)在另一个线程中运行.
这是代码,它为SaveFileDialog()创建线程:
private void bSave_Click(object sender, EventArgs e)
{
Thread saveFileDialog = new Thread(OpenSaveFileDialog);
saveFileDialog.SetApartmentState(ApartmentState.STA);
saveFileDialog.Start();
}
方法OpenSaveFileDialog()的代码:
private void OpenSaveFileDialog()
{
SaveFileDialog saveFileDialog = new SaveFileDialog();
saveFileDialog.Filter = "Text Files (*.txt)|*.txt|CSV (*.csv)|*.csv|All Files (*.*)|*.*";
saveFileDialog.FilterIndex = 0;
/* Call "ShowDialog" with an owner ("this.Parent") to achieve, so that
* the parent window is blocked and "unclickable".
*
* Danger of an "InvalidOperationException" because "this.Parent" control
* is running (was created) in another thread.
* But "this.Parent" should not be modified by this method call.
*/
DialogResult pressedButton = saveFileDialog.ShowDialog(this.Parent);
...
仅当使用Visual Studio的调试器运行应用程序时,才会引发/显示InvalidOperationException.到目前为止,“正常”运行应用程序没有问题.
但是我想避免这个问题.
我试图建立一个包装方法(SaveFileDialog):
private void OpenSaveFileDialog()
{
SaveFileDialog saveFileDialog = new SaveFileDialog();
...
SaveFileDialog(saveFileDialog, this.Parent);
}
包装方法:
private void SaveFileDialog(SaveFileDialog saveFileDialog, Control owner)
{
if (owner.InvokeRequired)
BeginInvoke(new dSaveFileDialog(SaveFileDialog), new object[] { saveFileDialog, owner });
else
{
DialogResult pressedButton = saveFileDialog.ShowDialog(owner);
...
尽管Main()方法标记为[STAThreadAttribute],但这会导致TargetInvocationException:
InnerException: System.Threading.ThreadStateException
Message=”Current thread must be set to single thread apartment (STA) mode before OLE calls can be made. Ensure that your Main function has STAThreadAttribute marked on it. This exception is only raised if a debugger is attached to the process.”
Source=”System.Windows.Forms”
是否有人知道如何以某种方式打开SaveFileDialog,以便在没有(线程)麻烦的情况下阻止主窗口(“不可点击”)?
谢谢.
解决方法:
您在调试过程中遇到的跨线程异常为Managed Debugging Assistant.它们通常在调试器外部不活动.这就解释了为什么在Visual Studio外部运行应用程序时看不到.
似乎您已经发现自己无法对除主UI线程以外的其他线程的UI元素执行任何操作.您使用ISynchronizeInvoke方法(即Invoke或BeginInvoke)将操作的执行编组到UI线程上,以便可以安全地访问UI元素.
我仍然看到您的代码有问题.在运行于工作线程上的OpenSaveFileDialog方法中,您正在调用SaveFileDiaglog的构造函数,它当然是UI元素.您就是无法做到这一点.值得重复.您不能从辅助线程对窗体或控件执行任何操作.这包括调用构造函数.