Calling JavaScript Functions in Windows Phone 7

Today we’re hosting one of our Windows Phone 7 deployment labs and I’ve been spending some time with a partner trying to determine if they’ll be able to do what they need to do on a Windows Phone 7 device. They have a rich media app which needs to be able to modify the HTML DOM based on certain events. We needed to explore whether we could grab some info from one web page and use it to modify another page.

We didn’t get off to the greatest of starts. Using the WebBrowser control and InvokeScript() we continually encountered an 80020006 error. We tried everything to get to the bottom of it. To cut a long story short we were being given the runaround by the bugbear of all webdev debugging – caching. Despite restarting the emulator, cached versions of the page were still being served. This wasn’t immediately obvious however and only after a couple of hours of hair-pulling and straw-clutching did we figure out what was happening.

So, I’m happy to confirm all works as expected and happy to provide the warning *watch out for caching of WebBrowser on the emulator*.

Here’s some code to illustrate what we wanted to do. Firstly a simple HTML page hosted on my server:

 <html>
  <head>
    <script>
      function hello() 
      { 
        return 'hello'; 
      }
      
      function sayHello(sayWhat) 
      { 
        return sayWhat; 
      }
    </script>
  </head>
  <body>
    Hello
  </body>
</html>

There are actually two identical pages (ie with the same source). I’ve called them Page1 and Page2 just for the novelty value. The app itself looks like this:

image

ie two WebBrowser controls. That’s it. The idea is we navigate to one page, call a JavaScript function, get the return value and pass that to a JavaScript function in the other page. Here’s the XAML for the WebBrowser controls:

 <phone:WebBrowser Name="webBrowser1" 
                    IsScriptEnabled="True" />
<phone:WebBrowser Name="webBrowser2" 
                    IsScriptEnabled="True" 
                    Grid.Row="1" />

Note that the IsScriptEnabled property on each WebBrowser control is set to True – a requirement in order to be able to invoke a JavaScript function in the loaded page. The rest of the code looks like this:

 using System;
using System.Diagnostics;
using System.Windows;
using System.Windows.Input;
using Microsoft.Phone.Controls;

namespace WindowsPhoneApplication1
{
    public partial class MainPage : PhoneApplicationPage
    {
        const string uriBase = @"https://mikeo.members.winisp.net/";

        public MainPage()
        {
            InitializeComponent();
            Loaded += new RoutedEventHandler(MainPage_Loaded);
        }

        void MainPage_Loaded(object sender, RoutedEventArgs e)
        {
            string noCache = DateTime.Now.ToLongTimeString();
            webBrowser1
                .Navigate(new Uri(uriBase + @"page1.html?t=" + noCache));
            webBrowser2
                .Navigate(new Uri(uriBase + @"page2.html?t=" + noCache));
        }

        private void MouseDown(object sender, MouseButtonEventArgs e)
        {
            string s1 = (string)webBrowser1.InvokeScript("hello");
            Debug.WriteLine(s1);

            string  s2 = (string)webBrowser1.InvokeScript("sayHello", s1);
            Debug.WriteLine(s2);
        }
    }
}

My intention here is not to show off some magical feature but just to confirm this works as I was beginning to doubt my sanity. Note the use of the querystring  in the URIs (adding a time parameter). This prevents the page being cached while debugging. Shame we didn’t spot this earlier :(.