WPF – Bring Closing Event to MVVM

The Issue

I’m jumping around a bit, but I don’t get to work on a single project at a time, so I am going to switch gears to talk about some WPF work that I put together. In this project, I want to make sure that I can control when the user closes the window, regardless of whether they click on the “X” in the top-right or if they click the “Exit” button in the UI.

The problem that we have is that the logic that controls whether the user can close the window or not is in the view model. The exit button is controlled by the view model. But the “X” in the top-left corner can only be controlled by the Closing event in the code behind. You can use the event parameters to cancel the close. How can we leverage the logic in the view model to determine if the Window can close or not?

Hack Method

One thing we can do, is inside of the Closing event, we could query the view model to see if it is ok to exit or not. It’s not the greatest, it will be writing a lot of custom code in every window code behind.

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        Closing += MainWindow_Closing;
    }

    private void MainWindowClosing(object sender, CancelEventArgs a)
    {
        // make sure we have the view model class
        if (DataContext is MainWindowViewModel vm)
        {
            // check with the view model if should be canceled
            e.Cancel = vm.ShouldCancel();
        }
    }
}

Not pretty but it works.

Using Attached Properties

The way I chose to do it is to use DependencyProperty’s that participate in the XAML binding system and can attach to the view model directly and not have to wire up each and every window explicitly. To do this, we need to setup a few new properties. One for enabling or disabling this functionality; a second for a parameter to send back and forth from the view model; and finally, a third that will hold an object that contains an action that gets called when the window is attempted to be closed.

Let’s get started with the two dependency properties that are used for holding the can close implementation and the can close parameter that is passed back and forth. These properties have the following shapes.

public interface ICanCloseResult
{
    bool Cancel { get; set; }
}

public interface ICanCloseCheck
{
    void CheckCanClose(ICanCloseResult canCloseResult);
}

And here are the properties that hold these values.

public class CanCloseBinding
{
        public static readonly DependencyProperty CanCloseCheckProperty =
            DependencyProperty.RegisterAttached(
                "CanCloseCheck",
                typeof(ICanCloseCheck),
                typeof(CanCloseBinding),
                new PropertyMetadata(null));

        public static ICanCloseCheck GetCanCloseCheck(DependencyObject sender) => 
            (ICanCloseCheck) sender.GetValue(CanCloseCheckProperty);
        public static void SetCanCloseCheck(DependencyObject sender, ICanCloseCheck value) => 
            sender.SetValue(CanCloseCheckProperty, value);


        public static readonly DependencyProperty CanCloseCheckParameterProperty =
            DependencyProperty.RegisterAttached(
                "CanCloseCheckParameter",
                typeof(ICanCloseResult),
                typeof(CanCloseBinding),
                new PropertyMetadata(null));

        public static ICanCloseResult GetCanCloseCheckParameter(DependencyObject sender) => 
            (ICanCloseResult) sender.GetValue(CanCloseCheckParameterProperty);
        public static void SetCanCloseCheckParameter(DependencyObject sender, ICanCloseResult value) => 
            sender.SetValue(CanCloseCheckParameterProperty, value);
}

Next we have to build up the dependency property that enables or disables the functionality. The important thing this property does is, when it is set to true, it attaches an event handler to the Closing event of the window. Then, when that event is executed, it tries to get the instances of the other two parameters and execute them. So it looks like the following.

namespace CanClose.Views
{
public class CanCloseBinding
{
    // ... other properties here

    public static readonly DependencyProperty CheckCanCloseEnabledProperty =
        DependencyProperty.RegisterAttached(
            "CheckCanCloseEnabled",
            typeof(bool),
            typeof(CanCloseBinding),
            new PropertyMetadata(false, new PropertyChangedCallback(OnCheckCanClosePropertyChanged)));

    private static void OnCheckCanClosePropertyChanged(object sender, DependencyPropertyChangedEventArgs e)
    {
        if (sender is Window w)
        {
            bool newEnabled = (bool) e.NewValue;
            bool oldEnabled = (bool) e.OldValue;

            if (oldEnabled && !newEnabled)
                w.Closing -= MyOwnWindowClosing; // remove the event handler for Closing from the Window
            else if (!oldEnabled && newEnabled)
                w.Closing += MyOwnWindowClosing; // add the event handler for Closing to the window
        }
    }

    public static bool GetCheckCanCloseEnabled(DependencyObject sender) => 
        (bool) sender.GetValue(CheckCanCloseEnabledProperty);
    public static void SetCheckCanCloseEnabled(DependencyObject sender, bool value) => 
        sender.SetValue(CheckCanCloseEnabledProperty, value);

    public static void MyOwnWindowClosing(object sender, CancelEventArgs e)
    {
        /// get the values from the dependency properties
        ICanCloseCheck q = GetCanCloseCheck((Window) sender);
        ICanCloseResult p = GetCanCloseCheckParameter((Window) sender);
        if (q == null || p == null)
            return;

        /// execute and save the value back to the CancelEventArgs object
        q.CheckCanClose(p);
        e.Cancel = p.Cancel;
    }
}
}

Finally, all we need to do is attach these properties to the window that we want to control from our view model. And that is done like this:

<Window x:Class="CanClose.Views.MainWindow"
    xmlns:local="clr-namespace:CanClose.Views"
    local:CanCloseBinding.CheckCanCloseEnabled="True"
    local:CanCloseBinding.CanCloseCheck="{Binding CCQuery}"
    local:CanCloseBinding.CanCloseCheckParameter="{Binding CCResult}"
    >
    <!-- stuff here -->
</Window>

And that is about it. If you want to see a sample, you can find it here.

XF vs PWA: Setting Up the API

Setting Up the API

In this post, I thought we would go over the API: the API will be able to be shared between both the Xamarin Forms app and the PWA. To get access to the API, you have to apply to the Translink Authority and get an API key. Of course, you shouldn’t share that API key with anyone, and that can be pretty difficult to achieve when you have a mobile app that must send it to the provider.

One way you can do it is to hide the key in your app somehow or encrypt it. But if you encrypt it, you are going to have to unencrypt it as well and that means hiding a different key. For this app, I am just going to proxy the provider API with my own API and that way, I won’t have to store the Translink Authority API in my local app. If this was a serious app, I would consider using my own keys and/or looking at user info in my app to guard against mis use. It is much easier to revoke my own keys than it is to have to go back to the Translink Authority for a new key in the event of misuse.

Storing the Key

I am just going to store the key in my appsettings.json file for simplicity. Your appsettings.json is stored in the root of the web api project. It looks like the following:

{
"Logging": {
"includeScopes": false,
"Debug": {
"LogLevel": {
"Default": "Warning"
}
},
"Console": {
"LogLevel": {
"Default": "Warning"
}
}
},
"mySecrets": {
"translinkKey": "xxxxxxxxxxxxxxxxxx"
}
}

To get access to the settings, we have to do some work in the Startup class. I changed the Configuration property to be an instance of IConfigurationRoot interface. To create that, I changed the constructor object to create it.

public class Startup
{
    public IConfigurationRoot Configuration { get; }
    private IHostingEnvironment _hostingEnvironment = null;

    public Startup(IHostingEnvironment env)
    {
        _hostingEnvironment = env;
        var builder = new ConfigurationBuilder()
            .SetBasePath(env.ContentRootPath)
            .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
            .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
            .AddEnvironmentVariables();

        Configuration = builder.Build();
    }

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc();
        services.AddSingleton<IConfigurationRoot>(Configuration);

        // rest of code
    }
}

To access the settings in your controller class, you need to have the IConfigurationRoot object injected into your controller through the constructor.

[Route("api/bus")]
public class BusController : Controller
{
    private IConfigurationRoot _configuration = null;
    private string _apiKey = null;

    public BusController(IConfigurationRoot configuration)
    {
        _configuration = configuration;

        var section = configuration.GetSection("secrets");
        _apiKey = setion.GetValue<string>("translinkKey");
    }

    // controller actions go here
}

Now with the API key the web app can make calls to the Translinke API directly.

If you feel like checking out some of the code, you can find it here.

XF vs PWA: Exploring PWA Service Workers

PWA Getting Started

I am not finished with my work on the Xamarin Forms portion of the series yet, but I had to start some PWA work in the middle, and I thought it would be good to write about my first lessons in PWA land.

Service Workers

I think the biggest lesson I have learned so far as that the service worker is the most important component of a PWA, or the piece that separates a PWA from a regular responsive web app.
One of the really interesting things about a service worker is that it runs in the background of the browser itself and can run even if your app isn’t currently loaded. How cool is that? I am looking forward to trying that one out.

The service worker has a number of capabilities.
Cache Content: It can cache your content, giving you the ability to have an app presence even when you are offline.
Push Notifications: It can subscribe to a push service and receive messages. The service worker can take these messages and update the state of the app or display a notification of some kind.
Notifications: It can send notifications to the user using the host operating system notification mechanism.
Channel Messaging: It can communicate with other service workers and the host application.

The first thing I am going to do is make sure that my app shows up even if it is offline. Even if your app needs to be online to do anything useful, it is still a nicer experience for the user if your app loads and presents an explanation of what is happening.

Setting Things Up

First we will make a simple web page. There won’t be much of anything going on. We will just pull in some bootstrap resources from their CDN and add in some of our own styles and javascript.

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <title>service workers</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css" integrity="sha384-WskhaSGFgHYWDcbwN70/dfYBj47jz9qbsMId/iRN3ewGhXQFZCSftd1LZCfmhktB" crossorigin="anonymous">
    <link rel="stylesheet" href="my.css" >
</head>
<body>
    <h1>hello from pwa land</h1>

    <a href="https://code.jquery.com/jquery-3.3.1.slim.min.js">https://code.jquery.com/jquery-3.3.1.slim.min.js</a>
    <a href="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js">https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js</a>
    <a href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/js/bootstrap.min.js">https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/js/bootstrap.min.js</a>
    <a href="http://my.js">http://my.js</a>
</body>
</html>

and some custom css

body {
    margin: 20px;
}

javascript

console.log('loaded');

For serving up your content, I am using a simple http web server called ‘http-server’. You do require node which you can download from here.

You can install the http-server using the following command:
npm install http-server -g
The -g installs it so that it installed globally and you can use it anywhere.

At the command line, change to your content directory and execute the following command:
http-server -p 8080 -a localhost -c 0
This starts up an http server servicing the content in the current direcctory from http://localhost:8080.
You should be able to open your web browser and go the above address to http://localhost:8080/index.html and serve up the page from above.

Create our Service Worker file

It’s important that we create this file at the root of our web app. Service workers have scope where they apply to the current folder and below. They can’t travel up. I should also note that service workers only work over https connections, localhost being the only exception to that. If you need an ssl certificate, letsencrypt.org is a place where you can get free SSL certificates for your site.

// service worker file

Our file will be empty, but that’s ok, we just need to have a file to handle the registration.

Register our Service Worker

The last thing we do in our index.html file is perform the registration of our service worker. In a script tag just before the closing body tag, add the following code.

    <!-- our code from above -->



        if ('serviceWorker' in navigator) {
            navigator.serviceWorker.register('service-worker.js')
            .then(function(registration) {
                console.log('Service worker registration successful with scope: ', registration.scope);
            })
            .catch(function(error) {
                console.log('Service worker registration failed: ', error);
            });
        }


</body>

This is important. We need to check if our browser supports service workers and that is checked from the navigator object in javascript. If it is, we can do the actual registration. If it isn’t, we just ignore it and keep our app as is. That is a key point to PWA’s: start at the basic functionality and then add on the capabilities as the host browser supports them.

Filling out the Service Worker

Alright, we have a page that is pulling in various resources, some that we own, some via external. Let’s take a look at how we can make the app function offline.
First we need to make a list of the resources that we want to cache. This is pretty simple. Obviously we want the home page, our js and css files, and our external bootstrap and jquery files.

// service-worker.js


(function () {
    'use strict';

    // our list of cache'd items. We can fill this in more later if we want.
    var filesToCache = [
        '.',
        'index.html',
        'my.css',
        'my.js',
        'https://code.jquery.com/jquery-3.3.1.slim.min.js',
        'https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js',
        'https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/js/bootstrap.min.js'
    ];

    var staticCacheShell = 'app-shell';

})();

Next we are going to register an event handler for when the service worker is installed. The purpose of this is to setup the cache.

    // this is just below the var staticCacheShell declaration and before the closing })();

    self.addEventListener('install', function (event) {
        console.log('installing sw and caching statics');
        event.waitUntil(
            caches.open(staticCacheShell)
                .then(function (cache) {
                    console.log('adding static shel cache');
                    cache.addAll(filesToCache);
                    return;
                }).catch(function(error) {
                    console.log(error);
                })
        );
    });

What is happening above is that we are going to create the cache using our key name and then instruct the cache to add all of our pages into it. Pretty straightforward. But, how do we get pages served from the cache when we are offline? That is the next thing we are going to add to our service worker.
We add another event handler intercepting the fetch event. This sits between your app and the internet and allows you to manipulate the request. In our case, we are going to check if the request is in the cache, and if it is, return it back to your app instead of going out on to the internet. If the network request returns a page not found (404) error, we can serve up the app’s not found page (remember to add it to the list of pages). If the request is returned, we make a clone of the response and stuff it in the cache. Finally, if there is any other error, return the app’s app offline page (remember to stick that in the cache as well).

    self.addEventListener('fetch', function(event) {
        console.log('fetch event for ', event.request.url);
        event.respondWith(
            caches.match(event.request).then(function(response) {
                if (response) {
                    console.log('found ', event.request.url, ' in cache');
                    return response;
                }

                console.log('network request for ', event.request.url);
                return fetch(event.request).then(function(response) {
                    if (response.status === 404) {
                        return caches.match('page404.html');
                    }

                    return caches.open(staticCacheShell).then(function(cache) {
                        if (event.request.url.indexOf('test') < 0) {
                            cache.put(event.request.url, response.clone());
                        }

                        return response;
                    });
                });
            }).catch(function(error) {
                console.log('Error ', error);
                return caches.match('pageoffline.html');
            })
        );
    });

Now let’s run our app. I used the http-server from above and loaded the index.html in Edge. I just had my windows update and Edge was upgraded again. In there you can see that the developer tools are showing that the service worker has been registered.
pwagettingstartedservicework

Next we can look at the cache and we can see that all the pages that registered have been added to the cache.
pwagettingstartedcache

I didn’t see a way in the Edge developer tools to simulate taking the page offline, so I went over to Chrome and loaded up my page. I opened up the developer tools, went to the network tab and set the mode to be offline.

pwagettingstartedoffline
If we refresh the page from here, it will still show up, even though there is no network access for the browser. If we try to load some random page, we will get the pageoffline page.

Conclusion

So that is the quick and dirty for getting started with PWA’s, service workers and offline mode. It’s pretty cool tech and I am looking forward to exploring it some more. You can explore this code in Github.

Unable to Debug .NET Standard Project in XF UWP

Helpful Hint

Ran into a funny one today. I was trying to debug my XF project and it wasn’t loading the symbols for the .NET Standard project so it was not hitting any of my breakpoints. I am currently using Visual Studio 2017.7.1. This was happening in my UWP project.

Obviously that’s not too helpful, but luckily I found some help in the Xamarin Forms as to the setting that was causing the problem. It has to do with the type of debugging information your build outputs. All I needed to do was change it to “Pdb-Only” and we were good to go.
credit
dotnetstandarddebug

XF vs PWA: XF Setting Up the Dashboard Page

The Goal

In this post we are going to setup the Dashboard page. The purpose of this page is to list the users favorite routes and stops so that no matter where they are, they can proceed straight to that information. It will be a relatively simple page, really just two lists. Tapping an item on one of the lists will navigate to the appropriate page and display the favorite route or stop.

Laying out the Page

This will be a pretty simple layout. Just use a grid, four rows, an auto height row for each title and an equal expanding height row for each of the lists.

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition Height="*" />
        <RowDefinition Height="Auto" />
        <RowDefinition Height="*" />
    </Grid.RowDefinitions>

    <Label Grid.Row="0" Text="My Favorite Stops" />
    <ListView Grid.Row="1" ItemsSource="{Binding Stops}"></ListView>

    <Label Grid.Row="2" Text="My Favorite Routes" />
    <ListView Grid.Row="3" ItemsSource="{Binding Routes}"></ListView>
</Grid>

ViewModel

The view model is going to be just as simple. Two ObservableCollections to handle the lists and a pair of commands to handle the user tapping on a list item. If you remember from setting up the hamburger menu, we are going to derive our view model from the BaseViewModel class.

public class DashboardPageViewModel : BaseViewModel
{
    private INavigationService _navigationService = null;
    private IFavoriteService _favoriteService = null;


    public DashboardPageViewModel(INavigationService navigationService, IFavoriteService favoriteService)
    {
        _navigationService = navigationService;
        _favoriteService = favoriteService;
    }


    // binds to the routes list view
    private ObservableCollection<FavoriteRouteViewModel> _routes = new ObservableCollection<FavoriteRouteViewModel>();
    public ObservableCollection<FavoriteRouteViewModel> Routes
    {
        get => _routes;
        set => SetProperty<ObservableCollection<FavoriteRouteViewModel>>(ref _routes, value);
    }

    // binds to the stops list view
    private ObservableCollection<FavoriteStopViewModel> _stops = new ObservableCollection<FavoriteStopViewModel>();
    public ObservableCollection<FavoriteStopViewModel> Stops
    {
        get => _stops;
        set => SetProperty<ObservableCollection<FavoriteStopViewModel>>(ref _stops, value);
    }


    // user taps on one of the stops
    private DelegateCommand<FavoriteStopViewModel> _commandGoToStop = null;
    public DelegateCommand<FavoriteStopViewModel> CommandGoToStop
    {
        get
        {
            return _commandGoToStop ??
                (_commandGoToStop = new DelegateCommand<FavoriteStopViewModel>((stop) => { }));
        }
    }

    // user taps on one of the routes
    private DelegateCommand<FavoriteRouteViewModel> _commandGoToRoute = null;
    public DelegateCommand<FavoriteRouteViewModel> CommandGoToRoute
    {
        get
        {
            return _commandGoToRoute ??
                (_commandGoToRoute = new DelegateCommand<FavoriteRouteViewModel>((route) => { }));
        }
    }
}

So how do we get the view model into the page itself? If you remember from the first post, we have to register all of our pages that we navigate to with the container, and we do that in our PrismApplication RegisterTypes method. In this case, we will register the dashboard page and the view model as follows:

public partial class App : PrismApplication
{
    // other stuff here

    protected override void RegisterTypes(IContainerRegistry containerRegistry)
    {
        containerRegistry.Register<IFavoriteService, TestFavoriteService>();
        containerRegistry.RegisterForNavigation<Views.DashboardPage, ViewModels.DashboardPageViewModel>(Pages.Dashboard);
        /// other registrations
    }
}

In the above code, we are telling the navigation service that when we navigate to the DashboardPage, we should also inject the view model into the page’s DataContext member. Of course the navigation service will make use of the Container to resolve any dependencies that might be required for the view model, such as IFavoriteService and INavigationService. Notice that we also register the IFavoriteService implementation in the RegisterTypes function. We don’t have to do that for INavigationService as it is a core part of the Prism framework and already taken care of in the PrismApplication base class.

Initializing the View Model

Things are looking pretty good, but how do we initialize the data in our view model? This is another part where the navigation service in Prism really helps us. After it finishes constructing the page, injecting the view model and making it the active view, it will test to see if the view model that was injected in the data context implements the INavigationAware interface (check out INavigatedAware and INavigatingAware for more granular implementations). INavigationAware looks like this:

public interface INavigationAware
{
    void OnNavigatingTo(INavigationParameters parameters);
    void OnNavigatedFrom(INavigationParameters parameters);
    void OnNavigatedTo(INavigationParameters parameters);
}

We will have our viewmodel implement the INavigationAware interface and then load our data when the ‘OnNavigatedTo’ method is called.

public class DashboardPageViewModel : BaseViewModel, INavigationAware
{
    /// all the other stuff from above here



    public void OnNavigatedFrom(INavigationParameters parameters)
    {
        // nothing doing here
    }

    public async void OnNavigatedTo(INavigationParameters parameters)
    {
        var favStops = await _favoriteService.GetFavoriteStopsAsync();
        /// do something here with the collection
        var favRoutes = await _favoriteService.GetFavoriteRoutesAsync();
        /// do something here with the routes collection
    }

    public void OnNavigatingTo(INavigationParameters parameters)
    {
        // nothing happening here
    }
}

And that is most of the work for the dashboard page. Really the only thing left to do is to make sure that the event that is raised from tapping one of the listview items is routed to our DelegateCommand objects in the view model. For an example of that, check out my article here.

Storing the Favorites Data

By now, you might be thinking, how am I going to store the data for the favorites in a platform independant way and still be able to use it with my shared view model? I am glad you asked, because I think that will be the next post!

code in GitHub