在单独的线程上创建WebBrowser控件

时间:2017-07-06 01:26:23

标签: c# winforms

好吧,我有一个包含5个WebBrowser控件的表单,每个控件都打开一个页面并处理DocumentComplete事件。问题是有时事件不会触发。阅读后我遇到了一个答案,如果线程繁忙,NavigatedDocumentComplete等事件将不会触发,所以我尝试在单独的线程中创建WebBrowser然后添加它到导致交叉线程异常的形式

代码

        new Thread(() =>
        {
            WebBrowser1 = new WebBrowser
            {
                Location = new Point(15, 14),
                MinimumSize = new Size(20, 20),
                Name = "WebBrowser1",
                ScriptErrorsSuppressed = true,
                Size = new Size(250, 370),
                TabIndex = 0,
                Url = new Uri("", UriKind.Relative)
            };

            ((WebBrowser_V1) WebBrowser1.ActiveXInstance).NewWindow += (string u, int f, string n, ref object d, string h, ref bool p) =>
            {
                p = true;
                WebBrowser1.Navigate(u);
            };

            WebBrowser1.DocumentCompleted += async (sender, args) =>
            {
                // Code...
            };

            WebBrowser1.Navigated += (sender, args) =>
            {
                // Code...
            };

            WebBrowser1.Navigate(Service.LinkTL.Find(_ => _.Valid)?.Use()?.Address);

            //Cross-Thread Exception
            base.Invoke(new MethodInvoker(() =>
            {
                Controls.Add(WebBrowser1);
            }));

            Application.Run();
        }) {ApartmentState = ApartmentState.STA}.Start();

我需要WebBrowser控件在表单中可见,但能够拦截所有触发的事件。

编辑:如果我调用WebBrowser,我会获得Controls created on one thread cannot be parented to a control on a different thread例外。

1 个答案:

答案 0 :(得分:0)

您需要在跨线程操作上检查InvokeRequired(请参阅:https://msdn.microsoft.com/en-us/library/system.windows.forms.control.invokerequired(v=vs.110).aspx)。

如果是,则需要从代码中调用委托。

 myFormControl1.Invoke(myFormControl1.myDelegate);

典型的模式是:

private void DoFoo() {
    if (myFormControl1.InvokeRequired) {
        myFormControl1.Invoke(new MethodInvoker(() => { DoFoo(); }));
    } else {
        object1.Visible = true;
        object2.Visible = false;
    }
}

此主题讨论了如何干净地实现此方法而不会弄脏您的代码:Automating the InvokeRequired code pattern

线程中一个有用的答案,显示了一个处理调用的静态方法:

public static void InvokeIfRequired(this Control control, MethodInvoker action)
{

    if (control.InvokeRequired) {
        control.Invoke(action);
    } else {
        action();
    }
}

然后定义代表如:

richEditControl1.InvokeIfRequired(() =>
{
    // Do anything you want with the control here
    richEditControl1.RtfText = value;
    RtfHelpers.AddMissingStyles(richEditControl1);
});

使用它来完成您的具体问题:

public static class myExtension
{
    public static void InvokeIfRequired(this Control control, MethodInvoker action)
    {
        // See Update 2 for edits Mike de Klerk suggests to insert here.

        if (control.InvokeRequired)
        {
            control.Invoke(action);
        }
        else
        {
            action();
        }
    }
}

// ...

protected WebBrowser WebBrowser1;
public void loadBrowser()
{
    new Thread(() =>
        {

            var WebBrowser1 = new WebBrowser
            {
                Location = new Point(15, 14),
                MinimumSize = new Size(20, 20),
                Name = "WebBrowser1",
                ScriptErrorsSuppressed = true,
                Size = new Size(250, 370),
                TabIndex = 0,


            };
            var form = new Form()
            {
                Name = "Other Form",
                Text = "Thread Start Browser",
                AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F),
                AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font,
                ClientSize = new System.Drawing.Size(284, 261),
            };
            form.Controls.Add(WebBrowser1);
            form.Show();
            //((WebBrowser_V1)WebBrowser1.ActiveXInstance).NewWindow += (string u, int f, string n, ref object d, string h, ref bool p) =>
            //{
            //    p = true;
            //    WebBrowser1.Navigate(u);
            //};

            //WebBrowser1.DocumentCompleted += async (sender, args) =>
            //{
            //    // Code...
            //};

            WebBrowser1.Navigated += (sender, args) =>
            {
                // Code...
            };

            //WebBrowser1.Navigate(Service.LinkTL.Find(_ => _.Valid)?.Use()?.Address);

             WebBrowser1.Navigated += (sender, args) =>
            {
                // Code...
            };

            //WebBrowser1.Navigate(Service.LinkTL.Find(_ => _.Valid)?.Use()?.Address);

            this.InvokeIfRequired(() =>
            {
                this.WebBrowser1 = WebBrowser1;
                this.CreatedBrowser(); 

            });

            Application.Run();
        })

        { ApartmentState = ApartmentState.STA }.Start();
    }
private void Form1_Load(object sender, EventArgs e)
{
    loadBrowser();
}
public void CreatedBrowser()
{
    Console.WriteLine("Created browser");
}
private void btnGo_Click(object sender, EventArgs e)
{
    string url = txtUrl.Text;
    WebBrowser1.InvokeIfRequired(() => WebBrowser1.Navigate(url));
}

这是一个屏幕截图:

WebBrowser created on a separate thread