簡體   English   中英

通過線程在另一個表單上顯示一個表單作為對話框

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

以下代碼給出了“跨線程操作”異常。 僅僅因為“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()調用是問題所在。 這會強制創建控件的本機窗口句柄,並在工作線程上發生。 一段時間后,您強制使用ShowDialog()調用創建表單的本機窗口。 但這發生在另一個線程上,主要的UI線程得益於Invoke()調用。

現在存在不匹配,表單的窗口由主線程擁有,但瀏覽器的窗口由工作線程擁有。 Winforms步驟提醒您這是非法的,子窗口必須由與容器窗口相同的線程擁有。 解決方法是在匿名方法內移動Navigate調用。

您可能已經到達此代碼,因為當您嘗試在沒有調用Invoke()的情況下顯示對話框時,您也遇到了IllegalOperationException。 如果你真的想在工作線程上運行對話框,這將是正常的事情。 Winforms引發異常,因為它不喜歡窗口的所有者是另一個線程上的窗口。 這在Windows中實際上是合法的,Winforms摸索了支票。

你可以通過pinvoking 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();

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM