早期 VS2003 開發瀏覽器最大痛點:**彈出窗口 (window.open) 無法攔截**,導致新窗口仍由系統默認瀏覽器打開。VS2005 引入 WebBrowser.NewWindow 事件,使得**在標籤頁中捕獲並打開彈出鏈接**成為可能,這是本專案的核心技術突破。
NewWindow 事件)MenuStrip:頂層菜單ToolStrip:工具欄 + 地址欄 (含 ToolStripComboBox)TabControl:瀏覽器主體,每個 TabPage 承載一個 WebBrowserStatusStrip:底部狀態欄 (含 ToolStripProgressBar/ToolStripStatusLabel)Dock 屬性適應窗體縮放private WebBrowser GetCurrentBrowser() {
return (WebBrowser)tabControl1.SelectedTab.Controls[0];
}
private Uri GetUrl(string address) {
string temp = address.Trim();
if (!temp.StartsWith("https://") && !temp.StartsWith("https://") && !temp.StartsWith("ftp://"))
temp = "https://" + temp;
return Uri.TryCreate(temp, UriKind.Absolute, out var uri) ? uri : new Uri("about:blank");
}
private void NewPage(string url = "about:blank") {
var page = new TabPage("新標籤頁");
var browser = CreateBrowser();
browser.Url = GetUrl(url);
page.Controls.Add(browser);
tabControl1.TabPages.Add(page);
tabControl1.SelectedTab = page;
}
private WebBrowser CreateBrowser() {
var wb = new WebBrowser { Dock = DockStyle.Fill };
wb.Navigated += Browser_Navigated;
wb.NewWindow += Browser_NewWindow;
wb.ProgressChanged += Browser_ProgressChanged;
wb.StatusTextChanged += Browser_StatusTextChanged;
return wb;
}
NewWindow 事件 —— 核心突破private void Browser_NewWindow(object sender, CancelEventArgs e) {
var sourceBrowser = (WebBrowser)sender;
var newUrl = sourceBrowser.StatusText; // 獲取目標 URL
// 阻止默認彈出行為
e.Cancel = true;
// 在新標籤頁打開
var newPage = new TabPage("載入中...");
var newBrowser = CreateBrowser();
newBrowser.Navigate(newUrl);
newPage.Controls.Add(newBrowser);
tabControl1.TabPages.Add(newPage);
tabControl1.SelectedTab = newPage;
}
關鍵點:e.Cancel = true禁止默認彈出;StatusText在此事件中包含目標 URL;新WebBrowser必須重新掛載所有事件。
Navigated 事件 —— 同步 UI 狀態private void Browser_Navigated(object sender, WebBrowserNavigatedEventArgs e) {
var browser = (WebBrowser)sender;
var page = (TabPage)browser.Parent;
// 僅更新當前選中標籤的地址欄
if (browser == GetCurrentBrowser())
tscbUrl.Text = browser.Url?.ToString() ?? "about:blank";
// 更新標籤標題 (截取前 20 字符)
page.Text = TruncateTitle(browser.DocumentTitle);
}
ProgressChanged / StatusTextChangedprivate void Browser_ProgressChanged(object sender, WebBrowserProgressChangedEventArgs e) {
if (e.MaximumProgress > 0) {
toolStripProgressBar1.Maximum = (int)e.MaximumProgress;
toolStripProgressBar1.Value = (int)Math.Min(e.CurrentProgress, e.MaximumProgress);
}
}
private void Browser_StatusTextChanged(object sender, EventArgs e) {
var browser = (WebBrowser)sender;
if (browser == GetCurrentBrowser())
toolStripStatusLabel1.Text = browser.StatusText;
}
// 導航
backButton.Click += (s,e) => { GetCurrentBrowser().GoBack(); UpdateNavButtons(); };
forwardButton.Click += (s,e) => { GetCurrentBrowser().GoForward(); UpdateNavButtons(); };
refreshButton.Click += (s,e) => GetCurrentBrowser().Refresh();
stopButton.Click += (s,e) => GetCurrentBrowser().Stop();
homeButton.Click += (s,e) => GetCurrentBrowser().GoHome();
searchButton.Click += (s,e) => GetCurrentBrowser().GoSearch();
// 地址欄回車/Go 按鈕
gotoButton.Click += (s,e) => GetCurrentBrowser().Navigate(GetUrl(tscbUrl.Text));
tscbUrl.KeyDown += (s,e) => { if (e.KeyCode == Keys.Enter) GetCurrentBrowser().Navigate(GetUrl(tscbUrl.Text)); };
// 菜單功能 (WebBrowser 內置對話框)
saveasToolStripMenuItem.Click += (s,e) => GetCurrentBrowser().ShowSaveAsDialog();
printToolStripMenuItem.Click += (s,e) => GetCurrentBrowser().ShowPrintDialog();
printPreToolStripMenuItem.Click += (s,e) => GetCurrentBrowser().ShowPrintPreviewDialog();
pageSetupToolStripMenuItem.Click += (s,e) => GetCurrentBrowser().ShowPageSetupDialog();
propeToolStripMenuItem.Click += (s,e) => GetCurrentBrowser().ShowPropertiesDialog();
// 更新後退/前進按鈕啟用狀態
private void UpdateNavButtons() {
var wb = GetCurrentBrowser();
backButton.Enabled = wb.CanGoBack;
forwardButton.Enabled = wb.CanGoForward;
}
private void tabControl1_SelectedIndexChanged(object sender, EventArgs e) {
var wb = GetCurrentBrowser();
tscbUrl.Text = wb.Url?.ToString() ?? "about:blank";
tabControl1.SelectedTab.Text = TruncateTitle(wb.DocumentTitle);
UpdateNavButtons();
}
private void tabControl1_DoubleClick(object sender, EventArgs e) {
// 至少保留一個標籤
if (tabControl1.TabPages.Count <= 1) {
GetCurrentBrowser().Navigate("about:blank");
tabControl1.SelectedTab.Text = "空白頁";
return;
}
// 隱藏 TabControl 防止切換閃爍
tabControl1.Visible = false;
var wb = GetCurrentBrowser();
wb.Dispose(); // 釋放非託管資源 (ActiveX 底層)
tabControl1.TabPages.Remove(tabControl1.SelectedTab);
tabControl1.SelectedTab = tabControl1.TabPages[tabControl1.TabPages.Count - 1];
tabControl1.Visible = true;
}
private void Form1_Load(object sender, EventArgs e) {
NewPage(); // 默認打開首頁 (GoHome 在 CreateBrowser 中調用)
}
StatusText 在某些 JS 彈出 (javascript:void(0)) 中無法獲取真實 URL,導致新標籤頁打開失敗。WebBrowser 底層基於 IE 内核 (MSHTML),不支持現代 Web 標準 (ES6+, CSS Grid, WebGL 等)。Dispose() 釋放底層 ActiveX 資源,否則內存洩漏嚴重。Microsoft.Web.WebView2)、同樣支持標籤頁架構。TabBrowser/
├── Form1.cs // 主邏輯 (含所有事件處理)
├── Form1.Designer.cs // 設計器生成代碼 (控件初始化)
├── Program.cs // 入口
└── Resources/ // 圖標等資源