下面的代码给出了“跨线程操作”的异常。仅仅因为“form2.ResumeLayout(false)”。如果这条语句被评论,我就看不到浏览器的形式。我知道需要 ResumeLayout(false) 但有解决方案吗?
namespace WindowsFormsApplication1
{
public partial class Form1: Form
{
private System.ComponentModel.IContainer components = null;
protected override void Dispose(bool disposing)
{ if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); }
private System.Windows.Forms.Button button1;
public Form1()
{
this.button1 = new System.Windows.Forms.Button();
this.SuspendLayout();
this.button1.Location = new System.Drawing.Point(64, 47);
this.button1.Text = this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(75, 23);
this.button1.Click += new System.EventHandler(this.button1_Click);
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.ClientSize = new System.Drawing.Size(284, 262);
this.Controls.Add(this.button1);
this.Text = this.Name = "Form1";
this.ResumeLayout(false);
}
private void button1_Click(object sender, EventArgs e)
{
Class1 clss = new Class1();
clss.startme();
}
}
class Class1
{
public void startme()
{
Thread thread = new Thread(new ParameterizedThreadStart(Run));
thread.SetApartmentState(ApartmentState.STA);
thread.Start(null);
}
private void Run(object j)
{
WebBrowser webBrowser1 = new WebBrowser();
webBrowser1.Dock = DockStyle.Fill;
webBrowser1.Navigate("https://dshift.sharepoint.com");
Form form2 = new Form();
form2.SuspendLayout();
form2.Controls.Add(webBrowser1);
form2.ResumeLayout(false);
Application.OpenForms["Form1"].Invoke(new MethodInvoker(delegate
{
form2.ShowDialog(Application.OpenForms["Form1"]);
}));
}
}
}
最佳答案
WebBrowser.Navigate() 调用是问题所在。这会强制创建控件的 native 窗口句柄,并且发生在工作线程上。一段时间后,您强制使用 ShowDialog() 调用创建窗体的 native 窗口。但由于 Invoke() 调用,这发生在另一个线程上,主 UI 线程。
现在有一个不匹配,窗体的窗口由主线程拥有,但浏览器的窗口由工作线程拥有。 Winforms 介入提醒你这是非法的,子窗口必须与容器窗口属于同一个线程。解决方法是将 Navigate 调用移到匿名方法中。
您得到这段代码的原因可能是,当您尝试在不调用 Invoke() 的情况下显示对话框时,您还遇到了 IllegalOperationException。如果您真的想在工作线程上运行对话框,这将是正常的做法。 Winforms 引发异常是因为它不喜欢窗口的所有者是另一个线程上的窗口。这在 Windows 中实际上是合法的,Winforms 搞砸了检查。
您可以通过调用 SetParent() 来解决这个问题。并且咬住我的舌头,在这种非常特殊的情况下,永远不要在任何其他情况下这样做,暂时将 Control.CheckForIllegalCrossThreadCalls 设置为 false。强调暂时。需要额外的工作来确保表单实际上对主线程上的窗口是模态的,并且它在对话框消失之前重新启用:
var owner = Application.OpenForms["Form1"];
form2.Load += delegate {
// NOTE: just as a workaround for the Owner bug!!
Control.CheckForIllegalCrossThreadCalls = false;
form2.Owner = owner;
Control.CheckForIllegalCrossThreadCalls = true;
owner.BeginInvoke(new Action(() => owner.Enabled = false));
};
form2.FormClosing += new FormClosingEventHandler((s, ea) => {
if (!ea.Cancel) {
owner.Invoke(new Action(() => owner.Enabled = true));
form2.Owner = null;
}
});
form2.ShowDialog();
关于c# - 通过线程将表单显示为另一个表单上的对话框,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10765792/