That side-project again. One of the primary outputs of the
app is data and text collected from web sites in (mostly) HTML format. This can
most simply be wrapped to create an HTML document and displayed in the
WebBrowser control made available by Visual Studio. This works well up to a
point – the WebBrowser control is a “wrapper” for the Internet Explorer COM
object but only exposes a limited set of events and properties. There is also
an issue with multi-threading – using the WebBrowser control makes it very
difficult to update the UI thread from (say) a BackgroundWorker.
I was happy with the HTML presentation within the WebBrowser
control but wanted to handle any user clicks on links by pushing the link off
to the default browser on the relevant machine. An external link could require
a whole host of support facilities not enabled by the WebBrowser control so
this made sense as well as (probably) reflecting the expectations of any user.
So, how to push responsibility to those external links off
to the user’s preferred browser. Turns out to be easy to handle in the
Navigating event:
private void webViewer_Navigating(object sender, WebBrowserNavigatingEventArgs e)
{
if (!hasStarted)
{
hasStarted = true;
return;
}
e.Cancel = true;
var startInfo = new ProcessStartInfo
{
FileName = e.Url.ToString()
};
Process.Start(startInfo);
}
The bool pageShown is set false before loading the original page in
the WebBrowser control and this is set true by the first Navigating event
resulting from that page load. Subsequent Navigating events before the
pageShown value is reset are pushed off to the default application (a web
browser certainly) that handles URLs.
This worked fine up until the HTML content being displayed contained
an <iframe> tag. While loading graphics and scripts from remote servers
caused no issues filling the content of an <iframe> triggered a
Navigating event. So I needed to know if a Navigating event was being triggered
by an <iframe> requesting content as this may happen after the main
content had loaded and the DocumentCompleted event fired (so I could not
reliably re-set the pageShown Boolean within that event handler.
StackOverflow is your friend here of course. Questions like “possible to detect whether an iframe is loaded in the navigating event” hit the nail on the head. This starts a trail that initially leads to a March 2006 Code Project post by Jeroen Landheer http://www.codeproject.com/Articles/13598/Extended-NET-2-0-WebBrowser-Control#CreateIWebBrowser2 but also mentions the BeforeNavigate2 event thus exposed and the opportunity to insert a test into the BrowserExtendedNavigatingEventArgs class.
There is also this http://www.codeproject.com/Articles/18935/The-most-complete-C-Webbrowser-wrapper-control CodeProject article from May 2007 which is worthy of investigation if you need to follow a similar path.
It looks like the Windows System SHDocVw.dll exposes a whole range of additional interfaces and events for IE not exposed by the WebBrowser control but getting a handle on them takes a little effort to say the least – hence the projects to sub-class the WebBrowser in the two CodeProject articles mentioned.
Anyway – the only snag remaining is that the standard WebBrowser Navigating event fires before the BeforeNavigate2 event which is slightly counter-intuitive. So the trick is to move the functionality from the Navigating event into the WebBrowserExtendedEvents class BeforeNavigate2 event by adding a Boolean property to that class for the main form to notify the start (and end) of the main document loading.
As an aside, I did wonder if there were alternatives to the provided WebBrowser control. Turns out there have been projects to “wrap” WebKit and FireFox browsers but what looks like the most able and up-to-date project provides a wrapper for the Chrome browser and the GitHub repository is here https://github.com/cefsharp/CefSharp/wiki . However if you were expecting a nice Visual Studio control to “drop onto” a form then you might be disappointed. I think the idea is that you will want to wrap the functionality in a custom control of your own perhaps supporting tabs and other browser flummery. The simplest way to add the CefSharp control to your form is programmatically in the form class constructor thus:
StackOverflow is your friend here of course. Questions like “possible to detect whether an iframe is loaded in the navigating event” hit the nail on the head. This starts a trail that initially leads to a March 2006 Code Project post by Jeroen Landheer http://www.codeproject.com/Articles/13598/Extended-NET-2-0-WebBrowser-Control#CreateIWebBrowser2 but also mentions the BeforeNavigate2 event thus exposed and the opportunity to insert a test into the BrowserExtendedNavigatingEventArgs class.
There is also this http://www.codeproject.com/Articles/18935/The-most-complete-C-Webbrowser-wrapper-control CodeProject article from May 2007 which is worthy of investigation if you need to follow a similar path.
It looks like the Windows System SHDocVw.dll exposes a whole range of additional interfaces and events for IE not exposed by the WebBrowser control but getting a handle on them takes a little effort to say the least – hence the projects to sub-class the WebBrowser in the two CodeProject articles mentioned.
Anyway – the only snag remaining is that the standard WebBrowser Navigating event fires before the BeforeNavigate2 event which is slightly counter-intuitive. So the trick is to move the functionality from the Navigating event into the WebBrowserExtendedEvents class BeforeNavigate2 event by adding a Boolean property to that class for the main form to notify the start (and end) of the main document loading.
As an aside, I did wonder if there were alternatives to the provided WebBrowser control. Turns out there have been projects to “wrap” WebKit and FireFox browsers but what looks like the most able and up-to-date project provides a wrapper for the Chrome browser and the GitHub repository is here https://github.com/cefsharp/CefSharp/wiki . However if you were expecting a nice Visual Studio control to “drop onto” a form then you might be disappointed. I think the idea is that you will want to wrap the functionality in a custom control of your own perhaps supporting tabs and other browser flummery. The simplest way to add the CefSharp control to your form is programmatically in the form class constructor thus:
private CefSharp.WinForms.ChromiumWebBrowser mBrowser;
public Form1()
{
InitializeComponent();
mBrowser = new CefSharp.WinForms.ChromiumWebBrowser("http://www.hanselman.com/blog/")
{
Dock = DockStyle.Fill,
};
this.Controls.Add(mBrowser);
}
It works and seems very fast and responsive. The documentation currently takes the form of “read the code” so it was not immediately obvious I could solve my problems by switching the underlying browser but at first glance it did look possible. What stopped me spending too much time here was the requirement to build your project against the 32bit or 64 bit version. While there are fewer 32bit PCs out there it still represents an additional issue should this project ever be shared with others even informally.
No comments:
Post a Comment