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