简体   繁体   中英

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)". 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?

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. 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. But that happens on another thread, the main UI thread thanks to the Invoke() call.

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. A workaround is to move the Navigate call inside the anonymous method.

You probably arrived at this code because you also got an IllegalOperationException when you tried to display the dialog without the Invoke() call. 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. Which is actually legal in Windows, Winforms fumbles the check.

You can work around this by 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. 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();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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