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