How to easily capture the NewWindow3 event and detect a JavaScript window.close() call with System.Windows.Forms.WebBrowser





5.00/5 (2 votes)
This article describes a simple method of capturing a NewWindow event for the Windows Forms WebBrowser control, and also how to detect (and potentially cancel) a JavaScript window.close() call.
Introduction
The purpose of this article is to explain a simple way of capturing a NewWindow
event for the Windows Forms WebBrowser
control, and a (slightly) less simple way of detecting and acting upon a JavaScript window.close()
call. A solution to both of these problems is particularly useful when developing a Windows Forms app with tabbed browsing.
Background
I ran across the above problems while creating a tabbed browsing interface within a desktop application that was going to be used to view two web applications used within my company. The purpose of this app was mainly to allow a modified copy and paste between two web pages. This would have been simple enough if not for the frequent opening of new windows and JavaScript window.close()
calls!
There are numerous articles around (mainly to do with the NewWindow
event), but they all seem to take an overly complicated approach, extending the WebBrowser
control and implementing interfaces, which is really not needed. I realised there was a simpler way, that's why I am writing this article.
NewWindow Event
Problem
After creating the tabbed browsing interface, I found that when a new window was being opened from the page, a completely new window would be opened. The main issue with this was the fact that the site being browsed required a username and password login, which meant that when an instance of IE was being opened up, the user had to log in again as IE was executing from a different thread (which messes up cookies relating to the username/passwordd I assume).
Easy I thought, simply use the WebBrowser
's NewWindow
event, capture the URL, and pop open a new tab, directing it to the URL. However, this is where problem number one reared its ugly head...the WebBrowser
's NewWindow
event provides us with CancelEventArgs
which allows us to stop the new window from opening by setting e.Cancel
to true
, but it doesn't provide us with any more useful info such as the URL etc.
Solution
The main solution that people seemed to be using after a bit of browsing the web was like the solution offered here on CodeProject which consists of extending the WebBrowser
control to implement IWebBrowser2
and DWebBrowserEvents2
. However, although this does work, there is a much simpler way to get access to a wider range of events.
Firstly, you need to add a COM reference to your project 'Microsoft Internet Controls':
You then need to add using SHDocVw;
to your code file. This namespace gives you access to SHDocVw.WebBrowser
, which is the class we need. Now comes the easy bit.
The System.Windows.Forms.WebBrowser
control has a property called ActiveXInstance
of type object
. All you have to do is cast the ActiveXInstance
property to SHDocVw.WebBrowser
and you have access to the full range of events that the ActiveX browser offers, including NewWindow3
which offers many more event arguments including the URL. Look at the code below to see an example of assigning an event handler to the NewWindow3
event:
System.Windows.Forms.WebBrowser webBrowser = new System.Windows.Forms.WebBrowser();
SHDocVw.WebBrowser axBrowser = (SHDocVw.WebBrowser)webBrowser.ActiveXInstance;
axBrowser.NewWindow3 += new DWebBrowserEvents2_NewWindow3EventHandler(Browser_NewWindow3);
window.close() from JavaScript
Problem
After discovering the solution to the new window problem, I quickly came upon a second problem. When a window was being closed via JavaScript, the WebBrowser
control was hanging. Easy this time, I thought. I have access to loads of events now through the ActiveX browser, all I need to do is find some sort of window closing/closed event and do whatever I need in there.
However, even though there is a WindowClosing
event, it is not fired when window.close()
is called from JavaScript. I have no idea why, and neither does anyone else I found who has discussed this problem online. Despite this, all is not lost!
Solution
I found this guy's blog where he describes a way overcoming the problem by overriding WndProc
, intercepting the destroy message, and then acting upon it however we wish. The code to achieve this can bee seen below (majority of it nicked off the aforementioned site).
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case WM_PARENTNOTIFY:
if (!DesignMode)
{
if (m.WParam.ToInt32() == WM_DESTROY)
{
Message newMsg = new Message();
newMsg.Msg = WM_DESTROY;
// Check for a parent tab
TabPage parentTab = this.Parent as TabPage;
// If parent tab found then remove it
if (parentTab != null)
{
TabControl browserTabs = parentTab.Parent as TabControl;
if (browserTabs != null)
{
browserTabs.TabPages.Remove(parentTab);
}
}
}
}
DefWndProc(ref m);
break;
default:
base.WndProc(ref m);
break;
}
}
Below is the full code for a class that extends the functionality of System.Windows.Forms.WebBrowser
by implementing the two solutions above:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace GoldMedal.TravelinkViewer.UI.User_Controls
{
/// Extends the functionality of System.Windows.Forms.WebBrowser
public class ExtendedWebBrowser : WebBrowser
{
#region Private Fields
private const int WM_PARENTNOTIFY = 0x210;
private const int WM_DESTROY = 2;
#endregion
/// Gets the underlying ActiveXBrowser instance and casts it as SHDocVw.WebBrowser
/// type to give access to greater functionality
public SHDocVw.WebBrowser ActiveXBrowser
{
get
{
return (SHDocVw.WebBrowser)ActiveXInstance;
}
}
/// Overrides WndProc to close parent tab
/// or form if WM_DESTORY message recieved from
/// javascript window.close method call.
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case WM_PARENTNOTIFY:
if (!DesignMode)
{
if (m.WParam.ToInt32() == WM_DESTROY)
{
Message newMsg = new Message();
newMsg.Msg = WM_DESTROY;
// Check for a parent tab
TabPage parentTab = this.Parent as TabPage;
// If parent tab found then remove it
if (parentTab != null)
{
TabControl browserTabs = parentTab.Parent as TabControl;
if (browserTabs != null)
{
browserTabs.TabPages.Remove(parentTab);
}
}
}
}
DefWndProc(ref m);
break;
default:
base.WndProc(ref m);
break;
}
}
}
}