Getting Started with Design for Windows, Windows Phone, iOS and Android

Getting Started with Design for Windows, Windows Phone, iOS and Android

Here’s a summary of some of the design guidelines for the various mobile platforms:

Windows Phone
http://developer.windowsphone.com/en-us/design

Windows
http://msdn.microsoft.com/en-au/windows/apps/hh779072.aspx

Android
http://developer.android.com/design/index.html

iOS
https://developer.apple.com/library/ios/design/index.html

 

Another great resource on user experience design is the UX Mastery website

http://uxmastery.com/

In particular the book Everyday UX is worth purchasing:

http://uxmastery.com/everyday-ux-remarkable-people-telling-their-stories/

Much needed updates to the Windows Store

Much needed updates to the Windows Store

Just in case you missed the announcement regarding changes to the Windows Store: More control for app developers in the Windows Store

The main updates are

*    Setting your app’s release date and time
*    Managing your app’s availability
        – Temporarily removing the app from store
        – Support for Windows 8 (ie only show a Windows 8.1 listing)
*    Simplified certification requirements

Data Validation and Visual States for Windows Phone and Windows

Data Validation and Visual States for Windows Phone and Windows

One of the developers we’ve been working with recently suggested incorporating the INotifyDataError interface into some of the view models on a particular project. As a lot of the applications we typically  work on are more consumption style applications I haven’t seen the need to add complex data validation logic; where we do need to validate one or two values we typically just have a different visual state that represents when the user hasn’t correctly filled in the required fields.

I just had a quick hunt and found http://dotnet-redzone.blogspot.com.au/2012/12/windows-phone-8-easy-and-intuitive-way.html. This post is a great starting point for implementing visual states into a Windows Phone project. A number of controls support data validation. For example the TextBox has a ValidationStates visual state group, with states for valid and invalid data. The default template doesn’t specify what these states look like but it would be easy to modify the template to provide a visual indicator when data doesn’t validate.

Modifying the validation states for a control is fine for individual controls but if you need to modify the visual state for the rest of the page you’d need to capture the ErrorsChanged event and change the visual state of the page.

One of the biggest weaknesses of the data binding framework for both Windows and Windows Phone is that there isn’t a built in mechanism for changing visual states from within a view model. There are some solutions, such as data triggers or raising an even that is captured by the page and converted into a state change. These could be combined with the ErrorsChanged event in order to build a more versatile data validation framework.

Visual States in Windows Phone and Windows Applications using MvvmCross

Visual States in Windows Phone and Windows Applications using MvvmCross

I’m a big fan of using Visual States defined in Blend to be able to see all the different states that a page/view within your application would look like under different conditions. Most of these conditions are reflected in the state of your view model. However, there is no built in mechanism for data binding to a visual state. There are a number of different ways to tackle this problem, for example data triggers to control state changes. On technique we’ve used quite successfully is to simply bubble up appropriate state changed events from the view model, which in turn triggers a state change at the page level. In this post I’m going to walk you through how we’ve extended MvvmCross to include our own base view model and base page which allows us to trigger and track the state of the page.

We’ll start by defining an interface which will be implemented by our base view model. This includes the StateChanged event and methods for invoking a state change, ChangePageState, and querying the current state, CurrentState. You’ll notice that these methods take a type parameter, this is so that we can use an enumeration, rather than string literals to define our states.

public interface IStateAndTransitions
{
    event EventHandler<DualParameterEventArgs<string, bool>> StateChanged;
    void ChangePageState<T>(T stateName, bool useTransitions = true) where T : struct;
    T CurrentState<T>() where T : struct;
}

Next, we’ll implement the IStateAndTransitions interface in our BaseViewModel class. This class also inherits from MvxViewModel, bringing with it all the goodness of MvvmCross.

public class BaseViewModel : MvxViewModel, IStateAndTransitions
{
    public event EventHandler<DualParameterEventArgs<string, bool>> StateChanged;

    private readonly Dictionary<string, string> currentStates = new Dictionary<string, string>();
    public T CurrentState<T>() where T : struct
    {
        var current = currentStates.SafeDictionaryValue<string, string, string>(typeof(T).FullName);
        var tvalue = current.EnumParse<T>();
        return tvalue;
    }

    public void ChangePageState<T>(T stateName, bool useTransitions = true) where T : struct
    {
        var current = currentStates.SafeDictionaryValue<string, string, string>(typeof(T).FullName);
        var newState = stateName.ToString();
        if (string.IsNullOrWhiteSpace(current) || current != newState)
        {
            currentStates[typeof(T).FullName] = newState;
            StateChanged.SafeRaise(this, newState, useTransitions);
        }
    }
}

The last thing to do is to extend the MvxPhonePage to wire up an event handler for the StateChanged event. The event handler simply invokes the state change by calling GoToState on the VisualStateManager.

public class BasePhonePage : MvxPhonePage
{

    protected IStateAndTransitions StatesAndTransitionsViewModel
    {
        get
        {
            return DataContext as IStateAndTransitions;
        }
    }

    protected override void OnNavigatedTo(NavigationEventArgs e)
    {
        base.OnNavigatedTo(e);

        var satvm = StatesAndTransitionsViewModel;
        if (satvm != null)
        {
            satvm.StateChanged += ViewModelStateChanged;
        }
    }

    private void ViewModelStateChanged(object sender, DualParameterEventArgs<string, bool> e)
    {
        //var controlName = e.Parameter1;
        var stateName = e.Parameter1;
        var useTransitions = e.Parameter2;

        // Locate the control to change state of (use this Page if controlNAme is null)
        Control control = this;
        VisualStateManager.GoToState(control, stateName, useTransitions);
    }

    protected override void OnNavigatedFrom(NavigationEventArgs e)
    {
        var satvm = StatesAndTransitionsViewModel;
        if (satvm != null)
        {
            satvm.StateChanged -= ViewModelStateChanged;
        }

        base.OnNavigatedFrom(e);
    }
}

For completeness you’ll also need the parameter and dualparameter event args classes. These are used to make it easier to pass data values around when raising events.

public class ParameterEventArgs<T> : EventArgs
{
    public T Parameter1 { get; set; }

    public ParameterEventArgs(T parameter)
    {
        Parameter1 = parameter;
    }

  public static implicit operator ParameterEventArgs<T>(T parameter)
    {
        return new ParameterEventArgs<T>(parameter);
    }
}

public class DualParameterEventArgs<T1, T2> : ParameterEventArgs<T1>
{
    public T2 Parameter2 { get; set; }

    public DualParameterEventArgs(T1 parameter1, T2 parameter2):base(parameter1)
    {
        Parameter2 = parameter2;
    }

    public static implicit operator DualParameterEventArgs<T1, T2>(object[] parameters )
    {
        if(parameters==null || parameters.Length!=2) return null;
        return new DualParameterEventArgs<T1, T2>((T1)parameters[0], (T2)parameters[1]);
    }
}

Now, let’s see this in action in a view model, FirstViewModel:

public class FirstViewModel : BaseViewModel
{
    public enum FirstStates
    {
        Base,
        Loading,
        Loaded
    }

    public async Task LoadData()
    {
        ChangePageState(FirstStates.Loading);
        // Load some data (async to ensure no UI blocking)
        ChangePageState(FristStates.Loaded);
    }
}

Your page, FirstView, needs to inherit from BasePhonePage and have visual states called Loading and Loaded. The “Base” enumeration value is there just to represent the default state of the page. You should never have a state called “Base”, nor should you call ChangePageState with a value of Base. You can however, call CurrentState and compare the value to Base to see if a state has been set.

Hope this makes it easier for you to build your visual states in Blend.

Querying data from the Windows Phone and Windows Stores

Querying data from the Windows Phone and Windows Stores

We had a requirement today to query both the Windows Phone and Windows Stores for information about some of the apps we’ve been working on. Since both stores have a rich client interface it’s only natural that the store can be queried via a hidden pseudo api (“pseudo” because they’re more feed-like than a real api).

To work out how the api is structured the easiest thing to do is to setup Fiddler with SSL decoding and remote connections enabled on one computer and then on your Windows Phone and/or Windows 8 device you set the Fiddler computer to be the proxy. In order for this to work you’ll need to install the Fiddler certificate onto the device (Windows Phone installs the certificate correctly by default; Windows 8 you’ll need to put the certificate into the Trusted Root Certificate Authorities store for it to work).

Here’s what I pulled from my Windows Phone:

Application Info URL

http://marketplaceedgeservice.windowsphone.com/v8/catalog/apps/{app guid}?os=8.0.10211.0&cc=AU&lang=en-US

Replace “app guid” with the Guid for your application. You can find the Guid for your application by finding the application in the web version of the Windows Phone Store and then looking in the address bar.

Image URL

http://cdn.marketplaceimages.windowsphone.com/v8/images/{image guid}?hw=486577666&imagetype=icon_small

Replace “image guid” with the Guid for the image you want to retrieve. The image Guids are listed in the information returned from the application info URL.

 

Here’s what I pulled from my Windows 8 device:

Application Info URL

https://next-services.apps.microsoft.com/browse/6.3.9600-0/776/en-GB_en-AU.en-US.en/c/AU/cp/10005001/Apps/{app guid}

Replace “app guid” with the Guid for your application. You can find the Guid for your application by finding the application in the web version of the Windows Store and then looking in the address bar.

Image URL

http://wscont1.apps.microsoft.com/winstore/1.4x/{image path}

Replace “image path” with the relative path to the image that you want which can be found in the Application Info URL

Windows Store In App Purchase ArgumentException when calling LoadListingInformationAsync

Windows Store In App Purchase ArgumentException when calling LoadListingInformationAsync

When you couple poor API documentation with dumb developer decisions you can cause developers to waste hours trying to diagnose issues with their apps. We’ve just tracked down an issue with calling CurrentApp.LoadListingInformationAsync from a Windows Store application we’re building. Whilst calling the same method on the CurrentSimulatorApp works without throwing exception, calling the method on CurrentApp throws an ArgumentException with message "Value does not fall within the expected range."

Initially we thought that this issue was related to the app not being published in the Store, so we submitted and published the app. Unfortunately the exception continues….

This thread: http://social.msdn.microsoft.com/Forums/windowsapps/en-US/0de52a76-2ec3-4b82-8d38-7385f22132f0/currentapploadlistinginformationasync-throws-argumentexception

holds the answer – you need to call this method from the UI thread.

Points to remember when building an API:

– You shouldn’t design an API that needs to run on the UI thread, that isn’t UI related

– You should document possible exceptions

– If you’re going to provide a simulator API

    – The simulator should implement an interface so it can be mocked properly

    – The simulator should mirror the behaviour of the real thing (in throw similar exceptions!)

App Generators: Windows Phone App Studio and Project Siena

App Generators: Windows Phone App Studio and Project Siena

With all the recent interest in app generators by Microsoft, I’m wondering whether there is going to be a run on app-kiddies (kick back to “script kiddies” who plagued the internet a decade or so ago): Application builders who don’t have a clue how to write an application but seem to be able to coble together a number of applications, that all look surprisingly similar, using generators.

The two app generators I’m referring to are:

Windows Phone App Studio

“Take your app from idea to Windows Phone in record time”

Microsoft Project Siena (Beta)

“Unleash your expertise and imagination”

Having checked out the capabilities of these generators I don’t feel that my job as a Windows platform developer is in jeopardy. In fact, I think these generators allow would-be app creators to get started with the basics. Once they hit the limitations of the tools, that’s when they can come to us to fill out the gaps. This is a great way to test a concept, or two, before over-investing in any particular idea.

