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.

11 November 2014

Returning Task<T> from a Windows Runtime Component

A very short tip this time, that took me some time to find out.

You will need a Windows Runtime Component if you want, for instance, host a background task in an Universal App. The problem with these components is that you cannot return a Task<T> from a public method. You can use Task<T> inside the component, but not return it to the outside world. If you have a method

public async Task<bool> SomeMethod()
{
  return false; // whatever
}

in your Windows RT component, it won’t even compile. But there is a solution – you can return an IAsyncOperation<T>, and that is awaitable too.

Therefore, I use this simple wrapper to use the method both from inside and outside:

public IAsyncOperation<bool> PublicMethod()
{
  return InternalMethod().AsAsyncOperation();
}

private Task<bool> InternalMethod()
{
  return false; // whatever
}

And we’re done. This will compile, and you can now call “await myObject.PublicMethod()” from outside the Windows Runtime Component.

Sometimes code is so easy if you just know how to do it. No sample this time (again) as this is such a small piece of code. If you want to see it running, wait for the next post ;)

23 October 2014

Making the Application Bar display on of top of the UI in Windows Phone 8.1 Store Apps

In Windows Phone 7, 8 and 8.1 Silverlight apps this is very easy: you just set the Opacity of the Application bar to any value less than 1 as described here, and the app does not reserve space for the app bar – it just displays the bar on top of the main UI like this:

image

The problem is, if you set the opacity of what is now called the CommandBar in Windows Phone 8.1 Store apps, the Application bar is translucent indeed - which you can see if you open it - but the apps still reserves space for it in it’s closed state:

image

The fix for this is surprisingly easy, but also pretty hard to find if you don’t know what you are looking for. In the end I got a tip by mail from my famous fellow MVP Rudy Huyn. You just add the following, not very obvious line of code to your app. I added it to my App.Xaml.cs, just before the line “Window.Current.Activate();”

 ApplicationView.GetForCurrentView().SetDesiredBoundsMode(
   ApplicationViewBoundsMode.UseCoreWindow);

Right. And then you get what you want (as is displayed in the first image). And once you enter this line of code into a search engine, then you find hits enough, dating back to even April. But well, that did not help me much. Hopefully this simple article will save someone else a fruitless seach ;)

To make this code compile, you will need to add a “using Windows.UI.ViewManagement;” on top of your file. And although it is in fact an article about code, I will skip the sample download app this time – for just one line of code it feels like a bit of overkill.

Update – be aware that not only the Application Bar now falls over the UI, but the status bar on top as well. So you may want to take that into account when designing your UI: keep some extra space at the top, or make in partially translucent as wel. Thanks to my fellow MVP Shawn Kendrot for pointing me to this article that he has written some time ago

01 October 2014

Visualizing geofences as circles on the map in Universal Apps

Intro

If you tried to work with geofences, you may have encountered this problem. You want to have an easy way to show where the darn things are on the map, so you can test the application. This is actually pretty simple, building upon stuff to draw circles on Windows Phone 8 I wrote earlier.

What is important to know – there are now more or universally applicable objects to depict a location. The first one is BasicGeoposition – this is a struct – that basically only holds latitude, longitude and elevation. The second one is Geopoint. You create a Geopoint from a BasicGeoposition. This kind of wrapper class that allows for setting of some additional location settings – like how coordinates and height should be interpreted. I don’t think any of this stuff is really used yet, but I have the feeling Geopoint is going to get more important over time, and anyway as a GIS person I tend to go with the fullest set of location designation. But you might as just well use just BasicGeoposition as a way to pass on coordinates in a universal app.

Do the math!

This is essentially the same method as I wrote before, but adapted for Geopoint. Some other changes come from my former colleague Jarno Peschier, who in his typical way saw possibilities to optimize speed up the circle calculation code (and optimizations actually work – I tested).

using System;
using System.Collections.Generic;
using Windows.Devices.Geolocation;

namespace MappingUtilities
{
  public static class GeopointExtensions
  {
    private const double circle = 2 * Math.PI;
    private const double degreesToRadian = Math.PI / 180.0;
    private const double radianToDegrees = 180.0 / Math.PI;
    private const double earthRadius = 6378137.0;
    
    public static IList<Geopoint> GetCirclePoints(this Geopoint center,
                                   double radius, int nrOfPoints = 50)
    {
      var locations = new List<Geopoint>();
      double latA = center.Position.Latitude * degreesToRadian;
      double lonA = center.Position.Longitude * degreesToRadian;
      double angularDistance = radius / earthRadius;

      double sinLatA = Math.Sin(latA);
      double cosLatA = Math.Cos(latA);
      double sinDistance = Math.Sin(angularDistance);
      double cosDistance = Math.Cos(angularDistance);
      double sinLatAtimeCosDistance = sinLatA * cosDistance;
      double cosLatAtimeSinDistance = cosLatA * sinDistance;

      double step = circle / nrOfPoints;
      for (double angle = 0; angle < circle; angle += step)
      {
        var lat = Math.Asin(sinLatAtimeCosDistance + cosLatAtimeSinDistance * 
                            Math.Cos(angle));
        var dlon = Math.Atan2(Math.Sin(angle) * cosLatAtimeSinDistance, 
                              cosDistance - sinLatA * Math.Sin(lat));
        var lon = ((lonA + dlon + Math.PI) % circle) - Math.PI;

        locations.Add(new Geopoint(new BasicGeoposition { 
        Latitude = lat * radianToDegrees, Longitude = lon * radianToDegrees }));
      }
      return locations;
    }
  }
}

I think this is the Haversine formula but I am not sure and the point is – it gives list of points that, when drawn on a map, gives a reasonable approximation of a circle, where ever you draw it. And that’s what this is about. The radius is the radius in meters, and the numberOfPoints is still the number of points that is used to draw the polygon. For that’s what we are making – as long as there are enough points, our eyes will see this as a circle.

The method is implemented as an extension method to Geopoint, and while I was at it, I added a helper method for BasicGeoposition as well, which was not quite rocket science.

public static IList<Geopoint> GetCirclePoints(this BasicGeoposition center,
                              double radius, int nrOfPoints = 50)
{
  return new Geopoint(center).GetCirclePoints(radius, nrOfPoints);
}

Geofences you said sir?

So far, I have been mumbling only about coordinates – stuff I tend to talk about a lot – but how do I go from geofences to things on a map? Well, pretty easily, actually. It turns out that a geofence has a property Geoshape of type IGeoshape, but since a geofence can only be a circle right now, you can try to cast it to a Geocircle. And a Geocircle has a Center property of type BasicGeoposition, and a Radius property in meters. And that’s what we needed to use the GetCirclePoints extension method. So it’s actually pretty easy to define a new extension method

using System.Collections.Generic;
using Windows.Devices.Geolocation.Geofencing;
using Windows.Devices.Geolocation;

namespace MappingUtilities.Geofencing
{
  public static class GeofenceExtensions
  {
    public static IList<Geopoint> ToCirclePoints(this Geofence fence, 
      int nrOfPoints = 50)
    {
      var geoCircle = fence.Geoshape as Geocircle;

      if (geoCircle != null)
      {
        return geoCircle.Center.GetCirclePoints(geoCircle.Radius, nrOfPoints);
      }
      return null;
    }
  }
}

I always do the as-and-if-null check. I could have make a hard cast, but I am all for defensive programming.

Now the thing is, the GeofenceMonitor has a Geofences collection that can be used to add geofences (duh) but of course you can also retrieve them. And thus you can make yet another simple extension method to retrieve a list of list of points of your current GeoFenceMonitor

using System.Collections.Generic;
using System.Linq;
using Windows.Devices.Geolocation;
using Windows.Devices.Geolocation.Geofencing;

namespace MappingUtilities.Geofencing
{
  public static class GeofenceMonitorExtensions
  {
    public static IList<IList<Geopoint>> GetFenceGeometries(this GeofenceMonitor monitor)
    {
      return monitor.Geofences.Select( p=> p.ToCirclePoints()).ToList();
    }
  }
}

And since the GeofenceMonitor is a static class with a singleton instance, getting all the points of all the geofences in you app in a handy list by one simple call goes like this:

GeofenceMonitor.Current.GetFenceGeometries();

And by iterating over them, you can draw shapes on the map. The fun thing is, since we now have Universal Apps, you can define all this code in a PCL and use it both on Windows Phone and Windows ,and reference this as a Binary. Today. No need to wait for Windows 10. So I put these methods in a PCL called MappingUtilities. And because the current GeofenceMonitor is a singleton, you can use this code in some button method in your app to just get a quick list of the active geofences and draw them on a map, without having to retrieve data from your models or viewmodels, or however you choose to implement your logic.

But there is a tiny detail though. Bing Maps does not using Geopoint or BasicGeoposition, but still the Location class, and that does not exist on Windows Phone.

One more thing to extend

Since I was creating extension methods anyway, I made a new assembly, this time Windows specific, called MappingUtilities.Windows - I bet you did not see that one coming :) – and added the following two extension methods:

using System.Linq;
using Bing.Maps;
using System.Collections.Generic;
using Windows.Devices.Geolocation;

namespace MappingUtilities
{
  public static class PointHelpers
  {
    public static LocationCollection ToLocationCollection(
      this IList<Geopoint> pointList)
    {
      var locations = new LocationCollection();
      foreach (var p in pointList)
      {
        locations.Add(p.ToLocation());
      }
      return locations;
    }

    public static Location ToLocation(this Geopoint location)
    {
      return new Location(location.Position.Latitude, 
                          location.Position.Longitude);
    }
  }
}

And these you can simply use to quickly turn a Geopoint into a Location, or a list of Geopoints into a LocationCollection. How this is used is displayed in the demo solution.

Wrapping it up

The demo solution creates two geofences, and shows how the location of said fences can be displayed pretty easily by calling the GetFenceGeometries extension method, both on Windows Phone and Windows.

image image

The drawing on the map is done by a simple method that I won’t go into here. I wish to stress the fact that the creation of geofences is in the Shared portion of the app, as well as the facts that these geofences are pretty minimal – I just create them with the default settings - and that I don’t do anything with these geofences apart from creating them. The only thing what this app shows is how easy it is to display geofences using this extension methods, if you have geofences.

If you want to know more about using geofences in Universal Apps and how you could actually be doing something useful with them, please visit my talk on October 18th 2014 where I will be doing a session about this subject on the Lowland Windows Phone Hacker day. This day is organized in a joint effort by both the Dutch and Belgian developer communities in Tilburg, the Netherlands, a place very near the Belgian-Dutch border. Apart from great content - we also have some prizes you may be interested in

image

Just saying :)

27 August 2014

Simple workaround for “Design view is unavailable for x64 and ARM target platforms”

When you are developing for Windows Phone or Windows and you are faced with using a native component (for instance the Bing Maps control on Windows, or the SensorCore api on Windows Phone) you will loose your designers when you are testing, both in Visual Studio and in Blend, as they will only work for “AnyCPU”  or “x86” configurations.This is highly annoying, but there is a kind-of fix.

The trick is to create an extra configuration. I usually make a copy of “Debug”, which I call “DebugArm” I do this by right clicking on the solution, selecting “properties”, and then hit “Configuration Manager”

image

Then I make a copy of Debug, by selecting “New” in the “Configuration” drop down, and I make a copy of Debug, called DebugArm.

image

In that solution I set everything to ARM:

image

So far so good, and nothing new. Now select the Debug project configuration again in Visual Studio, hit “Save all” and compile. Then open the solution in Blend. You will notice of course the designers now work, but now you cannot deploy. Changing that configuration every time again is slow and cumbersome, and it won’t be picked up by Blend. So you have to restart that time and time again. This now, my friends, we will use to our advantage.

Go back to Visual Studio, select “DebugArm”. You can now deploy and test again, but your designers will of course go dead again. But like I said Blend does not pick up that configuration change. Apparently that is only read at startup. So that will keep the Debug configuration and the designer still will work! So now you can use Blend to do design changes, and Visual Studio to actually run and deploy, using two different configuration settings, without having to change al the time!

