Communication between HTML in WebBrowser and Silverlight app
Two people asked on this post about communication between HTML and Silverlight, so I thought I would share broadly about the more 'advanced' techniques.
[Don't be misled, advanced is still easy, just explaining all you can do].
Silverlight on Windows Phone allows you to host HTML content in a WebBrowser control. You get bi-directional communication between the control and the HTML content:
- For HTML to host comunication, the WebBrowser control injects script into the page, and your javascript can call window.external.notify (<param>) to communicate with Silverlight.
There is nothing you need to do (like include a js file). You just call the function in HTML and calling it will trigger the WebBrowser.ScriptNotify event on the Silverlight side.
- For host to HTML, webbrowser control calls the WebBrowser.InvokeScript method, passing a string with the function name, and an array of string params ).
If you own the HTML page you are hosting, the infrastructure above is all you need to get anything done (since you can just add the javascript to communicate).
If you don’t own the page you are calling things are slightly more interesting but still very doable – these are the scenarios that fuel this post –.
These are the communication mechanisms I use when talking to HTML on a page that comes from a server that I can’t control or influence.
Use the execScript javascript function to call arbitrary javascript. execScript does not return a value, but you can call methods that don’t need to return one, for example:
webBrowser.InvokeScript("execScript", "history.go(-1)");
Use the eval javascript function to call arbitray javascript, and return a value from that javascript. The return must be a string, but you can easily solve it by doing a toString() on any type (or using JSON if you need to return some thing complex). For example:
// this will fail, it returns null // object height = webBrowser.InvokeScript ( "eval", "document.body.offsetHeight" ); // this works string height = (string) webBrowser.InvokeScript ( "eval", "document.body.offsetHeight.toString()" );
You can even take these techniques to the next level and inject scripts into the DOM, listen for events, and call back to your host. All of this without owning the page you are hosting.
For example: this snippet below is C# on the host WebBrowser control that inserts a function into the DOM, and then wires a listener for the click on a button in the page, the event handler that we are wiring up calls window.external.notify () to let our host know the event was clicked. Without owning the page, we can listen to events in the HTML. What more can we ask for?
Here is the code:StringBuilder bldr = new StringBuilder(); bldr.Append("var script = document.createElement('script');"); bldr.Append("script.text = 'function cb () { "); bldr.Append("window.external.notify (\"this text was injected on the fly\");}';"); bldr.Append("var headNode = document.getElementsByTagName('HEAD'); "); bldr.Append( "if (headNode[0] != null);headNode[0].appendChild(script); ;"); bldr.Append("var element= document.getElementById('btn'); "); bldr.Append("if ( element != null ) element.onclick = cb;"); webBrowser.InvokeScript("execScript", bldr.ToString());
I am sure there are more techniques, but the combination of the 3 above has gotten me pretty far in communicating between JS and Silverlight.
If you want to see the code above in action, download this sample.
Happy Windows Phone coding!
If you want to keep up with Windows Phone on a more frequent basis, subscribe to my Windows Phone question of the day RSS feed.