Windows and Windows Phone app convergence

Windows and Windows Phone app convergence

One of the things that is becoming apparent is that the convergence between Windows and Windows Phone applications it a hot topic. Developers often ask why we have two development platforms to target, where as iOS and Android only have the one. For example iOS you can build a single package which includes both iPad and iPhone versions of an application. This argument is of course completely flawed since we’re comparing apples and oranges. If you widen the scope to include Mac development then those in the Apple world also have to target two platforms. This argument doesn’t really apply to Android since it doesn’t really feature in the desktop space.

What we are seeing is that we can increasingly share resources between our Windows and Windows Phone applications. The continual evolution of Portable Class Libraries mean that we can build reusable libraries that will work without recompilation across both Windows and Windows Phone applications. In fact you can even use these libraries in your iOS and Android projects, assuming you’re using Xamarin as your cross platform approach.

Unfortunately all this discussion of convergence at an dev platform layer completely ignores what’s going on at the user experience level. The reality is that you don’t need technology convergence in order to build a consistent user experience across both platforms. What’s been interesting to watch is how Microsoft tries to converge the look of Windows and Windows Phone first party applications. Whilst  both interfaces are laced with the essence of Metro Modern UI, there are some significant differences between them.

Take for example the Hub/Panorama experience – Windows 8 didn’t have a control for this, where as Windows Phone has the Panorama control; Windows 8.1 now has the Hub control which makes it easier for developers to build a hub style experience. The Hub and Panorama controls aren’t the same eg the hub control typically has a hero pane, where as the panorama has a background image which has parallax scrolling.

If you look at some of the Microsoft applications, such as Bing News, you’ll see that they’ve taken UX convergence to the next level, attempting to minimize any difference between the platforms. Below you can see the Windows Phone Bing News app, followed by the Windows version.

1 of 6 2 of 6 3 of 6

 

Screen shot 1

The question you should be asking (and Microsoft should be answering) is whether you should be sticking to the standard Panorama look and feel, or should you be trying to match what Bing News is doing and aligning with the Windows Hub control?

Windows 8.1, the Hub Control and Visual States

Windows 8.1, the Hub Control and Visual States

The more I use the Windows 8.1 Hub control the more I get frustrated by it. In my previous post Databinding with the Windows 8.1 Hub control, I talked about getting data binding to work. This is useful if you’re going to have a series of panes all with similar structure. However, you could probably get the same effect using grouping in a GridView. A more common scenario is to have a number of different panes, which hold content coming from different sources. In this case, each pane is going to be loading data independently and you’ll probably want each pane to have different visual states representing loading, data loaded successfully, data load failed, data loaded successfully but no data available etc. If we were building this using the Panorama control for Windows Phone, or using the deprecated method of a horizontal stack panel inside a scrollviewer, then we could simply drop in a couple of extra elements and then control which ones are visible using visual states. As the view model for the page is typically responsible for controlling the loading of content across each of the panes (it simply loads the data, and data binding does the rest), it is also responsible for controlling the visual states across the page. Unfortunately this doesn’t work with the Hub control – the contents

As a starting point for this post I’m going to use the source code sample from the article States, Navigation and Testing with Portable Class Libraries for Windows Phone, as this will give us the basic infrastructure for raising state changed events from our view model. I won’t go through the details of wiring up the Windows application to use the locator  pattern – it’s essentially the same as for Windows Phone. What we will be focussing on is the hub control and how we can use state change events to control the content for each pane. The Panorama control used in Windows Phone applications, is made up of PanoramaItems and the contents of each pane is simply added as content. The Hub control is made up of HubSections but the contents for each pane is added into a data template, similar to the following:

<Page >http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      >http://schemas.microsoft.com/winfx/2006/xaml"
      >http://schemas.microsoft.com/expression/blend/2008"
      >http://schemas.openxmlformats.org/markup-compatibility/2006"
      >http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             >http://schemas.microsoft.com/winfx/2006/xaml"
             >http://schemas.microsoft.com/expression/blend/2008"
             >http://schemas.openxmlformats.org/markup-compatibility/2006"
             mc_Ignorable="d"
             d_DesignHeight="300"
             d_DesignWidth="400">

    <Grid>
        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup x_Name="LoadingStates">
                <VisualState x_Name="Loading">
                    <Storyboard>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)"
                                                       Storyboard.TargetName="SP_Loading">
                            <DiscreteObjectKeyFrame KeyTime="0">
                                <DiscreteObjectKeyFrame.Value>
                                    <Visibility>Visible</Visibility>
                                </DiscreteObjectKeyFrame.Value>
                            </DiscreteObjectKeyFrame>
                        </ObjectAnimationUsingKeyFrames>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(ProgressRing.IsActive)"
                                                       Storyboard.TargetName="progressRing">
                            <DiscreteObjectKeyFrame KeyTime="0">
                                <DiscreteObjectKeyFrame.Value>
                                    <x:Boolean>True</x:Boolean>
                                </DiscreteObjectKeyFrame.Value>
                            </DiscreteObjectKeyFrame>
                        </ObjectAnimationUsingKeyFrames>
                    </Storyboard>
                </VisualState>
                <VisualState x_Name="Loaded">
                    <Storyboard>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)"
                                                       Storyboard.TargetName="GD_Loaded">
                            <DiscreteObjectKeyFrame KeyTime="0">
                                <DiscreteObjectKeyFrame.Value>
                                    <Visibility>Visible</Visibility>
                                </DiscreteObjectKeyFrame.Value>
                            </DiscreteObjectKeyFrame>
                        </ObjectAnimationUsingKeyFrames>
                    </Storyboard>
                </VisualState>
                <VisualState x_Name="NotAbleToLoad">
                    <Storyboard>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)"
                                                       Storyboard.TargetName="GD_NotAbleToLoad">
                            <DiscreteObjectKeyFrame KeyTime="0">
                                <DiscreteObjectKeyFrame.Value>
                                    <Visibility>Visible</Visibility>
                                </DiscreteObjectKeyFrame.Value>
                            </DiscreteObjectKeyFrame>
                        </ObjectAnimationUsingKeyFrames>
                    </Storyboard>
                </VisualState>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>
        <StackPanel x_Name="SP_Loading"
                    Visibility="Collapsed">
            <TextBlock Text="Loading…."
                       FontSize="48" />
            <ProgressRing x_Name="progressRing"
                          Width="50"
                          Height="50" />

        </StackPanel>
        <Grid x_Name="GD_Loaded"
              Visibility="Collapsed">
            <TextBlock Text="All data loaded"
                       FontSize="48" />
        </Grid>
        <Grid x_Name="GD_NotAbleToLoad"
              Visibility="Collapsed">
            <TextBlock Text="Not able to load"
                       FontSize="48" />
        </Grid>
    </Grid>
</UserControl>

This usercontrol would then be added to the datatemplate for the hubsection:

<HubSection>
    <DataTemplate>
        <userControls:MainFirstPaneUserControl />
    </DataTemplate>
</HubSection>

The issue is that we need some mechanism for triggering the visual state change from the view model for the page. In the same way as the page wires an event handler for the StateChanged event on the viewmodel, the usercontrol also has to wire up to this event. As the usercontrol inherits the DataContext from the page, the usercontrol can listen to the DataContextChanged event and wire up to the StateChanged event at that point.

public sealed partial class MainFirstPaneUserControl
{
    public MainFirstPaneUserControl()
    {
        InitializeComponent();

        DataContextChanged += MainFirstPaneUserControl_DataContextChanged;
    }

    void MainFirstPaneUserControl_DataContextChanged(FrameworkElement sender, DataContextChangedEventArgs args)
    {
        var ctrl = sender as MainFirstPaneUserControl;
        ctrl.WireStateChanged();

    }

    private void WireStateChanged()
    {
        ViewModel.StateChanged += ViewModelStateChanged;
    }

    private void ViewModelStateChanged(object sender, StateChangedEventArgs e)
    {
        VisualStateManager.GoToState(this, e.StateName, e.UseTransitions);
    }

    private BaseStateChange ViewModel
    {
        get
        {
            return DataContext as BaseViewModel;
        }
    }
}

This appears to work, except the initial state changed event is skipped. The Hub, by design, lazy loads the hubsections, meaning that the OnNavigatedTo for the page gets run before the hubsection has been created and wired up. To resolve this problem the view model needs to track the current visual state:

public event EventHandler<StateChangedEventArgs> StateChanged;
private readonly Dictionary<Type,object> currentState = new Dictionary<Type, object>();
protected void OnStateChanged<T>(T state, bool useTransitions = true) where T : struct
{
    currentState[typeof (T)] = state;

    if (StateChanged != null)
    {
        StateChanged(this, new StateChangedEventArgs { StateName = state.ToString(), UseTransitions = useTransitions });
    }
}

public T CurrentState<T>()
{
    object current;
    currentState.TryGetValue(typeof (T), out current);
    return (T) current;
}

And the usercontrol needs to query it at the point the state changed event is wired up:

private void WireStateChanged()
{
    var current = ViewModel.CurrentState<MainViewModel.LoadingStates>();
    if (current != MainViewModel.LoadingStates.Base)
    {
        VisualStateManager.GoToState(this, current.ToString(), false);
    }
    ViewModel.StateChanged += ViewModelStateChanged;
}

When we run this, we’ll see the visual state for the hubsection switch between loading and loaded states.

image     image

Download the code

DNS Client for Windows and Windows Phone

DNS Client for Windows and Windows Phone

Don’t ask why, but I needed to be able to do a dns record lookup for a srv record from a Windows Phone application. I went looking for a library that could do this and came up with Phone Net Tools (http://phonenettools.codeplex.com/) – this seems a great library but fairly rudimentary support for DNS queries. I also came across a great post by Rob Philpott on CodeProject (http://www.codeproject.com/Articles/12072/C-NET-DNS-query-component) – it’s a bit dated and for legacy technologies (I needed to upgrade everything to even run it!). Whilst this option seemed to be more painful to update the architecture seemed to lead itself to being extended to support srv records. The net effect is there is yet another library on codeplex, this one dedicated to doing DNS lookups from a Windows 8 or Windows Phone application.

At the moment I’ve only got the Windows Phone version uploaded to the Windows and Windows Phone DNS Library. It’s relatively simple to use:

// create a DNS request
var request = new Request();

var domain = "builttoroam.com";
var type = DnsType.MX;
var dnsServer = IPAddress.Parse("203.0.178.191");

// create a question for this domain and DNS CLASS
request.AddQuestion(new Question(domain, type, DnsClass.IN));

// send it to the DNS server and get the response
//Response response =

var resp = await Resolver.Lookup(request, dnsServer);
foreach (var answer in resp.Answers)
{
    Debug.WriteLine("{0}", answer.Record);   
}

Windows 8 Launch

Windows 8 Launch

For those not living under a rock you’ll have noticed that Windows 8 has been launched. Over the past month or so I’ve been involved in a couple of Windows 8 projects. All three projects made it into the store just in time for the launch events.

image_thumb[1]

With this interlude over, we’ll be back to more frequent Windows Phone updates….

Limitations of the WebView in Windows 8 Metro Apps

Limitations of the WebView in Windows 8 Metro Apps

Having worked quite intimately with the WebBrowser control in Windows Phone I was a little flawed by how immature and annoying the WebView for Windows 8 metro app is. As noted by Brent Schooley in his post on Metro WebView Source and HTML workarounds you can work around some of the limitations such as the missing SaveToString method. One of the features of the WebBrowser control that I quite often use is the series of events that are raised around navigating to pages within the control. Unlike the WebBrowser, the WebView is missing events such as Navigating, Navigated and NavigationFailed, and has only the LoadCompleted event. I figured that I should be able to detect when the current page is changing, and thus raise a Navigating event. Turns out that it is possible with a combination of InvokeScript (to invoke some javascript within the page) and window.external.notify (to raise an event when the page in the browser is changing). The following WebViewWrapper class exposes a Navigating event which you can wire an event handler to in order to detect when the browser is being navigated away from the current page (unfortunately there doesn’t seem to be a way to extract the target url, nor to be able to cancel the navigation, unlike the real Navigating event that the WebBrowser control has). I’ve also included a SaveAsString method which wraps the code Brent had in his post.

public class WebViewWrapper
{
    // Maintain a reference to the WebView control so that
    // we can invoke javascript
    public WebView WebView { get; private set; }

    public WebViewWrapper(WebView webView)
    {
        WebView = webView;
    }

    // The Navigating event is a custom event, allowing us to hook/unhook
    // from the ScriptNotify and LoadCompleted events. To invoke this
    // event, we actually invoke the internalNavigating event.
    private event EventHandler<NavigatingEventArgs> internalNavigating;
    public event EventHandler<NavigatingEventArgs> Navigating
    {
        add
        {
            WebView.ScriptNotify+=NavigatingScriptNotify;
            WebView.LoadCompleted+=WireUpNavigating;
            internalNavigating += value;
        }
        remove
        {
            WebView.ScriptNotify -= NavigatingScriptNotify;
            WebView.LoadCompleted-=WireUpNavigating;
            internalNavigating -= value;
        }
    }

    // When each page loads, run a javascript function which wires up
    // an event handler to the onbeforeunload event on the window.  This
    // event is raised when the window is about to unload the current page
    // In the event handler we call window.external.notify in order to raise
    // the ScriptNotify event on the WebView. The javascript function also
    // returns the current document location. This is used to update the
    // AllowedScriptNotifyUris property on the WebView in order to permit
    // the current document to call window.external.notify (remembering
    // that even though we injected the javascript, it’s being invoked in the
    // context of the current document.
    private void WireUpNavigating(object sender, NavigationEventArgs e)
    {
        var unloadFunc = "(function(){" +
                            " function navigating(){" +
                            "  window.external.notify(‘%%’ + location.href);" +
                            "} " +
                            "window.onbeforeunload=navigating;" +
                            "return location.href;" +
                            "})();";
        var host = WebView.InvokeScript("eval", new[] { unloadFunc });
        WebView.AllowedScriptNotifyUris = new[] { new Uri(host) };
    }

   // Check to see if the ScriptNotify was raised by the javascript we
   // injected (ie does it start with %%), and then raise the Navigating
   // event.
   private void NavigatingScriptNotify(object sender, NotifyEventArgs e)
    {
        if (internalNavigating == null) return;
        if(!string.IsNullOrWhiteSpace(e.Value))
        {
            if(e.Value.StartsWith("%%"))
            {
                internalNavigating(this, new NavigatingEventArgs() {LeavingUri = new Uri(e.Value.Trim(‘%’))});
            }
        }
    }

    public string SaveToString()
    {
        try
        {
            var retrieveHtml = "document.documentElement.outerHTML;";
            var html = WebView.InvokeScript("eval", new[] { retrieveHtml });
            return html;
        }
        catch
        {
            return null;
        }
    }
}

public class NavigatingEventArgs:EventArgs
{
    public Uri LeavingUri { get; set; }
}

Actually making use of the WebViewWrapper class is as simple as creating an instance and then wiring up an event handler.

public BlankPage()
{
    this.InitializeComponent(); 

    var wrapper = new WebViewWrapper(MyWebView);
    wrapper.Navigating += WrapperNavigating;
}

void WrapperNavigating(object sender, NavigatingEventArgs e)
{
    Debug.WriteLine(e.LeavingUri);
    Debug.WriteLine((sender as WebViewWrapper).SaveToString());
}