Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / XAML

WinRT : How to Communicate with WebView JavaScript from C# and Vice Versa

4.47/5 (5 votes)
4 Mar 2014CPOL2 min read 31.4K  
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 has 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 the 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 C# code. Simple requirement really.

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

XAML
<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 let's have a look at the C# side of things. Here it is in its entirety:

C#
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:

JavaScript
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., 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:

XAML
<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.


License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)