I’ve been working on a sample application that needs to load some generated display content into the Web Browser control and I’ve been wanting to interact with this content. One of the things I want to do is use some transitions like modal overlays and activity displays while pages load – in other words do some Ajax based UI functionality but with data that is provided by the host application.
The process for this goes something like this:
- Bring up the form
- Based on parameters provided as input render the form with HTML content
- Interact with the page (like reload the display as changes are made or stuff data into the dom)
One of the operations that need to occur are re-freshes as new data is loaded so I like to display modal overlay. So in my page I have some script code that uses jQuery and my internal script library with something simple like this:
<div id="modalDialog" style="width: 100px; height: 100px;">
<img id="loadingImg" src="loading.gif" />
return "Rick" && just to demonstrate ret parameter
So, is it possible to call this script function in the page? Absolutely. As you probably know global functions are actually defined on the DOM’s window object so you can access scripts through the document.parentWindow property of the DOM.
Now if you’re using Winforms .NET and the Web Browser control there’s a WebBrowser.Document property, but it doesn’t have a parentWindow property. The Document object .NET exposes is only a wrapper around the ‘real’ DOM COM object that Internet Explorer uses internally. The COM object is what we need to access in order to get at script code.
In order to do this we need to get the COM document object and then use Reflection to walk the hierarchy to do what essentially amounts to:
Here’s what this looks like inside of a small test form that contains only the Web Browser control and a button:
private void Form1_Load(object sender, EventArgs e)
private void btnOverlay_Click(object sender, EventArgs e)
// This code is much easier than what I came up with – thanks to Jarle in comments
// who pointed out the uhm obvious
object result = this.Browser.InvokeScript("showOverlay", false);
//// *** Get the COM DOM object (not the .NET Wrapper)
//object doc = this.Browser.Document.DomDocument;
//// *** Now you can use Reflection on the COM DOM
//object win = wwUtils.GetPropertyCom(doc, "parentWindow");
//object result = wwUtils.CallMethodCom(win, "showOverlay", false);
The browser is navigated to testpage on startup and the interactive page is loaded. Then when the button is clicked the the overlay is displayed on top of it. The result looks something like this:
.NET makes this very easy as it has a Browser.Document.InvokeScript() function that can execute any script on the page with parameters and return a result value easily.
Originally I had completely missed this though and instead used COM Interop as you can see in the commented code. Jarle pointed out the obvious in the comments – bit again by the vast functionality in the framework. I was looking for a way to access the Window property of the document, which is where scripts normally are ‘attached’. Alas, Microsoft used Document.InvokeScript, which is in any case much cleaner than having to create a manual wrapper.
Egg on my face. It isn’t the first time I’ve made a simple solution difficult :-}… But I’m keeping this up here for (my own) reference especially in light of some of the complications in the following section.
You can download the .NET sample code from here.
A few gotchas for FoxPro
When I looked into this again I actually was working on a sample FoxPro piece for a conference presentation which has the same requirements. No matter what I tried though I could not get the function call to work calling the document.parentWindow or document.script object. Whenever I would call the method I’d get a method not found error.
The following can be done from the command window:
oBrowser = CREATEOBJECT("InternetExplorer.Application")
obrowser.Visible = .t.
Works in the Web Browser Control or InternetExplorer.Application
As you can see in the FoxPro snippet this approach also works with the InternetExplorer.Application browser automation as well as in the Web Browser control. As long as you have access to the DOM document this approach should work regardless how the Web browser is hosted. Note that if you run in Internet Explorer though with scripts from the local machine (ie. not a Web Url) you will get a security warning every time you load the page. You have to allow scripts in the yellow warning bar at the top first before they can be accessed. This makes script access in the IE browser a lot less useful. The Web Browser control doesn’t have this limitation as it assumes you have whatever rights your application already has (or doesn’t have).
Because of the Fox case issue I had actually thought that this feature didn’t work any longer – suspected some sort of security hack – but it turns out the issue is merely a COM translation issue. This will come in handy especially in .NET as I need to update some code that does some fairly heavy duty HTML editing code. With the ability to run some of that code in script in the browser and taking advantage of jQuery this will be a lot easier!