This switchemarole you have to do every time you start Visual Studio and Blend. Granted, it’s not ideal, but a lot less of a hassle than constantly having to change configurations, which is is slow, while now you only have to select a different Icon in the Task Bar. And this is how it should be done most of the time IMHO – Blend is for the UI, Visual Studio for code.

No code this time, as this is a tools-trick only post.

13 August 2014

Querying the Windows Phone 8.1 map when there are child objects over it

With the Windows Phone 8.1 SDK came (yet again) an improved Map Control. This brought, among other things, the MapIcon. In ye olde days, the map could only draw lines and polygons natively – when you had to draw icons you had to add child objects to the map itself, which were drawn by the UI thread and not by the map. This is slower and eats more resources.

imageThe new map still offers this possibility. And sometimes we need it, too, for a couple of reasons. First of all, a MapIcon can only display an image and an optional label. Second, MapIcons are drawn on the map using a ‘best effort’, which mean overlapping icons don’t get displayed at all and – worse – if you query the map, using the FindMapElementsAtOffset method, they are not found either.

So in some cases we just need to resort to drawing XAML elements by adding to the map’s Children collection – an option, which luckily has been improved tremendously as we now, for instance, can data bind these elements using the MapItemsControl, as explained by my smart fellow MVP Shawn Kendrot. Before 8.1, we needed the Windows Phone Toolkit for that.

But I noticed something very odd. I have been trained to use the MapTapped event to trap the location where the user tapped, as the Tapped event is being trapped by the map itself. It never fires. So I used MapTapped, and then FindMapElementsAtOffset to query the map;

But to my dismay I also discovered that the MapTapped event does not fire either when you tap on a location where a child object is displayed. Ergo – if you tap on the cross where the Windows Phone logo is displayed, nothing happens. So how am I to find out what’s underneath there?

After some trashing around I stumbled upon the answer – if you tap on a child element that’s on the map – not the MapTapped, but the Tapped event fires. “The event that never fires” comes to the rescue. In addition, the Tapped event on the child object itself fires. So I created the following method to query the map:

private void QueryMap(Point offset)
{
  foreach (var obj in MyMap.FindMapElementsAtOffset(offset))
  {
    Debug.WriteLine(obj.GetType());
  };
}
The regular method to trap MapTapped:
private void MyMap_OnMapTapped(MapControl sender, MapInputEventArgs args)
{
  Debug.WriteLine("MyMap.MapTapped occurred");
  QueryMap(args.Position);
}

And a method to trap Tapped when that occurs, and that also routes to the QueryMap method, with a little help from the GetPosition event method:

private void MyMap_OnTapped(object sender, TappedRoutedEventArgs e)
{
  Debug.WriteLine("MayMap.Tapped occurred");
  QueryMap(e.GetPosition(MyMap));
}

And because the events never fire simultaneously, you will see the map finds one PolyLine when you tap on a line just next to the symbol, will and it fill find two lines (the crossing of both) when you tap on the symbol- but never will you get a double hit from both events.

By attaching to the Tapped event of the Child object I can also detect if the symbol itself is tapped:

var childObj = new Image { 
  Source = new BitmapImage(new Uri("ms-appx:///Assets/wplogo.png")) };
MapControl.SetNormalizedAnchorPoint(childObj, new Point(0.5, 0.5));
MapControl.SetLocation(childObj, p5);
MyMap.Children.Add(childObj);
childObj.Tapped += ChildObj_Tapped;



void ChildObj_Tapped(object sender, TappedRoutedEventArgs e)
{
  Debug.WriteLine("ChildObj.Tapped occurred");
}

And thus we can find everything on the map, provided you attach the right methods to the right events. The MapTapped/Tapped combo of the map is an odd one but once you know how to do it – a few extra lines of code does it all.

A demo app demonstrating the whole concept can be found here.The only thing it does is write things to the debug console when you do something, so pay attention what happens there ;-)