Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 选项卡控件中的多个web浏览器_C#_Multithreading_Winforms_Webbrowser Control_Tabcontrol - Fatal编程技术网

C# 选项卡控件中的多个web浏览器

C# 选项卡控件中的多个web浏览器,c#,multithreading,winforms,webbrowser-control,tabcontrol,C#,Multithreading,Winforms,Webbrowser Control,Tabcontrol,我目前正在开发一个应用程序,它有一个用于搜索字符串的文本框 我有一个tabControl和四个tabPages。在每个选项卡中都有一个WebBrowser。这些标签是永远不会被修改的,他们只是被支持在那里,永远停留 WebBrowser设置为导航到,比如:(“”+textBox.Text);当按下搜索按钮时 我想让web浏览器在不同的线程上运行,但我不确定如何运行。 有什么想法吗?我不认为有一种简单的方法可以让WebBrowser控件在单独的线程中运行,因为它需要在创建控件的线程上运行。我也不认

我目前正在开发一个应用程序,它有一个用于搜索字符串的文本框

我有一个tabControl和四个tabPages。在每个选项卡中都有一个WebBrowser。这些标签是永远不会被修改的,他们只是被支持在那里,永远停留

WebBrowser设置为导航到,比如:(“”+textBox.Text);当按下搜索按钮时

我想让web浏览器在不同的线程上运行,但我不确定如何运行。


有什么想法吗?

我不认为有一种简单的方法可以让WebBrowser控件在单独的线程中运行,因为它需要在创建控件的线程上运行。我也不认为有一种简单的方法可以为每个选项卡创建一个新的线程

我能找到的最接近某人发布解决方案的地方是:


WPF异步导航。如果您使用的是Windows窗体,则可以使用类嵌入它。解释如何在Windows窗体中承载WPF控件。

下面是我用于创建选项卡式浏览器的解决方案,其中每个浏览器都在单独的线程中运行。我必须警告你,这不是一个简单的解决方案,这种windows黑客攻击将导致许多次要的界面和线程问题。(所有这些都是可以解决的,但需要一些时间来测试您的应用程序)

该解决方案由一个容器面板(BrowserPanel)和一个嵌入式web浏览器面板(BrowserForm)组成。创建BrowserPanel将在单独的线程中启动其内部的web浏览器

这远不是一个完整的解决方案,但希望它能帮助您开始

1) 为user32.dll方法创建一个单独的类文件(您可能已经有了这些方法)

2) 创建容器控件

创建一个新类文件并将其命名为BrowserPanel。 此控件将位于主UI线程上,并充当下面web浏览器表单的占位符

public class BrowserPanel : Panel
{
}
3) 创建web浏览器窗体

创建一个新表单并将webbrowser控件放置在其上。将表单命名为BrowserForm。 此窗体将有自己的独立线程。 表单必须具有指向BrowserPanel的链接。(参见下面的源代码)

3) 添加创建代码

将以下代码添加到BrowserPanel类中

public class BrowserPanel : Panel
{               
    public BrowserForm Browser { get; private set; }

    private IntPtr _threadownerhandle;
    private IntPtr _threadformhandle;
    private Thread _thread;
    private AutoResetEvent _threadlock;

    public BrowserPanel(): Panel
    {
        Resize += OnResize;
        ThreadCreate();            
    }   

    public void ThreadCreate()
    {
        // The following line creates a window handle to the BrowserPanel
        // This has to be done in the UI thread, but the handle can be used in an other thread
        _threadownerhandle = Handle;

        // A waiting lock
        _threadlock = new AutoResetEvent(false);

        // Create the thread for the BrowserForm
        _thread = new Thread(ThreadStart);
        _thread.SetApartmentState(ApartmentState.STA);
        _thread.Start();

        // Let's wait until the Browser object has been created
        _threadlock.WaitOne();
    }


    private void ThreadStart()
    {   
        // This a NOT the UI thread
        try
        {
            // Create the BrowserForm in a new thread
            Browser = new BrowserForm(this);

            // Get the handle of the form. This has to be done in the creator thread
            _threadformhandle = Browser.Handle;

            // The magic. The BrowserForm is added to the BrowserPanel
            User32.SetParent(_threadformhandle, _threadownerhandle);

            // Make the BrowserForm the same size as the BrowserPanel
            ThreadWindowUpdate();
        }
        finally
        {
            // Notify the BrowserPanel we are finished with the creation of the Browser
            _threadlock.Set();
        }

        try
        {
            // With the next line a (blocking) message loop is started
            Application.Run(Browser); 
        }
        finally
        {
            Browser.Dispose();
        }
    }

    private void OnResize(object sender, EventArgs e)
    {
        // Resizing the BrowserPanel will resize the BrowserForm too
        if (Browser != null) ThreadWindowUpdate();
    }               

    public void ThreadWindowUpdate()
    {
        if (Browser == null) return;
        User32.SetWindowPos(_threadformhandle, IntPtr.Zero, 0, 0, Width, Height, 0);
    }
}
4) 向BrowserPanel类添加更多逻辑

    public void Focus()
    {
        // normal focus will not work
        User32.SetFocus(_threadformhandle);
    }
我们做完了吗。不

从主UI线程调用浏览器控件方法可能会导致线程异常。对于许多WebBrowser方法,您必须在BrowserForm中创建一个包装器,如下所示。某些WebBrowser方法可以从另一个线程调用而不会出现问题。通过反复试验找出答案

    public void BrowserPrint()
    { 
        if (InvokeRequired)
            BeginInvoke(new MethodInvoker(() => { webBrowser1.Print(); }));
        else
            webBrowser1.Print();
    }

    public void BrowserClose()
    {
        Browser.DialogResult = DialogResult.Cancel; // or whatever
        if (InvokeRequired)
            BeginInvoke(new MethodInvoker(() => { this.Close(); }));
        else
            this.Close();
    }
调用主UI线程的WebBrowser事件也是如此。例如:

    In BrowserForm:

    private void webBrowser1_StatusTextChange(object sender, StatusTextChangeEventArgs e)
    {
        Panel.EventStatusTextChange(e.text);
    }

    In BrowserPanel:

    public void EventStatusTextChange(string text)
    {
        if (_statustext == text) return;
        _statustext s = text;
        if (InvokeRequired)
            BeginInvoke(new MethodInvoker(() => { Owner.StateChanged(this); }));
        else
            Owner.StateChanged(this);
    }
一些你必须注意的特殊事情:

  • 尽可能使用BeginInvoke而不是Invoke。如果使用阻塞调用对WebBrowser控件的调用将导致使用另一个阻塞调用的回调事件,则会发生死锁

  • 在另一个窗口中单击WebBrowser控件时,在主UI线程中创建的弹出菜单不会消失。(通过捕获WebBrowser控件的onclick事件并将其路由回主窗体来解决此问题)


为什么您需要在单独的线程上运行WebBrowser的每个实例?Sjaak的解决方案是可行的,但我遇到了这种方法的一个问题。似乎有些网站没有从主UI线程以外的不同线程正确加载webBrowser,例如一些flash应用程序。我正在考虑为每个选项卡使用完整的新流程。我还想知道,这是否就是谷歌Chrome使用进程而不是线程的原因。我刚刚测试了这个,它工作起来很有魅力。这个解决方案既简单又棒。与此同时,互联网上的其他人声称这是不可能的或非常困难的。旁注:BrowserPanel():Panel应该是BrowserPanel():base()。(它没有为我编译)
    public void BrowserPrint()
    { 
        if (InvokeRequired)
            BeginInvoke(new MethodInvoker(() => { webBrowser1.Print(); }));
        else
            webBrowser1.Print();
    }

    public void BrowserClose()
    {
        Browser.DialogResult = DialogResult.Cancel; // or whatever
        if (InvokeRequired)
            BeginInvoke(new MethodInvoker(() => { this.Close(); }));
        else
            this.Close();
    }
    In BrowserForm:

    private void webBrowser1_StatusTextChange(object sender, StatusTextChangeEventArgs e)
    {
        Panel.EventStatusTextChange(e.text);
    }

    In BrowserPanel:

    public void EventStatusTextChange(string text)
    {
        if (_statustext == text) return;
        _statustext s = text;
        if (InvokeRequired)
            BeginInvoke(new MethodInvoker(() => { Owner.StateChanged(this); }));
        else
            Owner.StateChanged(this);
    }