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 :
I’ve just tried your overlapping combobox on Win 8.1 RTM (not Windows ARM/RT): It works as expected.