简体   繁体   English

通过线程在另一个表单上显示一个表单作为对话框

[英]Show a form as a dialog on another form via thread

The following code gives "cross thread operation" exception. 以下代码给出了“跨线程操作”异常。 Just because of "form2.ResumeLayout(false)". 仅仅因为“form2.ResumeLayout(false)”。 And if this statement is commented, I can't see browser in form. 如果这个陈述被评论,我看不到浏览器的形式。 I know the need of ResumeLayout(false) but is there a solution? 我知道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"]);
        }));
    }
}
}

The WebBrowser.Navigate() call is the problem. WebBrowser.Navigate()调用是问题所在。 That forces the native window handle for the control to be created and that happens on the worker thread. 这会强制创建控件的本机窗口句柄,并在工作线程上发生。 Some time later you force the form's native window to be created with the ShowDialog() call. 一段时间后,您强制使用ShowDialog()调用创建表单的本机窗口。 But that happens on another thread, the main UI thread thanks to the Invoke() call. 但这发生在另一个线程上,主要的UI线程得益于Invoke()调用。

Now there's a mismatch, the form's window is owned by the main thread but the browser's window is owned by the worker thread. 现在存在不匹配,表单的窗口由主线程拥有,但浏览器的窗口由工作线程拥有。 Winforms steps in to remind you that this is illegal, the child windows must be owned by the same thread as the container window. Winforms步骤提醒您这是非法的,子窗口必须由与容器窗口相同的线程拥有。 A workaround is to move the Navigate call inside the anonymous method. 解决方法是在匿名方法内移动Navigate调用。

You probably arrived at this code because you also got an IllegalOperationException when you tried to display the dialog without the Invoke() call. 您可能已经到达此代码,因为当您尝试在没有调用Invoke()的情况下显示对话框时,您也遇到了IllegalOperationException。 Which would be the normal thing to do if you actually want to run the dialog on a worker thread. 如果你真的想在工作线程上运行对话框,这将是正常的事情。 Winforms raises the exception because it doesn't like the Owner of a window to be a window on another thread. Winforms引发异常,因为它不喜欢窗口的所有者是另一个线程上的窗口。 Which is actually legal in Windows, Winforms fumbles the check. 这在Windows中实际上是合法的,Winforms摸索了支票。

You can work around this by pinvoking SetParent(). 你可以通过pinvoking SetParent()来解决这个问题。 And byte my tongue, in this very special case and never do this in any other circumstance, by temporarily setting Control.CheckForIllegalCrossThreadCalls to false. 在这个非常特殊的情况下,通过临时将Control.CheckForIllegalCrossThreadCalls设置为false,在任何其他情况下都不会这样做。 Emphasis on temporarily . 暂时强调。 Additional work is needed to ensure the form is actually modal to the window on the main thread and that it gets re-enabled before the dialog disappears: 需要额外的工作来确保表单实际上是主线程上窗口的模态,并在对话框消失之前重新启用它:

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();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM