25 June 2014

Using SensorCore and MVVMLight to build a ‘what did I do where and when’ Windows Phone 8.1 app

Introduction

As you might have seen, the SensorCore SDK has been released. I intended to bring out a full fledged app utilizing this new and exiting SDK, but I found out that I first had to explain a bit about the ‘supporting acts’ I was going to use – if you study my last four posts you will see they all contain parts of the sample app that goes what this post. In addition, the last month I had a rather time- and energy-consuming, frustrating, ultimately fruitless and disappointing thing cross my way with which I won’t bother you with – sufficient to say I would rather have spent this time on writing apps.

Anyway. I set out to make an app that would use the both the SensorCore ActivityMonitor and TrackPointMonitor, as well as the existing MapLocationFinder to find out what I was doing where, and when.

For those who are not familiar with the concept of SensorCore: a few newer devices, among which the Lumia 630, have hardware aboard that allows you to track where the user has been about every 5 minutes, and what he was doing, for the last 10 days. Based upon this information, it designates places that are apparently important to the owner (like home, work). In addition, it has a step counter. Everything is handled by a special, very low power piece of hardware that continuously tracks location and movement with very little battery drain.

Now for the tin foil hat crew: there are some key differences to the way other platforms handle this. First of all, it can only happen with the user’s consent – it’s a phone setting that has to be explicitly turned on, and then and only then the hardware starts to collect data. Furthermore, this data never leaves the phone all by itself – there is no Bing server tracking your actions to clutter your phone with ads or other stuff you may not necessarily appreciate. The only way this data can ever leave your phone is by some for of app – but first you have to give the phone consent, then the app, and said app has to pass certification as well. This Windows/Windows Phone ecosystem takes your privacy very seriously. Ask any developer – almost everyone has been rejected at least once over some tiny detail they missed in the privacy sections of the certification requirements ;-)

The app I am going to show you looks like this. With sincere apologies for the video quality – apparently maps on a slow device is a bit too much for a cable connection with a Lumia 630, but it shows the gist.

SensorCore and MVVMLight

Setting the stage

So I created a Windows Phone 8.1 (Store) app WhatWhenWhere with one supporting class library WpWinNl.MvvmLight.Contrib. I added my WpWinNlMaps NuGet packages to both projects, and added PropertyChanged.Fody as well to it. The contrib package holds BaseNotifyingModel and TypedViewModelBase – as described in this article on using PropertyChanged.Fody for Model-to-Viewmodel communication

An other minor detail – you might want to pull in the Lumia SensorCore NuGet package as well ;-)

A model to hold one location

So I needed a model to know where and when something happened (a TrackPointMonitor TrackPoint), what I was doing there (an ActivityMonitorReading) and an approximate street address (a MapLocationFinder MapLocation). First analysis learns that an ActivityMonitorReading only holds a Timestamp and an Activity. This activity describes what I was doing (Idle, Moving, Stationary, Walking or Running). As I already know the time from the TrackPoint, and I only use that Timestamp to get the accompanying Activity, we might as just only hold that Activity, and not the whole ActivityMonitorReading.

So I created the following model class:

using System;
using System.Linq;
using Windows.Devices.Geolocation;
using Windows.Services.Maps;
using Lumia.Sense;
using WpWinNl.MvvmLight;

namespace WhatWhenWhere.Models
{
  public class ActivityPoint : BaseNotifyingModel
  {
    public ActivityPoint()
    {
    }

    public ActivityPoint(TrackPoint p)
    {
      LengthOfStay = p.LengthOfStay;
      Position = p.Position;
      Radius = p.Radius;
      Timestamp = p.Timestamp;
    }

    public ActivityPoint(TimeSpan lengthOfStay, BasicGeoposition position, 
      double radius, DateTimeOffset timestamp, Activity activity)
    {
      LengthOfStay = lengthOfStay;
      Position = position;
      Radius = radius;
      Timestamp = timestamp;
      Activity = activity;
    }

    public TimeSpan LengthOfStay { get; set; }
    public BasicGeoposition Position { get; set; }
    public double Radius { get; set; }
    public DateTimeOffset Timestamp { get; set; }
    public Activity Activity { get; set; }

    public MapLocation LocationData { get; set; }

    public void LoadAddress()
    {
      if (LocationData == null)
      {
        MapLocationFinder.FindLocationsAtAsync(
new Geopoint(Position)).AsTask().ContinueWith(p => { var firstLocationData = p.Result.Locations; if (firstLocationData != null) { LocationData = firstLocationData.FirstOrDefault(); } }); } } } }

Which has, unlike the TrackPoint and the ActivityMonitorReading, the added benefit of being serializable on account of having a default constructor (ahem) which comes in handy when doing storing state when the app is deactivated (note: the demo app does not handle that).

You might notice the LoadAddress method that can called externally to load the address on demand (in stead of just doing that in the constructor. This has been done for performance reasons – geocoding and loading addresses is expensive and  slow, and you don’t want to do that if you are not sure if the user actually wants to see those addresses or not. So you defer that to the last possible moment – when the user actually selects this object.This technique is described in detail in this article .Notice it carefully avoids the way how you obtain these locations ;-) .

A model to load the data.

The bulk of the DataLoaderModel is shamelessly stolen from the samples.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using GalaSoft.MvvmLight.Messaging;
using Lumia.Sense;
using WhatWhenWhere.Models.Messages;
using Tracker = Lumia.Sense.TrackPointMonitor;

namespace WhatWhenWhere.Models
{
  public class DataLoaderModel
  {
    public DataLoaderModel()
    {
    }

    public async Task Init()
    {
      await InitTracker();
      await InitActivityMonitor();
    }

    private Tracker tracker;
    private async Task<bool> InitTracker()
    {
      if (await Tracker.IsSupportedAsync())
      {
        if (tracker == null)
        {
          if (await CallSensorcoreApiAsync(async () =>
              tracker = await Tracker.GetDefaultAsync()))
          {
            return true;
          }
        }
      }
      return false;
    }

    private ActivityMonitor activityMonitor;

    private async Task<bool> InitActivityMonitor()
    {
      if (await ActivityMonitor.IsSupportedAsync())
      {
        if (activityMonitor == null)
        {
          if (await CallSensorcoreApiAsync(async () =>
              activityMonitor = await ActivityMonitor.GetDefaultAsync()))
          {
            return true;
          }
        }
      }
      return false;
    }

    public async Task SetSensorState(bool enable)
    {
      await SetSensorState(tracker, enable);
      await SetSensorState(activityMonitor, enable);
    }

    private async Task SetSensorState(ISensor sensor, bool active)
    {
      if (sensor != null)
      {
        if (!active)
        {
          await CallSensorcoreApiAsync(async () => 
{ await sensor.DeactivateAsync(); }); } else { await CallSensorcoreApiAsync(async () =>
{ await sensor.ActivateAsync(); }); } } } private async Task<bool> CallSensorcoreApiAsync(Func<Task> action) { try { await action(); } catch (Exception ex) { SenseHelper.GetSenseError(ex.HResult); Messenger.Default.Send( new SenseFailureMessage(SenseHelper.GetSenseError(ex.HResult), this)); // This is now not handled, but should really be handled as described here // (Calling the SensorCore SDK safely) return false; } return true; } } }

It has some clever tricks with Lambdas to be able to call SensorCore methods without having to copy & past the whole try/catch stuff. I basically only added in both sensors, and let it send out an error message via the MVVMLight Messenger if things go wrong (note: app does not handle this, but I think it’s a good pattern).

I did add some code to this stolen-from-sample class, to actually load the data into the model:

public public async Task<bool> LoadInitialData()
{
  if (tracker != null && activityMonitor != null)
  {
    var result = await tracker.GetTrackPointsAsync(
       DateTime.Now - TimeSpan.FromDays(10), TimeSpan.FromDays(10));
    if (RoutePoints == null)
    {
      RoutePoints = new List<ActivityPoint>(result.Select(p => new ActivityPoint(p)));
    }
    else
    {
      RoutePoints.Clear();
    }
    await LoadActivities();
    return true;
  }
  return false;
}

private async Task LoadActivities()
{
  foreach (var r in RoutePoints)
  {
    try
    {
      var reading = await activityMonitor.GetActivityAtAsync(r.Timestamp);
      r.Activity = reading.Mode;
    }
    catch (Exception ex)
    {  // lame – I know.
    }
  }
}

public List<ActivityPoint> RoutePoints { get; set; }

This is the core of the whole app – the rest is fluff to make things visible. LoadInitialData first loads all the TrackPoints from the last 10 days (which is the maximal available time anyway) and converts them into my ActivityPoint model. It then proceeds to find the actual activity that was performed at the time the TrackPoint was recorded. And if you call the ActivityPoint’s LoadAddress method, it will find the (approximate) address of the location it was recorded – it will even return highway names (it’s won’t find a house number then, but I don’t think that will surprise anyone).

A view model for a single point

Quite a lot of how this this works, is already described in my article on using PropertyChanged.Fody. I can directly bind to the model, but for formatting output and commands I like to keep my model clean and employ a viewmodel for that.

using System;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Windows.Input;
using Windows.Devices.Geolocation;
using Windows.Services.Maps;
using GalaSoft.MvvmLight.Command;
using GalaSoft.MvvmLight.Messaging;
using GalaSoft.MvvmLight.Threading;
using WhatWhenWhereMessages;
using WhatWhenWhere.Models;
using WpWinNl.MvvmLight;

namespace WhatWhenWhere.ViewModels
{
  public class ActivityViewModel : TypedViewModelBase<ActivityPoint>
  {
    public ActivityViewModel()
    {
    }

    public ActivityViewModel(ActivityPoint p) : base (p)
    {
    }
  
    public Geopath Location
    {
      get
      {
        return new Geopath(new[] { Model.Position });
      }
      set
      {
        var p = value.Positions.FirstOrDefault();
        if (Model.Position.Equals(p))
        {
          Model.Position = p;
          RaisePropertyChanged(() => Location);
        }
      }
    }

    public string DateAndTime
    {
      get
      {
        return Model != null ? 
Model.Timestamp.ToString("dd-MM-yyyy hh:mm:ss",
CultureInfo.InvariantCulture) : string.Empty; } } public string LocationName { get { return Model != null && Model.LocationData != null ?
GetFormattedAdress(Model.LocationData.Address) : string.Empty; } } private string GetFormattedAdress(MapAddress a) { if (a == null) throw new ArgumentNullException("a"); return string.Format("{0} {1} {2} {3}",
a.Street, a.StreetNumber, a.Town, a.Country); } public ICommand SelectCommand { get { return new RelayCommand( () => { Messenger.Default.Send(new SelectedObjectMessage(this)); Model.LoadAddress(); }); } } public ICommand DeSelectCommand { get { return new RelayCommand( () => Messenger.Default.Send(new SelectedObjectMessage(null))); } } protected override void ModelPropertyChanged(object sender,
PropertyChangedEventArgs e) { if (e.PropertyName == "LocationData") { DispatcherHelper.CheckBeginInvokeOnUI(() =>
RaisePropertyChanged(() => LocationName)); } } } }

There are a few things that might attract your attention:

  • The Location in this viewmodel is a GeoPath of only one position. This is because I want to re-use my MapBinding assembly that I originally created for Windows Phone 8, and recently ported to Windows Phone 8.1
  • The SelectCommand explicitly launches the loading of the address when the viewmodel is selected. In my previous sample I showed a way to do this in the getter of a property, but this is a better way I think (as I already said then)
  • The ModelPropertyChanged method launches a RaisePropertyChanged using MVVMLight’s DispatcherHelper. While this is very useful, it requires the DispatcherHelper to be initialized in the App.Xaml.cs. We will get to that later.

Bringing it all together

The MainViewModel is always my ‘class that brings it all together’. I won’t show all details here, or this article will be even longer than it already is. I start with some initialization stuff:

using System.Collections.ObjectModel;
using System.Threading.Tasks;
using System.Windows.Input;
using Windows.Devices.Geolocation;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using GalaSoft.MvvmLight.Messaging;
using WhatWhenWhereMessages;
using WhatWhenWhere.Models;
using WpWinNl.Messages;
using WpWinNl.Utilities;

namespace WhatWhenWhere.ViewModels
{
  public class MainViewModel : ViewModelBase
  {
    public async Task Start()
    {
      if (Activities == null)
      {
        Activities = new ObservableCollection<ActivityViewModel>();
      }

      if (Route == null)
      {
        Route = new ObservableCollection<RouteViewModel>();
      }

      Messenger.Default.Register<WindowVisibilityMessage>(this, async m =>
      {
        await ProcessWindowVisibilityMessage(m);
      });

      Messenger.Default.Register<SelectedObjectMessage>(this, 
ProcessSelectedObjectMessage); await Model.Init(); } private void ProcessSelectedObjectMessage(SelectedObjectMessage message) { SelectedItem = message.Activity; } private async Task ProcessWindowVisibilityMessage(WindowVisibilityMessage m) { if (Model != null) { if (!IsInDesignMode) { await Model.SetSensorState(m.Visible); } } } } }

And you can also see the viewmodel listens to two messages: one that is fired when an object is selected, and one that is fired when the main windows becomes visible (or invisible) - and sets the sensor state according to that. The second half of the MainViewModel mostly contains data properties and a command:

public ICommand LoadCommand
{
  get
  {
    return new RelayCommand(
      async () =>
      {
        await Model.LoadInitialData();
        Model.RoutePoints.ForEach(p => Activities.Add(new ActivityViewModel(p)));
        Route.Clear();
        var route = new RouteViewModel(Activities);
        Route.Add(route);
        ViewArea = GeoboundingBox.TryCompute(route.Path.Positions);
      });
  }
}

public ObservableCollection<RouteViewModel> Route { get; set; }

public ObservableCollection<ActivityViewModel> Activities { get; set; }

private GeoboundingBox viewArea = GeoboundingBox.TryCompute(new[] 
  { new BasicGeoposition { Latitude = -90, Longitude = -90 }, 
    new BasicGeoposition { Latitude = 90, Longitude = 90 } });

public GeoboundingBox ViewArea
{
  get { return viewArea; }
  set
  {
    if (viewArea != value)
    {
      viewArea = value;
      RaisePropertyChanged(() => ViewArea);
    }
  }
}

private ActivityViewModel selectedItem;
public ActivityViewModel SelectedItem
{
  get { return selectedItem; }
  set
  {
    if (selectedItem != value)
    {
      selectedItem = value;
      RaisePropertyChanged(() => SelectedItem);
    }
  }
}

Notice the LoadCommand: not only it loads the activities into a view model, but it also makes a new RouteViewModel (for drawing one line between all the points) and a bounding box to make all the points fit into the view. The RouteViewModel itself is a very simple thing that creates a GeoPath from all the points of all activities:

using System.Collections.Generic;
using System.Linq;
using Windows.Devices.Geolocation;
using GalaSoft.MvvmLight;

namespace WhatWhenWhere.ViewModels
{
  public class RouteViewModel : ViewModelBase
  {
    public RouteViewModel()
    {
    }

    public RouteViewModel(IEnumerable activities)
    {
      Path = new Geopath(activities.Select(p => p.Location.Positions.First()));

    }

    private Geopath geoPath;
    public Geopath Path
    {
      get { return geoPath; }
      set
      {
        if (geoPath != value)
        {
          geoPath = value;
          RaisePropertyChanged(() => Path);
        }
      }
    }
  }
}

And a wee bit of XAML to glue it all together

Being a lazy ****** and not wanting to think of something to draw all the stuff on a map, I reused both my Map Drawing behavior as the trick to show a popup, and came out with pretty little XAML indeed:

<Page.BottomAppBar>
  <CommandBar>
    <AppBarButton Icon="Accept" Label="Load" Command="{Binding LoadCommand, Mode=OneWay}"/>
  </CommandBar>
</Page.BottomAppBar>

<!-- stuff snipped -->

<Grid Grid.Row="1" x:Name="ContentRoot" Margin="19,9.5,19,0">
  <interactivity:Interaction.Behaviors>
    <behaviors:SizeListenerBehavior x:Name="ContentRootListener"/>
  </interactivity:Interaction.Behaviors>
  <Button Content="Button" HorizontalAlignment="Left" Margin="246,208,0,0" 
        VerticalAlignment="Top" Command="{Binding LoadCommand}"/>

  <Maps:MapControl maps:MapBindingHelpers.MapViewArea="{Binding ViewArea}">
    <interactivity:Interaction.Behaviors>
      <maps:MapShapeDrawBehavior LayerName="Locations" 
        ItemsSource="{Binding Activities}" 
        PathPropertyName="Location">
        <maps:MapShapeDrawBehavior.EventToCommandMappers>
          <maps:EventToCommandMapper EventName="MapTapped" 
            CommandName="SelectCommand"/>
        </maps:MapShapeDrawBehavior.EventToCommandMappers>
        <maps:MapShapeDrawBehavior.ShapeDrawer>
          <maps1:MapActivityDrawer/>
        </maps:MapShapeDrawBehavior.ShapeDrawer>
      </maps:MapShapeDrawBehavior>
        
      <maps:MapShapeDrawBehavior LayerName="Route" 
        ItemsSource="{Binding Route}" 
        PathPropertyName="Path">
        <maps:MapShapeDrawBehavior.ShapeDrawer>
          <maps:MapPolylineDrawer Color="Green" Width="3" 
                     StrokeDashed="True"/>
        </maps:MapShapeDrawBehavior.ShapeDrawer>
      </maps:MapShapeDrawBehavior>    
     </interactivity:Interaction.Behaviors>
  </Maps:MapControl>
    <Grid DataContext="{Binding SelectedItem}" 
        Height="{Binding WatchedObjectHeight, ElementName=ContentRootListener, 
           Converter={StaticResource PercentageConverter}, ConverterParameter=30}" 
           VerticalAlignment="Bottom" Background="Black" >
      <interactivity:Interaction.Behaviors>
        <behaviors:UnfoldBehavior RenderTransformY="1"  Direction="Vertical"  
        Activated="{Binding Converter={StaticResource NullToBooleanConverter}}"/>
      </interactivity:Interaction.Behaviors>
      <userControls:RouteItemPopup >
    </userControls:RouteItemPopup>
    </Grid>
  </Grid>
</Grid>

If you want more info on how these behaviors for binding elements to a MapControl work, I suggest you look at the original article explaining how they should be used. If a user taps on a map symbol, a popup with activity data is shown, and the address where that happened is loaded. Basically the same trick as I used here

Start it up

It’s important to initialize the Dispatcher helper, start new MainViewModel, and setup the WindowVisibilityMessage. I have talked about this before, but I thought it wise to repeat it one more time.

protected async override void OnLaunched(LaunchActivatedEventArgs e)
{
    DispatcherHelper.Initialize();
    MainViewModel.CreateNew();
    await MainViewModel.Instance.Start();
    WindowVisibilityMessage.Setup();
// more
Notice the OnLaunched needs to be async, because of the async nature of the Start method.

…and a class to draw a different image for every activity

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Devices.Geolocation;
using Windows.Foundation;
using Windows.Storage.Streams;
using Windows.UI.Xaml.Controls.Maps;
using Lumia.Sense;
using WhatWhenWhere.ViewModels;
using WpWinNl.Maps;

namespace WhatWhenWhere.Maps
{
  public class MapActivityDrawer : MapShapeDrawer
  {
    public override MapElement CreateShape(object viewModel, Geopath path)
    {
      var activityModel = ((ActivityViewModel) viewModel).Model;
      return new MapIcon
      {
        Location = new Geopoint(path.Positions.First()),
        Title = activityModel.Activity.ToString(),
        NormalizedAnchorPoint = new Point(0.5,1),
        Image = GetImage(activityModel.Activity)
      };
    }

    private static RandomAccessStreamReference GetImage(Activity activity)
    {
      var image = "Other";
      switch (activity)
      {
        case Activity.Idle:
        {
          image = "Idle";
          break;
        }
        case Activity.Walking:
        {
          image = "Walking";
          break;
        }

        case Activity.Moving:
        {
          image = "Moving";
          break;
        }

        case Activity.Stationary:
        {
          image = "Stationary";
          break;
        }
      }
      return RandomAccessStreamReference.CreateFromUri(
         new Uri(string.Concat("ms-appx:///Images/", image, ".png")));

    }
  }
}

Basically, translates every bound object back to ActivityViewModel, and draws a different image for it. This can be made more efficient, but this shows a way it could work.

Conclusion: the good and the bad parts

As you have seen, using SensorCore is actually pretty easy. Making it work together with MVVMLight as well, although I am very well aware that all the stuff I used around it may make it look a bit more convoluted that it actually is. The DataLoaderModel is all you have to understand. The rest is just the app around it.

The bad parts of SensorCore are pretty simple:

  • It’s still in beta
  • It requires a Windows Phone 8.1 phone with a Lumia Cyan update and the required hardware aboard. Currently, to the best of my knowledge, only the Lumia 630 has both the required hardware and software, although I assume the 930 will have it too.
  • The TrackPointMonitor only gives a position every 5 minutes – at the very best. Sometimes it misses points. So forget about a super-duper-detailed tracking of your location.

Another bad part – but that is more my bad – is that I have not included a way to run this on simulated data. A NuGet package allows you to test against fake or prerecorded data, so you can try your app without having access to an actual device have the sensors on board. I made this using a real live 630. To get this to work properly, you will need one too. The good part is: they are dirt cheap.

The good parts are a lot better:

  • Very simple and easy-to-use API.
  • Possibility to do geographical tracking and analysis after-the-fact
  • All kinds of fun apps possible, like fitness apps, life logging, and stuff like that
  • Very low power consumption (unlike ‘active’ GPS enabled apps)
  • Privacy is secured – no big brother watching over your shoulder. The data is yours and yours alone.

So. A new class of apps is enabled by this new SDK. I hope I have inspired you to have a spin with it. If you have read all the way to this, I am very impressed by your perseverance ;-)

Demo solution, as (nearly) always, can be found here.

Edit 26-06-2014: made some minor updates after suggestions from my Finland fellow MVP Jani Nevalainen.

Building multi-resolution popups in an Universal App

With the advent of Windows Phone 8.1 multiple resolution are now also becoming a fact of life for Windows Phone developers (as they already were for Windows Store developers). This has a few side effects, which will require you to think about the size of things that need to cover part of the screen. Particularly you can get into trouble with animations built with visual states, which tend to have fixed number in them. I already dabbled into this when I ported Two Phone Pong to Windows 8.1 and had to make a screen-size independent scroll-into-view pane.

Now, for a demo app I have been working on, I wanted to make a popup that always used 1/3th of the screen. That used to be easy, as we had only one resolution, more or less, and for the rest Windows Phone 8 did some auto scaling. Now it needs a little more work. Very little, as it turned out I almost had all the pieces in place:

  • I already had the UnfoldBehavior, which can be used to let UI elements appear and disappear by ‘folding’ them in or out.
  • I already had the SizeListenerBehavior, which allows me to data bind to the actual width and height of an element

The only thing that was missing was this very simple converter:

using System;
using System.Globalization;

namespace WpWinNl.Converters
{
  public class PercentageConverter : BaseValueConverter
  {
    public override object Convert(object value, Type targetType, 
      object parameter, CultureInfo culture)
    {
      double parmValue;
      if (value is double && double.TryParse(parameter.ToString(), 
                                            out parmValue))
      {
        value = (double)value * parmValue / 100;
      }
      return value;
    }

    public override object ConvertBack(object value, Type targetType, 
      object parameter, CultureInfo culture)
    {
      if (value is double && (parameter is double || parameter is int))
      {
        value = (double)value / 100 / (double)parameter;
      }
      return value;
    }
  }
}

Basically all it does is dividing the incoming value by 100 and multiply it with the parameter value, so if you supply “33” as ConverterParameter is will multiply the incoming value by 0.33, effectively dividing it by 3 (ish).

So now I can define a simple app with a map (for instance), and when the popup is displayed, it always takes the bottom third of the screen. Regardless of screen resolution.

On the 4” WVGA (800x480) emulator this looks like this:

imageimage

While on a 1080p 6” emulator it looks like this:

imageimage

Added bonus: the popup resizes automatically as you rotate the phone. It always takes the bottom third of the screen.

The way to achieve this is pretty simple:

<!-- some stuff snipped -->
<Grid>
  <interactivity:Interaction.Behaviors>
    <behaviors:SizeListenerBehavior x:Name="ContentRootListener"/>
  </interactivity:Interaction.Behaviors>
  <!-- some stuff snipped -->
  
  <Grid Grid.Row="1" Margin="19,9.5,19,0">
    <maps:MapControl>
    </maps:MapControl>
    <!-- the Grid below is the actual popup window -->
    <Grid Height="{Binding WatchedObjectHeight, 
      ElementName=ContentRootListener, Converter={StaticResource PercentageConverter}, 
      ConverterParameter=33}" VerticalAlignment="Bottom" Background="Blue" >
      <interactivity:Interaction.Behaviors>
        <behaviors:UnfoldBehavior RenderTransformY="1" Direction="Vertical" 
                                  Activated="{Binding IsOpen}"/>
      </interactivity:Interaction.Behaviors>
      <StackPanel>
        <TextBlock Text="This is my popup" FontSize="22"></TextBlock>
        <StackPanel Orientation="Horizontal">
          <TextBlock Text="It's height is:" FontSize="22"></TextBlock>
          <TextBlock Text="{Binding WatchedObjectHeight, 
            ElementName=ContentRootListener, Converter={StaticResource PercentageConverter}, 
            ConverterParameter=33}" 
            FontSize="22"></TextBlock>
        </StackPanel>
      </StackPanel>
    </Grid>
  </Grid>
</Grid>
  • The top grid has a SizeListenerBehavior. This enables me to bind to the ActualHeight and of the top grid as it changes by binding to WatchedObjectHeight of the SizeListenerBehavior.
  • The height of the popup is set like by actually binding to said value. By using the converter with a parameter of 33 it will – when activated – always use 1/3rd of the screen – or 1/3rd of whatever element your SizeListenerBehavior is sitting on.
  • By setting the UnfoldBehavior’s RenderTransformY to 1 and the Direction to Vertical the popup will open from the bottom up. This is not new, that was already in the original article about UnfoldBehavior.

I have included a small demo solution to show off this little trick. It uses my pre-release version of WpWinNl 2.0.x and a little MVVMLight, but you could use this just as easy from a non-MVVM app. But I still think the beauty of this all is that can be initiated from a viewmodel ;)

In addition, although the demo solution only contains a Windows Phone app, this trick works just as well as on Windows Store apps, as both UnfoldBehavior and SizeListenerBehavior are already in the new WpWinNl NuGet package.

24 June 2014

Messaging class for detecting and broadcasting that your Universal app is running in the background

Some time ago I discovered that you can trap Window.Current.VisibilityChanged in a Windows 8 app to check if the app was running in the background, and that you can use that moment to pause a game and/or save the state to storage. The fun thing is, this now works in Universal apps too, that is – and thus, also on Windows Phone 8.1.Although we can discuss if  the app is really running in the background or that is actually being suspended when it runs on Windows Phone – the mechanism is the same and it can be used to the same end.

So make this more easy to use, I made this very simple class, based upon the MVVMLight MessageBase class:

using Windows.UI.Xaml;
using GalaSoft.MvvmLight.Messaging;

namespace WpWinNl.Messages
{
  public class WindowVisibilityMessage : MessageBase
  {
    public WindowVisibilityMessage(bool visible, object sender = null, 
object target = null) :base(sender, target) { Visible = visible; } public bool Visible { get; protected set; } public static void Setup() { Window.Current.VisibilityChanged += (s, f) => Messenger.Default.Send(new WindowVisibilityMessage(f.Visible)); } } }

and all you have to use it, is to simply put

protected override void OnLaunched(LaunchActivatedEventArgs e)
{
   WindowVisibilityMessage.Setup();
   //Rest of code omitted
 

in the first line of OnLaunched in the App.Xaml.cs

Now anywhere in your app you can now listen for the WindowVisibilityMessage to come by and act on it, like this:

Messenger.Default.Register<WindowVisibilityMessage>(this, p =>
{
  if (p.Visible)
  {
    //do something 
  }
  else
  {
    //do something else
  }
});

And that’s all. The source of this class is already on codeplex and it’s also in the full WpWinNl NuGet package that I released yesterday. Since this class and it’s use is prettysimple and straightforward, I am going to skip a sample solution. A more comprehensive sample where this class plays a minor supporting role is coming soon

22 June 2014

Introducing WpWinl 2.0.0 (prerelease version)

Today I have decided to take the plunge – I have released an early version of WpWinNl 2.0.0 to NuGet. This introduces quite some changes.

To start with, WpWinNl now consists of two packages:

  • WpWinNlBasic, which contains all the stuff that is not dependent on MVVMLight. This includes a lot of behaviors, converters and utilities, including a lot of animation stuff.
  • WpWinNl proper, which is dependent on WpWinNlBasic and does pull in MVVMLight.

So if you just want to continue using WpWinNl like you used to do, nothing much is going to happen apart from the fact that you pull in two NuGet packages in stead of one. But if you for some odd reason does not like MVVMLight, or don’t use it can now use all my stuff that does not rely on MVVMLight by pulling in WpWinNLBasic only. I have received several request to that end (most vocally by Dutch DPE Rajen Kishna), so there it is.

If you, by any chance, use WpWinNl on Universal apps or Windows app, you are pulling in a profound change under the hood. For those type of apps, all the code I have written so far is sitting inside a PCL, including the behaviors. To that end, I use my previously released version of IBehaviorPortable, that uses a trick to make it possible to write cross-platform behavior (as described here).

This is a pre-release version as I said, for the simple reason that there are a few known issues:

  1. I am not entirely sure of the Bluetooth stuff. Although phone to phone seems to work, phone to PC does not, at least not under Windows Phone 8.1. I still have to get to the bottom of this
  2. Not all behaviors and utilities made it from Windows Phone 8.0 to 8.1. This includes the behaviors to check for updates, reminding the user to review, and some stuff that Timmy Kokke contributed some 1.5 years ago.

As a bonus, I also released a third package, that allows you to bind polygons, lines, and the new MapIcons to the new Windows Phone 8.1 here maps control. You can find this package by looking for WpWinNlMaps, and it basically is an update to the principle I describe here for Windows Phone 8.0. No sample code yet folks – been a bit busy lately.

I am pretty sure this all works pretty well (barring the mentioned issues), but being a engineer, I don’t say it’s done until it’s really done. So, therefore – prerelease. Terms and conditions may apply. Your mileage may vary. And stuff like that.

If you discover any issues, run into trouble, or have suggestions to improve things - please feel free contact me.

09 June 2014

Using Fody.PropertyChanged for automatic PropertyChangedEvents and model-to-viewmodel communication

When you are working with MVVM, you are faced with a couple of interesting challenges. Usually you have something like this:

You see a lot of properties with a lot of repetitive code. Of course, you can use snippets to make that easier, but still. It looks cluttered.

using GalaSoft.MvvmLight;

namespace MyApp.Logic.ViewModels
{
  public class DummyViewModel : ViewModelBase
  {
    private string myProperty;
    public string MyProperty
    {
      get { return myProperty; }
      set
      {
        if (myProperty != value)
        {
          myProperty = value;
          RaisePropertyChanged(() => MyProperty);
        }
      }
    }

    private string yourProperty;
    public string YourProperty
    {
      get { return yourProperty; }
      set
      {
        if (yourProperty != value)
        {
          yourProperty = value;
          RaisePropertyChanged(() => YourProperty);
        }
      }
    }

    public void DoSomething()
    {}
  }
}

And it usually gets more cluttered, as people tend to put what amounts to business logic into the viewmodel. I used to make a viewmodel as a wrapper around a model, like this.

namespace MyApp.Logic.Models
{
  public class DummyModel
  {
    public string YourProperty { get; set; }
    public string MyProperty { get; set; }

    public void DoSomeBusinessThing()
    { }
  }
}
and then something like this, also created via snippets
using GalaSoft.MvvmLight;
using MyApp.Logic.Models

namespace MyApp.Logic.ViewModels
{
  public class DummyViewModel : ViewModelBase
  {
    public DummyViewModel()
    {
    }

    public DummyViewModel(DummyModel model)
    {
      Model = model;
    }
    public DummyModel Model { get; set; }

    public string MyProperty
    {
      get { return Model.MyProperty; }
      set
      {
        if (Model.MyProperty != value)
        {
          Model.MyProperty = value;
          RaisePropertyChanged(() => MyProperty);
        }
      }
    }

    public string YourProperty
    {
      get { return Model.YourProperty; }
      set
      {
        if (Model.YourProperty != value)
        {
          Model.YourProperty = value;
          RaisePropertyChanged(() => YourProperty);
        }
      }
    }
  }
}
Nice. But now you have a different problem. I you call DoSomeBusinessThing and that affects "MyProperty" or "YourProperty" on the model in some way, how is the viewmodel supposed to know and fire a PropertyChanged?

Enter Fody - a great toolkit. It is basically an extensible toolkit for “weaving” .NET assemblies. It accepts plugins to decide what is actually injected. The most useful for our problem is the PropertyChanged.Fody.Which is also available as a Nuget package that you can easily add to your project. You then take your good old DummyObject and add the attribute ImplementPropertyChanged to it.

using PropertyChanged;

namespace Travalyzer.Logic.ViewModels
{
  [ImplementPropertyChanged]
  public class DummyModel
  {
    public string YourProperty { get; set; }
    public string MyProperty { get; set; }

    public void DoSomeBusinessThing()
    { }
  }
}

and boom. If something affects a property, the property will fire PropertyChanged. PropertyChanged.Fody will add all kinds of smart logic to your model, as described here. Ain't that cool, you don't even have to make a viewmodel anymore.

Well, sorry to rain on your parade, but if you now start to add commands to your enhanced model and properties or methods that are only used for display (for instance, a formatted date) you are basically polluting your business layer. It’s like adding business logic to your viewmodel, but coming from the opposite direction. Yes, you can now bind directly to model properties and they will fire a PropertyChanged, but business logic is business logic and thou shalt not pollute that with methods indented to deliver data for display and UI interaction. That is what viewmodels are for. And your viewmodel may still need to do something so it still may need to be notified. After some puzzling I came up with the following. First, I define a base class for my models with minimal baggage:

using System.ComponentModel;
using GalaSoft.MvvmLight.Threading;
using PropertyChanged;

namespace WpWinNl.MvvmLight
{
  [ImplementPropertyChanged]
  public class BaseNotifyingModel : INotifyPropertyChanged
  {
    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged(string propertyName = null)
    {
      if (PropertyChanged != null)
      {
        DispatcherHelper.CheckBeginInvokeOnUI(() => 
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName)));
      }
    }
  }
}

I don’t even use MVVMLight’s ViewmodelBase, but a very simple INotifyPropertyChanged implementation. PropertyChanged.Fody is apparently smart enough to leave that part of the class alone if it’s already implemented. But of course I do use other smart things by Laurent Bugnion – the DispatcherHelper. Mind you, this requires it being initialized in App.Xaml.cs. The most important thing is that the PropertyChanged event can be used by the viewmodel to track possible changes in the model. I use this in the following base view model, which is used on MVVMLight ViewModelBase:

using System.ComponentModel;
using GalaSoft.MvvmLight;
using PropertyChanged;

namespace WpWinNl.MvvmLight
{
  [ImplementPropertyChanged]
  public class TypedViewModelBase<T> : ViewModelBase where T : BaseNotifyingModel 
  {
    private T model;

    public TypedViewModelBase()
    {
    }

    public TypedViewModelBase(T model)
    {
      Model = model;
    }

    public T Model
    {
      get { return model; }
      set
      {
        if (value != model)
        {
          if (model != null)
          {
            model.PropertyChanged -= ModelPropertyChanged;
          }
          model = value;
          if (model != null)
          {
            model.PropertyChanged += ModelPropertyChanged;
          }
        }
      }
    }

    protected virtual void ModelPropertyChanged(object sender, 
       PropertyChangedEventArgs e)
    {
    }
  }
}

imageIf you derive your models from BaseNotifyingModel and your viewmodels from TypedViewModelBase you will have a model that automatically fires INotifyPropertyChanged on any property that is changed – so it can be used in data binding – and you will have viewmodels that will automatically be notified of any change in their model – and by overriding ModelPropertyChanged you can act on that.

Now of course this is all very nice and very architecty, but how would this ever be useful? Suppose you have a list of business object containing a location. When the user selects it, you want to display it’s street address. You can get that using the MapLocationFinder API. If you have a few 100 points, and you want to get all the addresses – this can take quite some time. So you want to do this on-demand, as displayed in the Windows Phone demo solution.

 

So I created this model:

using System;
using System.Linq;
using Windows.Devices.Geolocation;
using Windows.Services.Maps;
using WpWinNl.MvvmLight;

namespace FodyDemo.Models
{
  public class LocationData : BaseNotifyingModel
  {
    public LocationData()
    {
      TimeStamp = DateTime.Now;
    }

    public int Id { get; set; }

    public bool HasLocation { get; set; }

    public Geopoint Position { get; set; }

    public DateTimeOffset TimeStamp { get; set; }

    private MapLocation locationInfo;

    public MapLocation LocationInfo
    {
      get
      {
        if (locationInfo == null)
        {
          lock (this)
          {
            MapLocationFinder.FindLocationsAtAsync(Position).
AsTask().ContinueWith(p => { var firstLocationData = p.Result.Locations; if (firstLocationData != null) { LocationInfo = firstLocationData.FirstOrDefault(); HasLocation = true; } }); } } return locationInfo; } set { locationInfo = value; } } } }

Basically, only if the LocationInfo property is accessed it is actually retrieved. In real life, I would probably implement this using a “LoadLocation” method or something, but whatever – this works as well. If the location is found, not only the LocationInfo is set, but also the HasLocation property, which makes the checkbox light up. In the list, the following properties are bound like this: 

<TextBlock Text="{Binding Model.Id}" FontSize="15"
VerticalAlignment="Center"></TextBlock> <TextBlock Text="{Binding DateAndTime}" Grid.Column="1" FontSize="15"
VerticalAlignment="Center"></TextBlock> <CheckBox IsChecked="{Binding Model.HasLocation}" IsEnabled="False"
Grid.Column="2" ></CheckBox

You can see HasLocation is bound directly to the Model, still it gets a PropertyChanged event fired courtesy of Fody. The DataAndTime property apparently coming from the ViewModel, but Location Property is left alone. for now. The panel at the bottom, where the selected object is displayed, is like this

<TextBlock Text="{Binding Model.Id}" FontSize="15" ></TextBlock>
<TextBlock Text="{Binding DateAndTime}" Grid.Row="1" FontSize="15"></TextBlock>
<TextBlock Text="{Binding LocationName}" Grid.Row="2"
FontSize="15"></TextBlock>

In the the viewmodel it looks like this:

using System.ComponentModel;
using System.Globalization;
using System.Windows.Input;
using Windows.Services.Maps;
using FodyDemo.Messages;
using FodyDemo.Models;
using GalaSoft.MvvmLight.Command;
using GalaSoft.MvvmLight.Messaging;
using WpWinNl.MvvmLight;

namespace FodyDemo.ViewModels
{
  public class LocationViewModel : TypedViewModelBase<LocationData>
  {

    public LocationViewModel()
    {
    }

    public LocationViewModel(LocationData ld) : base(ld)
    {
    }

    public string DateAndTime
    {
      get
      {
        return Model != null ? 
          Model.TimeStamp.ToString("dd-MM-yyyy hh:mm:ss", 
            CultureInfo.InvariantCulture) : string.Empty;
      }
    }

    public string LocationName
    {
      get {
        return Model != null && Model.LocationInfo != null ? 
        GetFormattedAdress(Model.LocationInfo.Address) : string.Empty; }
    }

    private string GetFormattedAdress(MapAddress a)
    {
      return string.Format("{0} {1} {2} {3}", a.Street, a.StreetNumber,
a.Town, a.Country); } public ICommand SelectCommand { get { return new RelayCommand( () => Messenger.Default.Send(new SelectedObjectMessage(this))); } } protected override void ModelPropertyChanged(object sender,
PropertyChangedEventArgs e) { if (e.PropertyName == "LocationInfo") { RaisePropertyChanged(() => LocationName); } } } }

You can see the LocatioName property shows a neatly formatted address – and gets a manual RaisePropertyChanged when the ModelPropertyChanged is fired from the model (once again courtesy of Fody) – it checks if that is indeed the “LocationInfo” property and boom – the UI knows it must update because a property from the model is changed, even tough that property is not directly bound to the property.

Thus you can use the viewmodel as it is intended – a place to bind model and view together, a converter on steroids as Josh Smith put is so aptly as early as 2008. A simple base class will add some minimal extra functionality to your business class to make it play within MVVM – but it will still keep it clean.

Important detail – if you use the MVVMLight DispatcherHelper, you must initialize it first. I usually do this in the App.xaml.cs.

The demo solution, which contains some more code, can be found here. Credits go to the equally smart as sarcastic-in-a-fun-way Scott Lovegrove, who first put me on track with Fody quite some time ago, but only recently I was smart enough to actually take his advice and try it out. My base class for models was based on this Fody sample – but of course I thought I was smarter that that and opted for events rather than MVVMLight messages, it being a more lightweight solution. I like to reserve the Messenger for inter-viewmodel communication.

If you did indeed read all the way through here – congratulations. This is a very architect’s article, not some fun thing to make a cool new user control. But once in a while I start pondering about things like this, to keep my code cleaner, and make things better. If I haven’t bored you to death with it, you might be one of the very rare species that has some architectural genes hidden in your DNA too. I like to think I have, although I am more of a micro-architect, not one that only draws enormous complex diagrams for a living ;)

05 June 2014

Fix for being unable to rename files after applying Visual Studio 2013 update 2

A couple of people – me included – have been suffering from the fact that, after they applied Visual Studio 2013 update 2, it became impossible to rename files from Visual Studio. This annoying problem was occurring only on my Surface Pro, and not my Core i7 desktop. The difference was that the Surface Pro at one point ran the public RC of VS2013.2, and the desktop did not. Even totally deleting VS2013 and reinstalling it did not work

Today I got the golden tip from a fellow named Chung Bill (unfortunately I know only his name and e-mail address): apparently, if you did follow the upgrade path from public RC to RTM and you have the Multilingual Application Toolkit (MAT) installed, something goes wrong. The solution is also very simple: uninstall the MAT, then reinstall it again. If you then fire up Visual Studio 2013 again, renaming files works again. Note that you have to uninstall it from Windows, using Programs and Features, not from inside Visual Studio.

Somewhere in the intricate woven components of the MAT and VS2013.2 something apparently went wrong. But fixing it ain't rocket science. No harm done. Nothing broke permanently ;-).