25 January 2015

Keeping popups above the bottom app bar in Windows 8.1 store apps

Recently, I wrote about the KeepFromBottomBehavior that helped me to deal with popups that appeared on the bottom of the screen, but were covered by the app bar when using the full screen by applying ApplicationViewBoundsMode.UseCoreWindow. When I started porting parts of Travalyzer to Windows 8.1 as a companion app, I kind of ran into the same problem: popup appearing ‘under’ the bottom app bar.

image

(This time I replaced the map by a simple blue area, to prevent you from needing to install the Bing Maps SDK to run the sample).

So I kind-of ported my KeepFromBottomBehavior to Windows 8.1, and got the following result.image

Which is exactly what I wanted. If the App bar is open, the popup up appears above the App bar, if it is closed, it appears on the very bottom of the screen:

image

The code is actually a lot simpler than the KeepFromBottomBehavior for Windows Phone 8.1,

using System.Threading.Tasks;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;

namespace WpWinNl.Behaviors
{
  public class KeepFromBottomBehavior : SafeBehavior<FrameworkElement>
  {
    private double originalBottomMargin;
    private AppBar bottomAppBar;
    protected override void OnSetup()
    {
      var page = AssociatedObject.GetVisualAncestors().OfType<Page>().First();
      bottomAppBar = page.BottomAppBar;
      bottomAppBar.Opened += BottomAppBarManipulated;
      bottomAppBar.Closed += BottomAppBarManipulated;
      originalBottomMargin = AssociatedObject.Margin.Bottom;
      UpdateBottomMargin();
      base.OnSetup();
    }

    void BottomAppBarManipulated(object sender, object e)
    {
      UpdateBottomMargin();
    }

    protected override void OnCleanup()
    {
      bottomAppBar.Opened -= BottomAppBarManipulated;
      bottomAppBar.Closed -= BottomAppBarManipulated;
      base.OnCleanup();
    }

    private async void UpdateBottomMargin()
    {
      await Task.Delay(1);
      var currentMargins = AssociatedObject.Margin;

      var newMargin = new Thickness(currentMargins.Left, 
currentMargins.Top, currentMargins.Right, originalBottomMargin + (bottomAppBar.IsOpen ?
bottomAppBar.ActualHeight : 0)); AssociatedObject.Margin = newMargin; } public double WindowHeight { get; set; } } }

Here we seen this behavior works very different from it’s Windows Phone counterpart. It finds the Page this behavior is on using the GetVisualAncestors extension methods from WpWinNl, and attaches itself to the bottom AppBar’s Opened and Close events. When these events are fired, the bottom margin of the whole panel this behavior is attached is increased with the actual height of the bottom AppBar. Easy ask cake. The only weird thing is the Task.Delay(1). I found out that if you omit that, the behavior’s actions will flip-flop, that is, it won’t move the panel when you open the AppBar, but it will move it up when you close it. I think this has something to do with the calculation of the ActualHeight not being done yet. By using Task.Delay, with however small the value, the whole event is asynchronous and thus the calculation is not being blocked by whatever it was being blocked ;)

The WindowHeight is no longer a dependency property and is in fact no longer used, it’s just kept here to keep the behavior’s signature identical to it’s Windows Phone counterpart. In the sample solution you will see this behavior works stand-alone, it does not need SizeListenerBehavior as a companion to bind to.

The sample solution is essentially the same as in the previous article, but contains both code for Windows and Windows Phone now.

10 January 2015

“System.IO.FileNotFoundException” using controls from another assembly in Xamarin Forms on iOS.

Disclaimer: I am at the early stages of learning Xamarin and may be just writing some stupid and obvious things. That’s just the way it is. This blog is as much a source of information as a it is a record of my learning process. I am a Windows Phone developer trying to make my code usable on other platforms.

I was building a solution as follows

  • I created a Xamarin solution XamarinPclControlBug for Windows Phone, Android and iOS using the “Blank App (Xamarin.Forms portable)” template.
  • I created a separate extra PCL PclControl that would hold some controls I was creating.
  • I then upgraded the whole solution to Forms 1.3.0 using this step-by-step procedure
  • I added references to PclControl in all four other projects of XamarinPclControlBug
  • I created a StartPage.xaml and made sure that was the start page in XamarinPclControlBug (portable) by setting MainPage to new StartPage() in de App constructor
  • I placed my custom control (and only that custom control) on StartPage.xaml
  • I deployed on Android and Windows Phone, and all was rosy.
  • I deployed on iOS, and I got:
    System.IO.FileNotFoundException: Could not load file or assembly 'PclControl' or one of its dependencies. The system cannot find the file specified.

You can check your references and deployment settings all you want, my friends, it turns out there’s probably a bug in Xamarin Forms’ iOS implementation. If you use controls and only controls from a PCL or dll that does not contain other code that is called from the app, apparently Xamarin on iOS ‘forgets’ to load/deploy the assembly (or something like that). But there is a work around.

In my sample solution I made a trivial control MyButton which is simply a child class of Button that does nothing special. The PCL PclControl contains this only this code:

using Xamarin.Forms;

namespace PclControl
{
  public class MyButton :  Button
  {
  }
}
Which allows me to use it in Xamarin Forms Xaml like this:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
  xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
  xmlns:pclControl="clr-namespace:PclControl;assembly=PclControl"
  x:Class="XamarinPclControlBug.StartPage">
  <pclControl:MyButton Text="Click me" />
</ContentPage>

imagewp_ss_20150110_0001If you deploy this to Android, it looks good (albeit ugly), likewise in Windows Phone. It’s nothing special – just a button. But is you try to deploy this on iOS, this does not work at all, you get the System.IO.FileNotFoundException

 

 

 

 

The work around is as simple as it is weird. You go the iOS app’s AppDelegate class, and you add the code in that I displayed in red and underlined

using MonoTouch.Foundation;
using MonoTouch.UIKit;
using PclControl;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;

namespace XamarinPclControlBug.iOS
{
  [Register("AppDelegate")]
  public partial class AppDelegate :
      FormsApplicationDelegate 
  {
    public override bool FinishedLaunching(UIApplication app, NSDictionary options)
    {
      var b = new MyButton();
      Forms.Init();

      LoadApplication(new App()); 

      return base.FinishedLaunching(app, options);
    }
  }
}

imageIndeed – you make a manual fire-an-forget instance of the control that you want to use, and lo and behold on the right:

If you comment out the var b = new MyButton(); in the sample solution it will not work – if you do uncomment it, it will. Make sure you clean the solution before attempting re-deploy – I have noticed that sometimes making a little change like this is not enough to properly redeploy.

I think this is a bug, and one that took me quite some time to track and work around. Creating the control in code was just the result of testing whether the code could be compiled if I created a control from code – and then suddenly it worked, after hours of checking all kinds of settings. Living on the bleeding edge has it’s price.

UPDATE January 11, 2015

It has been brought to my attention that this is not caused by a bug but due to the behavior of the iOS linker. My brilliant Italian friend and fellow MVP Corrado Cavalli has run into the same or at least very similar problem earlier and has blogged about it as early as October, and recently this article from December by James Montemagno describes the same problem – and solution. Well, I can only hope my blog makes this issue (and the solution) easier to find, because I could not ;)

05 January 2015

Binding shapes to the Windows Phone 8.1 Here Maps control

Intro

Every time I meet someone from the Windows Phone Map team (or get into contact with them in some other way) I talk about the importance of being able to data bind shapes to the maps. Things are definitely improving: XAML elements can displayed using data binding out of the box (we used to need the Windows Phone toolkit for that), but Map Shapes alas not. As the saying goes – ‘if the mountain will not come to Muhammad, then Muhammad must go to the mountain' I took up an old idea that I used for Bing Maps on Windows 8 and later converted to Windows Phone 8.0 – and basically adapted it for use in Windows Phone 8.1. Including some bug fixes.

Map display elements recap

You can display either XAML elements on top of the map or use map shapes. XAML elements can be anything, a can be displayed by either adding them in code to the map’s Children property, or by using a MapItemControl, databind it’s ItemSource property to a list object and them define a template for the display using properties from the object bound to the template. Typically, that looks like this:

<Maps:MapItemsControl ItemsSource="{Binding Activities}">
    <Maps:MapItemsControl.ItemTemplate>
        <DataTemplate >
            <Image Source="{Binding Image}" Height="25"
                Maps:MapControl.NormalizedAnchorPoint="{Binding Anchor}" 
                Maps:MapControl.Location="{Binding Position}">
            </Image>
        </DataTemplate>
    </Maps:MapItemsControl.ItemTemplate>
</Maps:MapItemsControl>

Important to know is that these elements are not drawn by the map, but on top of the map, by the UI thread. Typically, they are slow.

Map shapes, on the other hand, are drawn by the map itself, by native code, and therefore fast. You can add them to the map by adding a MapShape child class to the maps’ MapElements properties. You can choose from MapPolyLine, MapPolygon and – new in Windows Phone 8.1 – MapIcon. Data binding is, unfortunately, not something that is supported.

Enter WpWinNlMaps

This time, you won’t have to type or have to look at a lot of code, as I have already published all that’s necessary on NuGet – in the WpWinNlMaps package. You simply add this to your application, and you are ready to go. Mind you, this pulls in a lot of other stuff – the WpWinNl package itself, MVVMLight, and some more stuff. This will enable you to bind view models to the map, have them displayed to and make them ‘tappable’. This is all done with the aid of – you guessed it – a behavior. MapShapeDrawBehavior to be precise.

Concepts

Typically, maps are divided into layers. You can think of this as logical units representing one class of real-world objects (or ‘features’ as they tend to be called in the geospatial word). For instance, “houses”, “gas stations”, “roads”. In Windows Phone, this is clearly not the case: all MapShapes are thrown into one basket – the MapElements property. In WpWinNlMaps, a layer roughly translates to one behavior attached to the map.

A MapShapeDrawBehavior contains a few properties

  • ItemsSource – this is where you bind your business objects/view models to
  • PathPropertyName – the name of the property in a bound object that contains the Geopath describing the object’s location
  • LayerName – the name of the layer. Make sure this is unique within the map
  • ShapeDrawer – the name of the class that actually determines how the Geopath in PathPropertyName is actually displayed
  • EventToCommandMappers – contains a collection of events of the map that need to be trapped, mapped to a command on the bound object that needs to be called when the map receives this event. Presently, the only events that make sense are “Tapped” and “MapTapped”.

A sample up front

Before going further into detail, let’s do a little sample first, because theory is fine, but code works better, in my experience. So I have a little class containing a viewmodel that has a Geopath, and Name, and a command that can be fired:

public class PointList : ViewModelBase
{
  public PointList()
  {
    Points = null;
  }
  public string Name { get; set; }

  public Geopath Points { get; set; }
  
  public ICommand SelectCommand
  {
    get
    {
      return new RelayCommand<MapSelectionParameters>(
        p => DispatcherHelper.CheckBeginInvokeOnUI(() => 
        Messenger.Default.Send(new MessageDialogMessage(
          Name, "Selected object", "Ok", "Cancel"))));
    }
  }
}

Suppose a couple of these things are sitting in my main view model’s property “Lines”, then I can simply display a number of blue violet lines by using the following XAML

<maps:MapControl x:Name="MyMap" >
  <mapbinding:MapShapeDrawBehavior LayerName="Lines" ItemsSource="{Binding Lines}" 
       PathPropertyName="Points">
  
      <mapbinding:MapShapeDrawBehavior.EventToCommandMappers>
        <mapbinding:EventToCommandMapper EventName="MapTapped" 
             CommandName="SelectCommand"/>
      </mapbinding:MapShapeDrawBehavior.EventToCommandMappers>
      
      <mapbinding:MapShapeDrawBehavior.ShapeDrawer>
        <mapbinding:MapPolylineDrawer Color="BlueViolet"/>
      </mapbinding:MapShapeDrawBehavior.ShapeDrawer>
      
  </mapbinding:MapShapeDrawBehavior>
</maps:MapControl

So:image

  • Objects are created from the collection “Lines”
  • The name of the layer is “Lines” (this does not need to correspond with the name used in the previous bullet)
  • The property containing the Geopath for a single object in the list “Lines” is called “Points”
  • When the event “MapTapped” is detected on one of these lines, the command “SelectCommand” is to be fired. Mind you, this command should be on the bound object. Notice this fires a MessageDialogMessage that can be displayed by a MessageDialogBehavior. Basically, if all the parts are in place, it will show a message dialog displaying the name of the object the user tapped on.
  • This object is to be drawn as a blue violet line.

The result being something as displayed to the right.

Map shape drawers

These are classes that turn the Geopath into an actual shape. You get three out of the box that can be configured using a few simple properties.

  • MapIconDrawer
  • MapPolyLineDrawer
  • MapPolygonDrawer

To draw an icon, line or polygon (duh).  The key thing is – the drawers are very simple. For instance, this is the drawer that makes a line:

public class MapPolylineDrawer : MapLinearShapeDrawer
{
  public MapPolylineDrawer()
  {
    Color = Colors.Black;
    Width = 5;
  }

  public override MapElement CreateShape(object viewModel, Geopath path)
  {
    return new MapPolyline { Path = path, StrokeThickness = Width, 
                             StrokeColor = Color, StrokeDashed = StrokeDashed, 
                             ZIndex = ZIndex };
  }
}

There is only one method CreateShape that you need to override to make your own drawer. You get the viewmodel and the Geopath (as extracted by the MapShapeDrawBehavior and you can simply mess around with it.

The drawer class model is like this:

image

Trapping events and activating commands with EventToCommandMapper

By adding an EventToCommandMapper to EventToCommandMappers you can make a command get called when an event occurs. You can do that easily in XAML as displayed in the sample. Basically only events that have a MapInputEventArgs or TappedRoutedEventArgs can be trapped. In most real-world cases you will only need to trap MapTapped. See the example above how to do that. Keep in mind, again, that although the event is trapped on the map, the command is executed on the elements found on the location of the event.

There is a special case though where you might want to trap the “Tapped” event too – that is when you mix and match XAML elements and MapShapes. See this article for background information on that particular subject.

Some limitations and caveats

  • Even for MapIcons the PathPropertyName needs to point to a Geopath. This is to create a common signature for the CreateShape method in the drawers. Geopaths of one point are valid, so that is no problem. If you provide Geopaths containing more than one point, it will just use the first point.
  • Although the properties are read from bound objects, those properties are not bound themselves. Therefore, an object that has already been drawn on the map will not be updated on the map if you change the contents of the Geopath. You will need to make sure the objects are in an ObservableCollection and then replace the whole object in the collection to achieve that result.
  • If you use a method like mine to deal with selecting objects (firing messages), your app’s code will need to deal with multiple selected objects – since there can be more than one object on one location. My sample solution does clearly not do that. All objects will fire a message, but only one will show the message dialog. A better way to deal with it would be
    • Make a message that contains the MapSelectionParameters that are supplied to the command that is fired on select (see sample code)
    • Have the MainViewModel collect those messages, and decide based upon the SelectTime timestamp what messages are the result of one select action and should be displayed together.
  • In professional map systems the order of the layers determines the order in which elements are drawn. So the layers that are drawn first, appear at the bottom, and everything that’s drawn later on top of it. In Windows Phone, the Z-index of each element determines that. So be sure to set those in the right order if you want to appear stuff on top of each other.
  • Be aware that MapIcons are a bit of an oddball. First, they are drawn using a ‘best effort’. In layman’s terms, this means that some of them won’t a appear if they are too close together. If there are a lot close together, a lot won’t appear. This will change while you zoom and pan, and you have no way to control it. Furthermore, MapIcons don’t always show on top of other shapes even if you specify the Z-index to be higher.

Conclusion

imageThe sample solution shows the drawing of icons, lines and polygons using the binding provided by WpWinNlMaps. You can tap on a map element and the name of the tapped element will display in a message dialog. This should give you a running start in using this library.

Although there are quite some limitations with respect to the binding, mainly caused by the nature of how the map works (you cannot bind to a command that is to be called when you tap the element – you can just provide the name of the command, just like the path property) I think this library makes using the map a lot easier – at least in MVVM apps. I am using this library extensively in my latest app Travalyzer.

Happy mapping!

22 December 2014

A behavior to show a MessageDialog from a MVVMLight viewmodel in Universal apps–with callbacks

Of course you are now all writing MVVM based apps, after all I have been advocating that for years, and you are listening this here old venerable MVP, right? ;). Anyway, some UI interactions between UI and viewmodel can seem to be a bit complicated, but these kinds of problems are usually pretty simple to solve with a combination of a behavior, the MVVMLight messenger, and a message containing a callback. And as I hate to write things more than once, I  made this into a reusable behavior. It ain’t rocket science, but it’s nice and clean.

I start with a class holding the actual message, and in this class file I have defined a callback delegate as well:

using System.Threading.Tasks;
using GalaSoft.MvvmLight.Messaging;

namespace WpWinNl.Behaviors
{
  public class MessageDialogMessage : MessageBase
  {
    public MessageDialogMessage(string message, string title,
                                string okText, string cancelText,
                                MessageDialogCallBack okCallback = null,
                                MessageDialogCallBack cancelCallback = null,
                                object sender = null, object target = null)
                                : base(sender, target)
    {
      Message = message;
      Title = title;
      OkText = okText;
      CancelText = cancelText;
      OkCallback = okCallback;
      CancelCallback = cancelCallback;
    }

    public MessageDialogCallBack OkCallback { get; private set; }
    public MessageDialogCallBack CancelCallback { get; private set; }
    public string Title { get; private set; }
    public string Message { get; private set; }
    public string OkText { get; private set; }
    public string CancelText { get; private set; }
  }

  public delegate Task MessageDialogCallBack();
}

It’s a basic MVVMLight BaseMessage child class, with properties for various texts, and two optional callbacks for OK and cancel actions.

The behavior itself is, as behaviors go, pretty simple:

using System;
using System.Diagnostics;
using Windows.UI.Popups;
using Windows.UI.Xaml;
using GalaSoft.MvvmLight.Messaging;

namespace WpWinNl.Behaviors
{
  public class MessageDialogBehavior : SafeBehavior<FrameworkElement>
  {
    protected override void OnSetup()
    {
      Messenger.Default.Register<MessageDialogMessage>(this, ProcessMessage);
      base.OnSetup();
    }

    private async void ProcessMessage(MessageDialogMessage m)
    {
      bool result = false;
      var dialog = new MessageDialog(m.Message, m.Title);

      if (!string.IsNullOrWhiteSpace(m.OkText))
      {
        dialog.Commands.Add(new UICommand(m.OkText, cmd => result = true));
      }

      if (!string.IsNullOrWhiteSpace(m.CancelText))
      {
        dialog.Commands.Add(new UICommand(m.CancelText, cmd => result = false));
      }

      try
      {
        await dialog.ShowAsync();
        if (result && m.OkCallback != null)
        {
          await m.OkCallback();
        }

        if (!result && m.CancelCallback != null)
        {
          await m.CancelCallback();
        }
      }
      catch (Exception ex)
      {
        Debug.WriteLine("double call - ain't going to work");
      }
    }
  }
}

This being once again a SafeBehavior, it simply starts to listen to MessageDialogMessage messages. When one is received, a MessageDialog is constructed with the message and title to display, as well as an optional Ok and Cancelbutton – and callback. If you send a message, you can provide optionally provide simple method from your viewmodel to be called by the behavior when Ok or Cancel is called, thus providing a two-way communication channel. Mind, though, all this is async, which has a curious side effect: if you send another message before the user has clicked Ok or Cancel, this will raise an exception.This will be displayed in the Visual Studio output window while you debug, but it won’t show up in release, and your message won’t be displayed either – so you need to carefully control sending messages, and not throw them out like there’s no tomorrow.

I have made a little sample app – a Universal app of course, because there have been some pretty clear hints by Microsoft this is the way to go forward. You will notice everything worthwhile is the shared project, up to the MainPage.xaml. People who were present at the latest Lowlands WpDev day last October will remember my fellow MVP Nico Vermeir saying in his talk “don’t do that, there’s pain in there”, and while this is true, it’s an ideal thing for lazy demo writers.

I started out with a blank Universal app, and moved the MainPage.Xaml and it’s companion MainPage.Xaml.cs to the shared project. Then I brought in my WpWinNl project that, amongst other things, drags in MVVMLight and everything else you need. Then I created this whopping complex viewmodel:

using System.Threading.Tasks;
using System.Windows.Input;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using GalaSoft.MvvmLight.Messaging;
using WpWinNl.Behaviors;

namespace SampleMessagePopup
{
  public class MainViewModel : ViewModelBase
  {
    public ICommand MessageCommand
    {
      get
      {
        return new RelayCommand(
          () => 
            Messenger.Default.Send(new MessageDialogMessage(
              "Do you really want to do this?", "My Title", 
              "Hell yeah!", "No way", 
              HellYeah, Nope)));
      }
    }

    private async Task HellYeah()
    {
      Result = "Hell yeah!";
    }

    private async Task Nope()
    {
      Result = "NOOOO!";
    }


    private string result = "what?";
    public string Result
    {
      get { return result; }
      set { Set(() => Result, ref result, value); }
    }
  }
}

imageimageIf you fire the command, it will show a dialog that looks like the left on these two screens (on Windows Phone) and if you press the “no way” button it will show the very right screen. A few things are noticeable:

  • Both methods are async, although they don’t implement anything asynchronous. That’s because I wanted to be able do awaitable things in the callback and a contract is a contract – were not making JavaScript here. Only tools like ReSharper (highly recommended) will make you notice it, but you can ignore it for now.
  • You might notice the callback methods are private but still the behavior is apparently able to call these methods. This, my dear friends who grew up in the nice era of managed OO languages that shield you from the things that move below you in the dark dungeons of your processor, is because you basically give a pointer to a method – and that’s accessible anywhere. Your compiler may tell you it’s a private method, but that’s more like it has a secret telephone number – if you got that, you can access it anywhere ;-). Us oldies who grew up with C or Assembler can tell you all about the times when boats were made of wood and men were made of steel and the great ways you can shoot yourself in the foot with this kind of things. If you want to make you app testable it makes more sense to make the methods public, by the way, but I could not let the opportunity to show this great example of the law of leaky abstractions pass.

But I digress, I know, that is also a sign of age ;)

The Xaml is of course also pretty simple – as this app does not do much:

<Page
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:Interactivity="using:Microsoft.Xaml.Interactivity" 
    xmlns:Behaviors="using:WpWinNl.Behaviors"
    x:Class="SampleMessagePopup.MainPage"
    DataContext="{StaticResource MainViewModel}">
  <Interactivity:Interaction.Behaviors>
    <Behaviors:MessageDialogBehavior/>
  </Interactivity:Interaction.Behaviors>
  <Page.BottomAppBar>
    <CommandBar>
      <AppBarButton Icon="Accept" Label="go ask!" 
                    Command="{Binding MessageCommand}"/>
    </CommandBar>
  </Page.BottomAppBar>

  <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" >
  	<TextBlock Text="{Binding Result}" HorizontalAlignment="Center" 
                   VerticalAlignment="Center" FontSize="56"/>
  </Grid>
</Page>

Notice the behavior sitting on the page itself. For obvious reasons, it’s the logical place to put. You can also put it on the top grid. But whatever you do, use only one per page. They all listen to the same message, and if you put more of them on one page you will get an interesting situation where multiple behaviors will try to show the same MessageDialog. I have not tried this, but I assume it won’t be very useful.

To make this work, by the way, you need of course to declare your viewmodel in App.Xaml. You have no other choice here, as the App Bar sits at the top level of the page, the button calls the command and thus the resource must be one level higher, i.e. the app itself.

<Application
    x:Class="SampleMessagePopup.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:SampleMessagePopup"
    xmlns:system="using:System">

  <Application.Resources>
    <local:MainViewModel x:Key="MainViewModel" />
  </Application.Resources>
</Application>

Again, if you use something like ReSharper a simple click on the declaration in MainPage.Xaml will make this happen.

So, that’s all there is to it. You won’t find the code for this behavior in the solution, as it is already in WpWinNl. Just like the previous one ;). As usual, a sample solution is provided, but in inspired by my fellow MVP Diederik Krols I’ve have decided that going forward I will publish those on GitHub.

13 December 2014

A behavior to deal with UI consequences of full screen and Software Keys in Windows Phone 8.1

This week I was presented with an interesting challenge. Using this technique, I used the whole screen for my app. The problem was I had not anticipated a Denim feature for the so called software buttons. For those unfamiliar with that – on the newest low and mid-tier phones the back, start and search buttons are not necessarily hardware buttons anymore, but can be a dedicated piece of screen that shows buttons. This enables hardware manufacturers to make phones for all platforms in one (hardware) package. Now the Denim firmware – that comes with the Lumia 73x and 83x - enables users to make those software buttons disappear – so the extra piece of screen can be used by the app itself. Pretty awesome. This can be done by using pressing a little arrow that appears on the left of the button bar:image


It can be brought up again by swiping in from the bottom of the imagescreen. Pretty cool, but with a side effect I had not anticipated. If the application view bound mode is set to ApplicationViewBoundsMode.UseCoreWindow in App.Xaml.cs the phone reports the whole screen size – not only the part that is normally taken by the status bar on top and the application bar at the bottom, but also the part that is used by the button bar. The My SensorCore app Travalyzer employs slide-in ‘windows’ that slide in from the side and stick to the bottom. I just took an offset the size of the application bar, and I was done, right? Yeah. Until my app got deployed to Denim phones. When the button bar is hidden, there is no problem as you can see to the right. But when it is not hidden… image

I believe the correct word in this circumstance is something like “blimey”, or maybe “crikey”, depending on what kind part of the globe you come from.

The solution is – you guessed it – a behavior. Or actually, two. But one is an oldie. Never one for original names, I have called it KeepFromBottomBehavior.

The code is actually surprisingly simple:
using Windows.UI.ViewManagement;
using Windows.UI.Xaml;

namespace WpWinNl.Behaviors
{
  public class KeepFromBottomBehavior : SafeBehavior<FrameworkElement>
  {
    private double originalBottomMargin;
    protected override void OnSetup()
    {
      originalBottomMargin = AssociatedObject.Margin.Bottom;
      UpdateBottomMargin();
      ApplicationView.GetForCurrentView().VisibleBoundsChanged += 
        KeepInViewBehaviorVisibleBoundsChanged;

      base.OnSetup();
    }

    void KeepInViewBehaviorVisibleBoundsChanged(ApplicationView sender, object args)
    {
      UpdateBottomMargin();
    }

    private void UpdateBottomMargin()
    {
      if (WindowHeight > 0.01)
      {
        var currentMargins = AssociatedObject.Margin;

        var newMargin = new Thickness(
          currentMargins.Left, currentMargins.Top, currentMargins.Right,
          originalBottomMargin + 
            (WindowHeight - ApplicationView.GetForCurrentView().VisibleBounds.Bottom));
        AssociatedObject.Margin = newMargin;
      }
    }

    #region WindowHeight
  }
}

Like all my behaviors, it’s a SafeBehavior so you have a nice and easy base class. It first saves the current Bottom margin, and then calls the “UpdateBottomMargin” method. That method assumes “WindowHeight” contains the actual (full) height of the space available to the app. It subtracts that from that height the bottom of the rectangle depicting the visible bounds, that is – the part that is, according to the phone, not obscured by an App Bar. That it adds to the original bottom margin (in my app that is zero – I want the window to stick to the very bottom). Net effect: the object to which this behavior is attached always moves upward and downward if the user opens or closes the ‘software button bar’, and if he rotates the phone, it takes that into account as well.

Now WindowHeight is a region (yeah flame me, I use that for Dependency properties) containing a  Dependency property that calls UpdateBottomMargin as well if the WindowHeight changes.

public const string WindowHeightPropertyName = "WindowHeight";

public double WindowHeight
{
  get { return (double)GetValue(WindowHeightProperty); }
  set { SetValue(WindowHeightProperty, value); }
}

public static readonly DependencyProperty WindowHeightProperty = DependencyProperty.Register(
    WindowHeightPropertyName,
    typeof(double),
    typeof(KeepFromBottomBehavior),
    new PropertyMetadata(default(double), WindowHeightChanged));
 
public static void WindowHeightChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
  var thisobj = d as KeepFromBottomBehavior;
  var newValue = (double)e.NewValue;
  if (thisobj != null)
  {
    thisobj.UpdateBottomMargin();
  }
}

But how does this property get it’s value? Enter our old friend SizeListenerBehavior. How this all works, will be demonstrated using a simple app. First,we need to have an emulator capable of displaying software keys. The 8.1 U1 WXGA 4.5 inch fits the bill.To enable soft keys display, you will need to open the additional tools, and under sensor you will find the “Software buttons” checkbox. The emulator will reboot, and then show a screen with software keysimageimageEven with just hardware keys most of the ‘popup’ already disappears behind the app bar, but if you hide the software keys, then swipe them up again, it indeed looks pretty bad – the text “Some popup” has disappeared behind the software buttons bar, and most of the controls that could be there are hardly readable, let alone usable to the user.

The XAML for this page is not quite the most complex in the world.
<Page [name space stuff omitted]
    >
  <Page.BottomAppBar>
    <CommandBar Opacity="0.7">
      <AppBarButton Icon="Accept" Label="Click"/>
    </CommandBar>
  </Page.BottomAppBar>

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

    <!-- Title Panel -->
    <StackPanel Grid.Row="0" Margin="12,0,0,0">
      <TextBlock  Text="MY APP" Style="{ThemeResource TitleTextBlockStyle}" 
                  Margin="0,12,0,0" />
      <TextBlock Text="a map" Margin="0,-6.5,0,26.5" 
                 Style="{ThemeResource HeaderTextBlockStyle}" 
                 CharacterSpacing="{ThemeResource PivotHeaderItemCharacterSpacing}" 
                 VerticalAlignment="Top"/>
    </StackPanel>

    <Maps:MapControl Grid.Row="1"/>
    <Grid Height="150" Grid.Row="1" VerticalAlignment="Bottom" 
      Background="#FF7A2222" >
      <TextBlock Text="Some popup"  
                 Style="{ThemeResource TitleTextBlockStyle}" 
                 VerticalAlignment="Bottom" HorizontalAlignment="Center" />
    </Grid>
  </Grid>
</Page>

Now to solve the problem, follow just these easy steps:

  1. Pull in the newest version of WpWinNl from NuGet. Make sure you have set the NuGet package manager settings to “including prerelease’. You will need to have the 2.1.2 alpha version or higher. Don’t worry about that alpha – I am already using this in my apps, I just haven’t got around to making this a final version.
  2. Compile the application
  3. Open the Windows Phone project in Blend
  4. Find the SizeListenerBehavior in “Assets” , drag it on top of the Page Element, rename it to ContentRootListener
    image
  5. Find the “KeepFromBottomBehavior”, then drag it on top of the grid holding the ‘popup’
    image
  6. On the right hand side, find the “Properties” tab and select the little square beside “WindowHeight”
    image
    In the popup menu, select “Bind to Element”
  7. Now select ContentRootListener element again (in the Objects and TimeLine tab where you just put it in step 4
  8. Select WatchedObjectHeight. That’s it. You are done.
The XAML now looks like this:
<Page [name space stuff omitted]
   >
  <Page.BottomAppBar>
    <CommandBar Opacity="0.7">
      <AppBarButton Icon="Accept" Label="Click"/>
    </CommandBar>
  </Page.BottomAppBar>

  <Interactivity:Interaction.Behaviors>
    <Behaviors:SizeListenerBehavior x:Name="ContentRootListener"/>
  </Interactivity:Interaction.Behaviors>

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

    <!-- Title Panel -->
    <StackPanel Grid.Row="0" Margin="12,0,0,0">
      <TextBlock  Text="MY APP" Style="{ThemeResource TitleTextBlockStyle}" 
        Margin="0,12,0,0" />
      <TextBlock Text="a map" Margin="0,-6.5,0,26.5" 
        Style="{ThemeResource HeaderTextBlockStyle}" 
        CharacterSpacing="{ThemeResource PivotHeaderItemCharacterSpacing}" 
        VerticalAlignment="Top"/>
    </StackPanel>

    <Maps:MapControl Grid.Row="1"/>
    <Grid Height="150"  Grid.Row="1" VerticalAlignment="Bottom" 
    Background="#FF7A2222" >
      <Interactivity:Interaction.Behaviors>
        <Behaviors:KeepFromBottomBehavior 
        WindowHeight="{Binding WatchedObjectHeight, 
        ElementName=ContentRootListener}"/>
      </Interactivity:Interaction.Behaviors>
      <TextBlock Text="Some popup"  
        Style="{ThemeResource TitleTextBlockStyle}" 
        VerticalAlignment="Bottom" HorizontalAlignment="Center" />
    </Grid>
  </Grid>
</Page

In bold and red you see the new parts. And sure enough, if you now watch the ‘popup’ it will always be above the App Bar – it will even move dynamically move up and down if you open and close the software buttons.

imageimage

Problem solved, case closed. You don’t even have to type code because the behavior is already in WpWinNl.

Mind you – this behavior works only on Windows Phone 8.1, since it only is applicable to Windows Phone. That’s because the ApplicationView.GetForCurrentView().VisibleBoundsChanged event is only available on Windows Phone.

Demo solutions – before and after adding NuGet Package and behaviors – can be found here. Mind you – both solutions contain a Windows 8.1 project, but that does not do anything.

Special thanks to my colleague Valentijn Makkenze for first noticing this problem, Ben Greeve and this guy for sending me screenshots, and Hermit Dave for some valuable pointers.

22 November 2014

The Microsoft Band and me–three weeks later

Why? How?

I will be honest – I wasn’t really thinking the whole thing about wearables is more than a bit overhyped marketing stuff and that I personally would not get much use of it. Sure, you had all these devices that could track your running but I am not a really sporty person nor do I feel the need to tell the whole world where I was while I go running. Which is easy enough, because I don’t. So when I woke op on October 30th with the news the Microsoft Band had launched out of the blue, I was surprised but not really enticed by what I deemed to be ‘yet another fitness device’.

Boy, was I wrong.

My gadget addicted former colleague Jarno Peshier - who had read up on the specs - contacted me that morning, writing “I want one. You will be in the USA soon (for the MVP Summit). Let’s order two, you take them home, and split the next day delivery costs”. I was like, “what the heck, it’s not that expensive and if I don’t like it I can probably sell it off easily as they are not for sale in Europe”. So he ordered, got them delivered at the home of a friend living in Seattle, and before 3PM CET (while the USA was still kind of waking up) the order was already processed and two bands were on their way from California to Seattle. This quick decisive action turned out a be a very good idea indeed, as you all probably know what happened when the USA did wake up. Bands sold out faster than turkeys on Thanksgiving and back orders now stretch up until Christmas. MVPs arriving in Seattle mostly ran into sold-out stores. It must have been a pretty frustrating experience both for them and the ever friendly and helpful Store staff. But my Band was already delivered and waiting for me in Seattle a few hours before I actually got up to leave for the airport.

Notifications - the killer feature

Some 24 hours later I actually got my Band

and initially I thought it was pretty OK. At the very least, I could bug all the American MVPs who thought they could buy one in the Bellevue store which – of course – was cleaned out empty. After a few days, I noticed substantial changes in my behavioral patterns. Some week later, after I already was back home again, I found it was now an indispensible device. Why? One word – notifications. I was no longer attached to my phone. I even turned off my phone’s ringer and notification sounds. For a very simple reason. Consider someone like me, who is online a lot and has a Windows Phone. I get a notification. I pick up the phone. I look at the notification. But then I see all these other things on the screen. Which is the whole idea of Windows Phone – to be ‘glanceable’. But I tend get distracted by all these interesting things. So I go and answer this tweet, that Facebook post – and before I know 15 minutes have passed by, my wife – with whom I was on an the typical on-and-off talk when we both arrive home a bit tired from work, is now reading a book, whatever.

Now I get a light vibration on my wrist, look at the Band’s screen (I wear it face-up, so not like in the commercials or on the picture above, and it ended up on my right wrist for some reason), and can see enough if it’s important enough to get up and get my phone (that is lying around somewhere in the room, usually on the charging place) without missing a beat in a conversation. And you know what – usually it is not. I can see every notification from the Notification center, and every mail – but only the few first lines. The same during work. I don’t even have to lift my hands from the keyboard or mouse to check the notification. In a restaurant – my phone is somewhere in a pocket (or last time, in my wife’s handbag). At times, it’s more like a wireless hot spot for my Band ;)

Without noticing, I had become a more sociable creature, and more efficient too. The Band has become my gate keeper. Going into social media and mail is now a conscious decision. I have to get up and find my phone, for instance. Yet I don’t have to shut off my phone anymore to reach that state. I have found a kind of middle ground.

But there’s more

The calendar is synced to the Band as well, so even when I walk out of the house without my phone to do some quick shopping (I never forgot my phone before, but now I do) I still get calendar reminders. It has Cortana integration, so I can actually say “Remind me to take the pizza out of the oven in 30 minutes” to my Band using the action button, and it will remind me in 30 minutes. That will need connection to my phone at the moment I actually take the reminder, but I don’t need to get my phone first. And then I can get out of range and it will still remind me. The other day I was in bed, my wife was out, and my phone was lying around downstairs. I was very tired, and decided to tell my wife I went shut-eye. I found out I could actually say to my band “Text my wife I am going to sleep now” and sure enough, my phone, one floor down (and we have 40cm – well over a foot - concrete floors here) picked up the command. And it sent back response to my Band a minute later, too: “will be home in 15 minutes”.

And even more…

You can read online of course about all the that the Band does. It actually does quite a lot. Apart from notifications, alarms and the Cortana commands, it tracks hearth rate, steps, and calorie burn, you can use it as a fitness coach, track running (it has its own GPS so you don’t have to lug around your phone), and you can use it to check UV levels. I am not even sure I have the complete list covered now. I can tell you it felt a bit odd noticing that, when the cabin attendants told us to put all devices in airplane mode before taking off from Seattle, there is actually an airplane mode on the Band. In 2014, my freaking watch actually has airplane mode and takes voice commands. Welcome in the future.

I really think the UV meter is a really nice and smart touch in these days when we learn more and more about sun burn and skin cancer correlations. It basically takes a sample, and says how much minutes current sustained will typically get you a sunburn. As there has not been very much sun both in Seattle and here since I got the device, I only got “low” the few times the sun actually was out.

Some people, like Paul Thurrott, call it “Microsoft’s moonshot” meaning that Microsoft could not decide what should and should not go into it’s first wearable, so they put in everything and the kitchen sink, making the device complex and hard to operate. I personally think the day-to-day operation of the device is very easy (the tile interface is very simple and natural, especially for someone who has seen Windows Phone or Windows 8 applications) and there are only two buttons. How hard can it be. I think by putting all this stuff into one little device they actually made it something for everyone. My wife wants one too – not for all the gizmo’s I like, but for the fitness/sleep stuff.

Finally – what the designers probably never thought of as actual usage – it makes for an excellent night light in the dark. When you press the big button in the middle – even in sleep mode the screen gives off enough light to see a little by in the pitch dark. And since you are wearing it, you don’t have to find it first. Very useful when you are staying in a strange house or hotel and want to find the toilet without waking everyone up in the middle of the night by turning on the light (or crashing down the stairs – something I narrowly avoided last year).

Charge and charging

Your mileage may vary. The first day, you will probably have a very bad battery experience, as you constantly play with the device. Typical usage will last me just a little short of 48 hours. Charging from empty to full takes about 90 minutes I think, but the funny thing is – charging from like 30% to 80% (which will take you through the day) goes very fast, think 20 minutes (at most). So you can do that over breakfast or while you are in the shower. It only comes with a charging cable, any USB power port will do. Very nice, as it also saved me of having yet another useless USA plug charger ;)

Further effects so far

Apart from a different usage pattern for my phone, making me more sociable and efficient, it also has had an impact on my sleep pattern. I like the sleep tracker, but I don’t like what it’s telling me – I sleep too little. I thought I was getting 8 hours per night – it was more like a little over 6. I should rest more, and I do – it’s now close to 7 hours per night and already feel better. I also feel I will need to do more exercise, as the steps tracker has a default goal of 5000 steps and I get to that usually only half the time. In general, it makes me conscious about a lot of things I did not really know about.

Thoughts on improvements

So is this the perfect device? Not yet, although I must admit I like it 500% more than I ever imagined. My main concerns are

  • While the screen itself seems pretty scratch resistant, the actual watch housing very susceptible to scratching. That is probably why it comes with a free screen protector. I tried to apply that, it did not work out for me, and decided to pull it off again. So I wear the device face up and try to be careful. The housing is now somewhat scratched. As it it is black, it does not attract much attention IMHO. But in a V2 this could be improved upon
  • As the Band is now more or less functioning as a remote for my phone that is ‘somewhere’, I would much like a simple app or function to control the music player. Skip forward one track, skip back one track would be much appreciated. Skip forward 60 seconds, skip back 30 seconds would be an awesome bonus for listening to podcasts (and skipping ads).  I can do that with Cortana to an extent, but it does not always work, especially when the phone is streaming music over Bluetooth and thus that channel is already very busy.
  • I would like to be able to see a 2nd time on ‘Watch mode’. There’s room enough on the screen for that. As someone dealing a lot with people from another time zone (i.e. PST) that would be convenient.
  • The UV meter needs to take into account skin type, or just stick to saying the level. Apart from the level, it now says “typical times to sunburn nn minutes” but how does one define typical? Someone from African heritage can walk for hours in a sunshine that will burn a red-haired descendant from Irish stock into (blistering) misery in 15 minutes. That even varies over the year – while the first fierce spring sun can give bleached-out-me a sunburn, my skin darkens fairly quickly in the sun and the same level of sun power won’t bite me just a month later. I wonder if it would be possible to have the app take a picture of facial skin, analyze that and calibrate the sunburn time from that.
  • The design worries me a little. I find it pretty cool looking and comfortable to wear. It’s light and does not get in the way when I am working on a keyboard, which I found puzzling as my normal watch is too constrictive for wearing while typing. The Band is just a little bit flexible, light and does not get in the way. I also am a fussy sleeper (the most stupid things keep me awake), but I have no troubles sleeping with it. And yet - I have also noticed over the years that the rest of humanity does not necessarily agree with me on what is good looking and what is not. It only comes in black. I wonder how fashionists will think about it.
  • I understand why the companion app works and looks like it does. The New Microsoft went all-platform in the typical new way, which is very commendable and probably has contributed to the device selling out so fast. That does not help that on a Windows Phone it looks odd and alien. The awesome, well-designed and natural feeling UI of the Band itself is a stark contrast with the clunky UI of the companion app.
  • I want an SDK! I want an SDK! Please give me an SDK ;)

Conclusion

For a first launch into the wearables, Microsoft have done a tremendous job at a pretty compelling price. Even with expensive NDD I ended up a little north of €180 (yay Euro!). People routinely spend that on watches that look like they come out of the dashboard of the Titanic bridge - and those only display time. Going cross platform with it from the get go and making it available the next day – full marks. The first batch sold out in a day, the second – this week – lasted about 10 minutes. Consumer demand says it all. I find the device an invaluable companion device to my phone. Especially since I use a 1520, which is pretty big – I don’t pull it out half as much anymore these days. It’s the first gadget I bought that my wife is very explicitly happy about, because I am less distracted. She wants to have one too – that is going to be a bit of a challenge I think. I am also very happy with it, to the extent that I would to get another one ASAP if this one would break down. In the mean time, I just have to be careful.

Long story short – if you are interested in these kind of devices and recognize this user story (or even partially) – go and try to get one. You won’t regret it and even on the very off chance you will – plenty of takers. ;)

Disclaimer

I am not a professional reviewer. This is just my experience with this one device, after about 3 weeks. As I have no comparison material, I also cannot tell if this device is better than comparable devices. I just felt the need to write it from a tech user’s perspective.

12 November 2014

Executing and testing geofencing background tasks in Universal Apps

Intro

On October 18th the Dutch and Belgian Windows Platform Development communities got together for an event called “Lowlands WPdev 2014”- yours truly was one of the conference organizers, and gave a talk about geofencing. This blog post is basically a partial write-down, just explaining the code and how to test it.

The ShopAlerter application

The application is very simple and shows itself like this on Windows Phone 8.1 and Windows 8.1:

imageimage

It shows the created geofences on the map. On Windows Phone, we use Here Maps, on Windows we use Bing Maps. As both banners show, you should get a key for both map platforms to get you app certified. As the enters a geofence, a trigger is fired to show you are approaching a shop and a toast message is being displayed. That is – in theory. In practice stuff is not so easy, especially in Windows.

Project structure

image

The project consists out of what nowadays is a pretty standard Universal App structure, with a Windows, Windows Phone and a Shared Project. In the shared project is the App.xaml and a partial class MainPage.cs, basically holding some of the code behind stuff that normally goes into MainPage.xaml.cs. But we want to share as much code as possible – and we can.

The Mapping.Utilities.Pcl contains helper methods to visualize geofences on the map – described in an earlier article about visualizing geofences.

The MappingUtilities.Windows contains one helper class to convert to the common Geopoint type to the specific Location and LocationCollection types used by Bing Maps.

Finally, the ShopAlerter.Background is a so-called Windows Runtime Component. Background tasks need to be defined specifically in such a component. I am not entirely sure why, but that’s the way it is.

Windows Phone project

The Windows Phone project is pretty simple: a little bit of XAML, and some code behind. The XAML is just this:

<Page[Name space stuff omitted]>
  <Page.BottomAppBar>
    <CommandBar>
      <AppBarButton Icon="ZoomOut" Label="zoom out" Click="ZoomOut"/>
      <AppBarButton Label="Geofences" Icon="View" Click="ToggleGeofences"/>
      <AppBarButton Label="Task" Icon="Target" Click="ToggleTask"/>
    </CommandBar>
  </Page.BottomAppBar>

  <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <Grid Margin="12,0,12,0" >
      <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
      </Grid.RowDefinitions>
      <StackPanel Margin="0,0,0,5">
        <TextBlock TextWrapping="Wrap" Text="ShopAlerter" FontSize="26.667"/>
      </StackPanel>
      <maps:MapControl Grid.Row="1" x:Name="MyMap" >
      </maps:MapControl>
    </Grid>
  </Grid>
</Page>

A command bar with three buttons, and a grid containing the header text and the map itself. The user hits the middle geofences button to turn the geofences on and off, and hits the right button to register the task. The left(zoom out) button is only there for demo purposes – believe me, sometimes the touch screen and the emulator suddenly decide no longer to play together, and while zooming in with a mouse is easy (just double click), zooming out is not possible. So although I don’t consider myself a pro presenter, I feel I can give pro tip here for demoing something with maps – always include a zoom out button to save your … behind when something goes wrong ;-)

It’s hard to believe, but the Windows Phone specific code, in MainPage.Xaml.cs is just this:

protected override void OnNavigatedTo(NavigationEventArgs e)
{
  GeofenceMonitor.Current.Geofences.Clear();
  MyMap.Center = new Geopoint(
    new BasicGeoposition { Latitude = 52.155915, Longitude = 5.390376 });
  MyMap.ZoomLevel = 16;
}

const int fenceIndex = 1;
private void DrawGeofences()
{
  //Draw semi transparent purple circles for every fence
  var color = Colors.Purple;
  color.A = 80;

  // Note GetFenceGeometries is a custom extension method
  foreach (var pointlist in GeofenceMonitor.Current.GetFenceGeometries())
  {
    var shape = new MapPolygon
    {
      FillColor = color,
      StrokeColor = color,
      Path = new Geopath(pointlist.Select(p => p.Position)),
      ZIndex = fenceIndex

    };
    MyMap.MapElements.Add(shape);
  }
}

private void RemoveGeofences()
{
  var routeFences = MyMap.MapElements.Where(
      p => p.ZIndex == fenceIndex).ToList();
  foreach (var fence in routeFences)
  {
    MyMap.MapElements.Remove(fence);
  }
}

This is the power of the Universal App. All the rest is either in the shared project or the Windows Runtime Component that is also used both on Windows and Windows Phone. What you basically see is the OnNavigatedTo method that clears the geofences, and sets the zoom level and center point of the map. The other two methods are just used for drawing circles on the map and removing them – also showed in the same article I wrote earlier in my article about visualizing geofences.

The Shared project

So what's in this famous shared project then, huh? Well, just the partial class MainPage.cs. This adds methods to both the Windows and the Windows Phone MainPage.xaml.cs. The key thing is to understand code is shared, not binaries. Every line is compiled twice, both into the Windows and the Windows Phone project. Unlike Portable Class Libraries or Windows Runtime Components, which are shared binary.

So. The method called by pressing the middle button adds (and shows) or removes the geofences. This is a simple toggle function like this.

private void ToggleGeofences(object sender, RoutedEventArgs e)
{
  if (!GeofenceMonitor.Current.Geofences.Any())
  {
    foreach (var location in LocationObject.GetLocationObjects())
    {
      AddFence(location.Id, location.Location);
    }

    DrawGeofences(); // Actual implementation deferred to none shared portion
  }
  else
  {
    GeofenceMonitor.Current.Geofences.Clear();
    RemoveGeofences(); // Actual implementation deferred to none shared portion
  }
}

Note the actual drawing and removing is done by none shared code. This implies that both in Windows and in Windows Phone there must be methods "DrawGeofences" and "RemoveGeofences" implemented. You can also see here a method "AddFence" being called. This creates an actual geofence that is being monitored by the device:

  public void AddFence(string key, Geopoint position)
  {
    // Replace if it already exists for this key
    var oldFence = GeofenceMonitor.Current.Geofences.FirstOrDefault(p => p.Id == key);
    if (oldFence != null)
    {
      GeofenceMonitor.Current.Geofences.Remove(oldFence);
    }

    var geocircle = new Geocircle(position.Position, 150);
    const bool singleUse = false;
    MonitoredGeofenceStates mask = 0;
    mask |= MonitoredGeofenceStates.Entered;

    // NOTE: Dwelling time!
    var geofence = new Geofence(key, geocircle, mask, singleUse, 
                                TimeSpan.FromSeconds(1));
    GeofenceMonitor.Current.Geofences.Add(geofence);
}

The key thing about geofences is that they, well, have a key as well as a location. So if you add a geofence, you should first check if a geofence with the given key is already present. If it is, delete it first before adding one with the same key.

Then I create the shape for the geofence. Currently, only circles are supported so I create a circle with 150 meters in diameter. Then I define the geofence should be used multiple times – setting singleUse to true will make it disappear from the GeofenceMonitor automatically after it has been triggered. The mask will determine to what events the GeofenceMonitor will respond on this particular geofence. You can choose between Entered, Exited, Removed and None – to respond to multiple events, just OR ( +| ) to the mask like I show in the code.

Finally a word on dwelling time. This feature is here to prevent edge cases, in the most literal sense of the word. Suppose you are sitting on the edge of a fence – geo positioning is never that accurate, and the phone would probably detect it’s in, out, in, out, in (etc) the fence. If your App user gets a bazillion notifications showing that, you will probably not hit a 5 star rating anytime soon. So the default value is 10 seconds – only if you are 10 seconds in a geofence (or left it for 10 seconds) something will happen. For my demos I usually take 1 second because well, audiences tend to get uncomfortable watching a screen where nothing much happens (and the presenter too, for what matters). For production scenario’s, take some time to think about the speed your typical user will move with, the size of your geofences, and the dwelling time you select. I spent some time figuring out why none of my triggers ever went off – using the default 10 seconds, geofences with a 25 m diameter, and moving at 50km/h. I was already out of any fence again before even half the dwelling time had passed ;-)

Finally the method to register the background task, which is pretty simple:

private async void ToggleTask(object sender, RoutedEventArgs e)
{
  var registered = AlertTask.IsTaskRegistered();
  if (registered)
  {
     AlertTask.Unregister();
  }
  else
  {
    await AlertTask.Register();
  }

  var dlg = new MessageDialog(
     string.Format("Task {0}", !registered ? "registered" : "unregistered"));
  await dlg.ShowAsync();
}

The Background task

The background task is defined in one class containing the actual implementation of the task, as well as some static methods to handle registering and unregistering – an idea from our local DX guru Rajen Kishna, that I improved a little upon. The basic class definition with the required Run method is pretty simple:

public sealed class AlertTask : IBackgroundTask
{
  public void Run(IBackgroundTaskInstance taskInstance)
  {
    var monitor = GeofenceMonitor.Current;
    if (monitor.Geofences.Any())
    {
      var reports = monitor.ReadReports();
      foreach (var report in reports)
      {
        var l = LocationObject.GetLocationObject(report.Geofence.Id);

        switch (report.NewState)
        {
          case GeofenceState.Entered:
            {
              ShowToast("Approaching shop", l.Name);
              break;
            }
        }
      }
    }
  }
}

Mind you – in a Windows Runtime Component classes are sealed. This is mandatory. Anyway, when the background task is triggered, I first check if there are any geofences – logically there should be, for what else could have triggered this task. But they have been deleted on some other thread – it never hurts to be careful. Then I follow this procedure:

  1. Read all the reports from the monitor
  2. Each report should hold a Geofence object
  3. That has an id
  4. Use the id to retrieve the original object the geofence was created from

For the last step I use a LocationObject helper method. That is kind of my ‘data base’, which I will show later. Having retrieved the object, I can show it’s name in a toast.

private static void ShowToast(string firstLine, string secondLine)
{
  var toastXmlContent = 
    ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastText02);

  var txtNodes = toastXmlContent.GetElementsByTagName("text");
  txtNodes[0].AppendChild(toastXmlContent.CreateTextNode(firstLine));
  txtNodes[1].AppendChild(toastXmlContent.CreateTextNode(secondLine));

  var toast = new ToastNotification(toastXmlContent);
  var toastNotifier = ToastNotificationManager.CreateToastNotifier();
  toastNotifier.Show(toast);

  Debug.WriteLine("Toast: {0} {1}", firstLine, secondLine);
}

A simple helper method, that I kinda stole from Rajen as well. My only improvement is the Debug.WriteLine at the end, which we unfortunately will need badly further down the road.

The methods to query the background task’s state, to register and unregister is are also based upon Rajen’s sample, only I have made the Register task actually awaitable. That’s a real live application of the trick I blogged about yesterday.

private const string TaskName = "ShopAlerterTask";
//NOTE:  WinRT component cannot return a Task<T>, but CAN return 
//IAsyncOperation<T>/>
public static IAsyncOperation<bool> Register()
{
  return RegisterInternal().AsAsyncOperation();
}

private async static Task<bool> RegisterInternal()
{
  if (!IsTaskRegistered())
  {
    await BackgroundExecutionManager.RequestAccessAsync();
    var builder = new BackgroundTaskBuilder 
      { Name = TaskName, TaskEntryPoint = typeof(AlertTask).FullName };

    builder.SetTrigger(new LocationTrigger(LocationTriggerType.Geofence));
    builder.Register();
    return true;
  }
  return false;
}

public static void Unregister()
{
  var entry = BackgroundTaskRegistration.AllTasks.FirstOrDefault(
     t => t.Value.Name == TaskName);

  if (entry.Value != null)
  {
    entry.Value.Unregister(true);
  }
}

public static bool IsTaskRegistered()
{
  return BackgroundTaskRegistration.AllTasks.Any(t => t.Value.Name == TaskName);
}

In the RegisterInternal method I simply make a fairly standard background task, only it’s now triggered by a LocationTrigger. The odd thing is, the only parameter you can – and must – supply to the LocationTrigger’s constructor is a LocationTriggerType enum, and that only has one value – Geofence. I assume the API designers originally planned to implement more options here but in it’s current state it’s looking a bit strange. But anyway, it works, that’s the important part. If now anyone enters or exits a geofence, the Run method will be called.

Location object

This is not very interesting, but only showed here for the sake of completeness. It’s basically an object with an ID and a location (which I need for creating a geofence) and a name to display. Two static helper methods help me to create a list of fake hard coded objects, or retrieve one by ID. The area is real – it’s the main shopping street of my home town Amersfoort – but the data is completely fake. There is, unfortunately, no Microsoft store on this continent, let alone in Amersfoort.

public sealed class LocationObject
{
  public LocationObject()
  {
  }

  public LocationObject(string id, string name, 
     double latitude, double longitude)
  {
    Id = id;
    Name = name;
    Location = new Geopoint(
        new BasicGeoposition 
          { Latitude = latitude, Longitude = longitude });
  }

  public string Id { get; set; }

  public string Name { get; set; }

  public Geopoint Location { get; set; }

  public static IEnumerable<LocationObject> GetLocationObjects()
  {
    return new List<LocationObject>
           {
             new LocationObject("1","Microsoft Store", 52.157043, 5.392407),
             new LocationObject("5","Colruyt", 52.156339, 5.391015),
             new LocationObject("9","The PhoneHouse", 52.154651, 5.388553),

           };
  }
  public static LocationObject GetLocationObject(string id)
  {
    return GetLocationObjects().FirstOrDefault(p => p.Id == id);
  }
}

Testing the Windows Phone project

This is pretty easy, thanks to the awesome Windows Phone emulator and it’s tools. Deploy the Windows Phone 8.1 project to the emulator, then proceed as follows (I kind of like that Bothell is displayed by default, as I spend two nights there recently visiting an old friend):

image

  1. imagePress the “Additional tools” button
  2. Select the “Location” tab
  3. Select the “Load” button
  4. Find the file “Locations.xml” that comes with the demo solution (it is in the project root) and select it.

Note I also marked the play button – we will need that later. After you have loaded the xml file, zoom in about five times (press the zoom in button) and you should see about as displayed on the right.

Then proceed as follows:

  • Press the middle button on the application bar (the one that is labeled “Geofences”). The geofences should appear as semi translucent circles as displayed in the very first image on top of this post.
  • Then press the right button on the application bar (“Task”) – this should pop up a message box indicating the task has been registered
  • The press the “Play” button on the Location Tools

Now if you have done everything right, pretty soon you should see the first toast pop up, and they should appear in the notification center as well. Since the phone should move on walking speed (it’s a pedestrian area) you might need to wait for a while for them all to appear.

imageimage

Awesome. Now it’s time to have a look at the Windows project.

The Windows project

Since we are using maps we are actually in a quite bad position, as the convergence story is still pretty weak in this area. For Windows uses Bing Maps – you will need to install the Bing Maps toolkit, use a different kind type of object for map positions (Location in stead of Geopoint), and Bing Maps is a native component so this will make your app a native app, too (i.e. not AnyCPU). And yet… This is all the XAML we need

<Page>
  <Page.BottomAppBar>
    <CommandBar IsOpen="True">
      <CommandBar.SecondaryCommands>
        <AppBarButton Icon="ZoomOut" Label="zoom out" Click="ZoomOut"/>
      </CommandBar.SecondaryCommands>
      <AppBarButton Label="Geofences" Icon="View" Click="ToggleGeofences"/>
      <AppBarButton Label="Task" Icon="Target" Click="ToggleTask"/>
    </CommandBar>
  </Page.BottomAppBar>
  <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <maps:Map x:Name="MyMap"></maps:Map>
    <TextBlock HorizontalAlignment="Left" TextWrapping="Wrap" Text="ShopAlerter" 
               VerticalAlignment="Top" Foreground="Black" FontSize="48"
               Margin="150,5,0,0"/>
  </Grid>
</Page>

Granted, different, but not that different and I also choose a little bit different layout anyway. The only Windows specific code is this:

 MapShapeLayer fenceLayer;

protected override void OnNavigatedTo(NavigationEventArgs e)
{
  base.OnNavigatedTo(e);
  GeofenceMonitor.Current.Geofences.Clear();

  MyMap.Center = 
  new Location { Latitude = 52.155915, Longitude = 5.390376 };
  MyMap.ZoomLevel = 17;

  fenceLayer = new MapShapeLayer { ZIndex = 1 };
  MyMap.ShapeLayers.Add(fenceLayer);
}

private void DrawGeofences()
{
  var color = Colors.Purple;
  color.A = 80;
  foreach (var pointlist in GeofenceMonitor.Current.GetFenceGeometries())
  {
    var shape = new MapPolygon
    {
      FillColor = color,
      Locations = pointlist.ToLocationCollection()
    };
    fenceLayer.Shapes.Add(shape);
  }
}

private void RemoveGeofences()
{
  fenceLayer.Shapes.Clear();
}

That is all.  I love the Universal App model. All I need are methods to set the initial zoom level and location of the map, and methods to draw and delete geofence geometries. The rest is shared code in some way or the other.

You can see here Bing Maps uses the concept of layers. That’s about the only thing Bing Maps handles better than Here Maps IMHO – the z-index of map objects is handled by a layer object rather than on a per-object basis. This makes certain display manipulations a lot easier and faster. The real pain, though, is in testing this.

Testing the Windows project

We will need to test in the Windows Simulator. After all, we will need to test using locations, and I don’t feel like running around our main shopping street testing my app. Now there are some interesting challenges:

  1. The Simulator does not support a notion of route – only of location. You can basically appear on one location and then on another, but not make it smoothly move from a to b, like the Windows Phone simulator. But that is the least of our worries.
  2. The Windows simulator does not display toast notifications. This apparently has something to do with the simulator being a kind-of-copy of your actual PC, with all your apps installed.
  3. The Windows simulator does not allow the registration of background tasks. Try it. You will simply get an exception saying “This request not supported”. You can unregister, but not register. I have no idea why.

This sounds like a classical catch-22 – we need the Simulator to simulate locations, but we cannot register background task to actually use those locations, and even if we could, we cannot display the result. But there is a way out. Kind of. A long and winding way. Follow these steps:

  1. Run the app from Visual Studio on your local machine first.
  2. Make sure the “Debug Location” toolbar is visible. If it’s not: click View/Toolbars and check “Debug Location”. You will need to do this step only once.
  3. Press the “Task” Application bar button. This will show the following pop up, that you may have seen before on other apps asking for background access
    image
    and if you press “Allow” you will get
    image
  4. The task is now registered. Now stop the application (just from Visual Studio).
  5. Deploy and run the app from Visual Studio on the simulator. Remember I wrote about this being a kind of copy of your PC? Guess what, your app is already there, including the background task.
  6. Hit the “Geofence” application bar button. You will now see the geofences.
  7. You will now have to set the location of the simulator. There’s a button for that on the right hand side of it’s window. I picked the location of the ‘Microsoft Store’ as location.image
  8. Nothing happens if you press the “Set Location” button. Now go to the Debug/Location toolbar. Click the Lifecyle Events drop down. It should show the following, if it does not, try to reselect the
    ShopAlerter.Windows.exe again first
    image
  9. Still nothing seems to happen. But if you mosey back to Visual Studio and have a look at your output window….
    image
    Lo and behold – the result of the Debug.WriteLine. The toast code has fired, the geofence therefore has been activated, thus our code works.

Some thoughts and things to take into account

  • To make this app work at all, you will need to set some capabilities. For both apps, you will need the “Location” capabilities, and both applications needs to be Toast capable.
  • For the Windows application, you will need to set a Badge logo.
  • To save battery and to prevent you app from hogging the processor, LocationTriggers will only fire once every 2 minutes – at most, at least on the phone. So this technique is not very suitable for high-precision, high-speed tracking. Geofence events will “coalesce”, as it is so beautifully called – meaning they happen about every two minutes all at the same time.
  • The Windows Simulator – to put it very mildly – is not quite on par with the Windows Phone emulator. Particularly the situation around location simulation, background tasks and notifications could benefit significantly from quite some attention. I sincerely hope this will be addressed in some future release – preferably the very next one.

Credits

I have been standing on the shoulders of giants cobbling this all together, most notably:

Demo solution available here.