WinRt

WinRT : How to communicate with WebView Javascript from C# and Vice Versa

The other day I was contacted by one of the fellow Xaml Disciples (who have been dormant for a long time, but hey ho), who was asking how he might
determine if some audio within a web site was playing or not. I thought he problem could be solved using the WebView available
in WinRT. Turns out I didn’t get what his actual problem was, and this did not help him. I did however try something out before I answered him, which
was how to communicate with the WebView hosted HTML/JavaScript from c# and vice versa.

I thought that may make a semi-useful blog post. So I have provided a dead simple example, the c# code will try and change the FontSize of a DIV
within the hosted HTML content by calling a JavaScript function in the hosted HTML content. The JavaScript will then double the incoming FontSize and use
it for the DIV, and at the same time tell the c# what the doubled size is via a callback into the c3 code. Simple requirement really

It all starts with the markup which is shown in its entirety below

<Page
    x:Class="App1.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:App1"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Border Background="{StaticResource ApplicationPageBackgroundThemeBrush}" BorderBrush="White"
		BorderThickness="4" Height="400" Width="640" Padding="10">
        <StackPanel>
            <StackPanel.Resources>
                <Style x:Key="RefreshAppBarButtonStyle" TargetType="ButtonBase"
			BasedOn="{StaticResource AppBarButtonStyle}">
                    <Setter Property="AutomationProperties.AutomationId" Value="RefreshAppBarButton"/>
                    <Setter Property="AutomationProperties.Name" Value="Refresh"/>
                    <Setter Property="Content" Value=""/>
                </Style>
            </StackPanel.Resources>
            <WebView x:Name="webView" Height="300" Width="600"/>
            <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="10">
                <Button x:Name="font25" Content="25px" Click="Change25_Click"/>
                <Button x:Name="font40" Content="40px" Click="Change40_Click"/>
                <Button x:Name="font60" Content="60px" Click="Change60_Click"/>
                <Button x:Name="reset" Content="Reset" Click="Reset_Click"/>
            </StackPanel>
        </StackPanel>
    </Border>
</Page>

Nothing fancy there, just a WebView control and a few buttons to change the FontSize of the DIV

So lets have a look at the c# side of things. Here it is in its entirety

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Popups;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;

namespace App1
{
    public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent();
            webView.ScriptNotify += webView_ScriptNotify;
        }

        async void webView_ScriptNotify(object sender, NotifyEventArgs e)
        {
            var jsScriptValue = e.Value;
            MessageDialog msg = new MessageDialog(jsScriptValue);
            var res =  await msg.ShowAsync();

        }

        private const string htmlFragment =
                    "<html><head><script type='text/javascript'>" +
                    "function doubleIt(incoming){ " +
                    "  var intIncoming = parseInt(incoming, 10);" +
                    "  var doubled = intIncoming * 2;" +
                    "  document.body.style.fontSize= doubled.toString() + 'px';" +
                    "  window.external.notify('The script says the doubled value is ' + doubled.toString());" +
                    "};" +
                    "</script></head><body><div id='myDiv'>I AM CONTENT</div></body></html>";

        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            webView.NavigateToString(htmlFragment);
        }

        private void Reset_Click(object sender, RoutedEventArgs e)
        {
            webView.NavigateToString(htmlFragment);
        }

        private void Change25_Click(object sender, RoutedEventArgs e)
        {
            webView.InvokeScript("doubleIt", new string[] { "25" });
        }

        private void Change40_Click(object sender, RoutedEventArgs e)
        {
            webView.InvokeScript("doubleIt", new string[] { "40" });
        }

        private void Change60_Click(object sender, RoutedEventArgs e)
        {
            webView.InvokeScript("doubleIt", new string[] { "60" });
        }

    }
}

Calling JavaScript from c#

This is done using the WebView.InvokeScript() method, where it expects a name of a function, and a string[] for the arguments

Calling c# from JavaScript

This is slightly trickier as you need to carry out the following 3 steps

Step 1 : Hook up the WebView.ScriptNotify event

Step 2 : Use the result of the NotifyEventArgs.Value from Step 1

Step 3 : Get the WebView to actually call the ScriptNotify c# event, which is done via this bit of code:

window.external.notify(..)

NOTE

WinRT makes a big point out of the fact that the WebView control is a regular WinRT control that can be treated as any other element
such as applying Transforms etc etc, while this is true (I have seen the demos/videos/screenshots). However it seems one area has been missed (at least in Windows 8.0 that
I am currently using), which is that the WebView seems to be the top most element. Try the XAML below and see what you think

<Page
    x:Class="App1.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:App1"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <Grid Background="Black" Grid.Row="0">
            <StackPanel Orientation="Horizontal"  Margin="20">
                <ComboBox Width="150">
			<ComboBoxItem Content="hello1"/>
			<ComboBoxItem Content="hello2"/>
			<ComboBoxItem Content="hello3"/>
			<ComboBoxItem Content="hello4"/>
			<ComboBoxItem Content="hello5"/>
			<ComboBoxItem Content="hello6"/>
			<ComboBoxItem Content="hello7"/>
			<ComboBoxItem Content="hello8"/>
			<ComboBoxItem Content="hello9"/>
			<ComboBoxItem Content="hello10"/>
			<ComboBoxItem Content="hello11"/>
			<ComboBoxItem Content="hello12"/>
			<ComboBoxItem Content="hello13"/>
			<ComboBoxItem Content="hello14"/>
		</ComboxBox>
                <Button Content="Reset" Margin="10,0,0,0" Height="45" />
            </StackPanel>
        </Grid>
        <WebView Grid.Row="1" HorizontalAlignment="Stretch"
                 VerticalAlignment="Stretch" Margin="10,0,10,10" />

    </Grid>

</Page>

For me the ComboBox popup goes behind the WebView, a tad annoying, and for me makes the WebView pretty unworkable.

As always here is a small demo project :

Demo project.zip

One thought on “WinRT : How to communicate with WebView Javascript from C# and Vice Versa

Leave a comment