2014-03-03 45 views
0

我目前正在研究一個應用程序,它有一個用於搜索字符串的文本框。標籤控件中的多個Web瀏覽器

我有一個tabControl有四個tabPages。在每個標籤中都有一個WebBrowser。這些標籤永遠不會被修改,他們只是想要在那裏,永遠留下來。

WebBrowsers設置爲導航到,讓我們說:(「https://www.google.se/#q=」+ textBox.Text);當按下搜索按鈕時。

我想使Web瀏覽器運行在不同的線程上,但我不知道如何。

有什麼想法?

+2

到底爲什麼你需要一個單獨的線程運行web瀏覽器的每個實例? – Noseratio

+0

Sjaak的解決方案有效,但我遇到了這種方法的問題。似乎有一些網站無法使用webBrowser從不同於主UI線程的線程正確加載,例如某些Flash應用程序。我正在考慮爲每個選項卡使用完整的新流程。我也想知道,如果這就是Google Chrome使用進程而不是線程的原因。 – xamid

回答

0

我不認爲有一個簡單的方法讓WebBrowser控件在單獨的線程中運行,因爲它需要在創建控件的線程上工作。我也不認爲有一個簡單的方法來爲每個標籤創建一個新的線程。

我能找到的地方有人張貼解決方法最接近的是:

WebBrowser Control in a new thread

1

下面是我用於創建標籤瀏覽器的解決方案,其中每個瀏覽器都在單獨的線程中運行。我必須警告你,這不是一個簡單的解決方案,這種windows黑客會導致很多小的界面和線程問題。 (所有解決方法,但需要一些時間來測試您的應用程序)

該解決方案由容器面板(BrowserPanel)和嵌入式Web瀏覽器面板(BrowserForm)組成。創建BrowserPanel將在一個單獨的線程中啓動一個Web瀏覽器。

這遠不是一個完整的解決方案,但希望它能幫助你開始。

1)創建的方法user32.dll的一個單獨的類文件(你可能已經有這些)

public static class User32 
{ 
    [DllImport("user32.dll")] 
    public static extern IntPtr SetParent(IntPtr wnd, IntPtr parent); 

    [DllImport("user32.dll")] 
    public static extern bool SetWindowPos(IntPtr wnd, IntPtr parent, int x, int y, int w, int h, uint flags); 

    [DllImport("user32.dll")] 
    public static extern IntPtr SetFocus(IntPtr wnd); 
} 

2)創建容器控件

創建一個新的類文件,並將其命名BrowserPanel 。 此控件將位於主UI線程上,並充當下面Web瀏覽器表單的佔位符。

public class BrowserPanel : Panel 
{ 
} 

3)創建Web瀏覽器表單

創建一個新的形式,並在其上放置WebBrowser控件。將該窗體命名爲BrowserForm。 此表單將擁有自己的獨立線程。 表單必須有一個指向BrowserPanel的鏈接。 (見下面的源代碼)

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

    public BrowserForm(BrowserPanel panel) 
    { 
     Panel = panel; 
     InitializeComponent(); 
     FormBorderStyle = FormBorderStyle.None; 
     TopLevel = false; 
    } 
} 

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,而不是調用的。如果使用阻止調用對WebBrowser控件的調用將導致帶有另一個阻止調用的回調事件,則會發生死鎖。在另一個窗口中單擊WebBrowser控件時,在主UI線程中創建的彈出菜單不會消失。 (通過捕捉WebBrowser控件和路線,這回主要形式的onclick事件解決此問題)

+0

我剛剛測試過它,它的功能就像一個魅力。這個解決方案很簡單,很棒。同時,互聯網上的其他人聲稱這是不可能的或非常困難的。 – xamid

+0

旁註:** BrowserPanel():Panel **應該是** BrowserPanel():base()**。 (它沒有爲我編譯) – xamid