Many of my Cineworld app users whether its Windows Phone or Windows 8 complain about lack of in-app ticketing. In fact I would personally as a user prefer otherwise – Use tried and trusted website for ticketing as opposed to an app – sure my app might be good enough to be an official app but that’s a different issue.
So what did I do, well for Windows Phone 7 and 8, I used WebBrowser control and added in-app use of Cineworld mobile website. Most users are happy – can’t please everyone. I do remember that WebBrowser control offered many more events for management in Windows Phone 8 than in Windows Phone 7. So how about Windows 8 ?
Well Windows 8 / WinRT 1.0 ships with WebView control. This is similar is many ways to Windows Phone 7 WebBrowser control. Why do I say that ? Well, it contains LoadCompleted event to inform about page loaded but no loading event. Doesn’t expose the internal navigation stack and is substantially more painful than Windows Phone 7 Browser Control 😐
I am also aware that Windows 8.1 / WinRT 1.1 (??) ships with much improved WebView that exposes all the relevant event without hacks
Navigating Event: A quick search on the net found me Limitations of the WebView in Windows 8 Metro Apps. The post along side How to emulate Navigating event in the WebView turned out to be very useful. I used the WebViewWrapper to add Navigating event.
LoadCompleted Event: This event was exposed by the WebView and I used both maintain NavigationStack and toggle progress ring visibility
Progress Ring: Nothing can sit on top of WebView – no matter what order you define the elements in XAML. One needs to set WebView to collapsed visibility to show any other xaml content. I tend to toggle opacity to 0.5 and then show progress ring however this was a kill-joy till I found this “How to put a ProgressRing over a WebView” – easy peasy.. use WebViewBrush and paint a rectangle and hide the webview.. user cant really tell 🙂
Navigation Stack: Most pages in a Windows 8 apps expose back button. By default the back button calls GoBack defined in LayoutAwarePage.cs. It is however overrideable. So what should be behaviour be ? Well if you have been browsing a few pages, backbutton should ideally unwind that stack. Since WebView doesn’t really expose Nav Stack (just like Windows Phone 7 API), we need to do the dirty work. Use Completed to push the Uri. On Back button press pop the Uri. Apart from that, we just use InvokeScript to use browser’s internal nav stack using javascript.
<Grid Background="#FF231F20"> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto"/> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <WebView Grid.Row="0" Grid.RowSpan="2" Grid.Column="1" x:Name="wbCineworld" /> <Rectangle Grid.Row="0" Grid.RowSpan="2" Grid.Column="1" Name="MaskRectangle"/> <Grid x:Name="gProgress" Grid.Row="0" Grid.RowSpan="2" Grid.Column="0" Grid.ColumnSpan="2" HorizontalAlignment="Stretch" VerticalAlignment="Center" Height="100" Background="White" Visibility="Collapsed"> <ProgressRing x:Name="prProgress" HorizontalAlignment="Center" VerticalAlignment="Center" IsActive="True" Height="80" Width="80" Foreground="#FFB51C10"/> </Grid> <Button x:Name="backButton" Click="GoBack" IsEnabled="{Binding Frame.CanGoBack, ElementName=pageRoot}" Style="{StaticResource BackButtonStyle}"/> <Button Grid.Column="0" Grid.Row="1" VerticalAlignment="Bottom" x:Name="btnExitTicketing" Style="{StaticResource DeleteAppBarButtonStyle}" AutomationProperties.Name="Exit ticketing" Click="btnExitTicketing_Click" /> </Grid>
public sealed partial class InAppBrowser : Cineworld.Common.LayoutAwarePage { public InAppBrowser() { this.InitializeComponent(); var wrapper = new WebViewWrapper(this.wbCineworld); wrapper.Navigating += WrapperNavigating; } void WrapperNavigating(object sender, NavigatingEventArgs e) { this.SpinAndWait(true); } private Stack<Uri> NavigationStack = new Stack<Uri>(); public static Uri NavigationUri { get; set; } private void SpinAndWait(bool bNewVal) { WebViewBrush brush = new WebViewBrush(); brush.SourceName = "wbCineworld"; brush.Redraw(); MaskRectangle.Fill = brush; this.wbCineworld.Visibility = bNewVal ? Windows.UI.Xaml.Visibility.Collapsed : Windows.UI.Xaml.Visibility.Visible; this.MaskRectangle.Opacity = bNewVal ? 0.5 : 1; this.MaskRectangle.Visibility = bNewVal ? Windows.UI.Xaml.Visibility.Visible : Windows.UI.Xaml.Visibility.Collapsed; this.gProgress.Visibility = bNewVal ? Windows.UI.Xaml.Visibility.Visible : Windows.UI.Xaml.Visibility.Collapsed; this.prProgress.IsActive = bNewVal; } protected override void OnNavigatedTo(NavigationEventArgs e) { base.OnNavigatedTo(e); this.wbCineworld.LoadCompleted += wbCineworld_LoadCompleted; this.wbCineworld.Navigate(NavigationUri); this.SpinAndWait(true); } void wbCineworld_LoadCompleted(object sender, NavigationEventArgs e) { this.SpinAndWait(false); NavigationStack.Push(e.Uri); } protected override void GoBack(object sender, RoutedEventArgs e) { if (this.NavigationStack.Count > 1) { this.wbCineworld.InvokeScript("eval", new string[1] { "history.go(-1)" }); // note that this is another Pop - as when the navigate occurs a Push() will happen NavigationStack.Pop(); } else base.GoBack(sender, e); } private void btnExitTicketing_Click(object sender, RoutedEventArgs e) { base.GoBack(sender, e); } }
Let me just reiterate – I haven’t found something new, I have just found a few bits and have put them together. Happy coding.
Note: Meant to post this earlier.. I am checking NavigationStack.Count and ideally the check should be > 0 but in my case, there was an additional redirection and hence I am using Count > 1.
Pingback: WebView and working with C# and XAML