27 December 2015

Gotcha–navigating from SplitView requires back button to be pressed twice in UWP apps

Yesterday, while being in the process of converting my one of my apps to UWP, I was moving a lot of the functionality of the app menu to a SplitView. Microsoft have really gone out on a limb as far as samples are concerned, so I was following the SystemBack sample, which has really little code in it. How hard can it be, right?
All  went rosey, until I discovered that altough the back button showed up nicely on my secondary page, the actual navigation back only happenend after I had pressed the back button twice. Some debugging learned me the global event
SystemNavigationManager.GetForCurrentView().BackRequested
does not even get called the first time. I spent a not so enjoyable hour deconstructing my app, then finally building a bare bones repro – and there it happened too. One of those moment where you seriously start to doubt your own mental health. The code is not very complicated. I have a MainPage.xaml with a button on a SplitView, and when you click that, it navigates to the next page
private void Navigation_Click(object sender, RoutedEventArgs e)
{
  Frame rootFrame = Window.Current.Content as Frame;
  rootFrame.Navigate(typeof(Page2));
}
And on that Page2 a rather piece of standard code
protected override void OnNavigatedTo(NavigationEventArgs e)
{
  base.OnNavigatedTo(e);
  var rootFrame = Window.Current.Content as Frame;
  if (rootFrame != null)
  {
    SystemNavigationManager.GetForCurrentView().AppViewBackButtonVisibility =
      rootFrame.CanGoBack
        ? AppViewBackButtonVisibility.Visible
   : AppViewBackButtonVisibility.Collapsed;
  }
}
And in the app.xaml.cs, also rather standard (and ugly, now that I look at it), nicked from the sample
private void App_BackRequested(object sender, BackRequestedEventArgs e)
{
  Frame rootFrame = Window.Current.Content as Frame;
  if (rootFrame == null)
    return;

  if (rootFrame.CanGoBack && e.Handled == false)
  {
    e.Handled = true;
    rootFrame.GoBack();
  }
}
And yet, this method gets only called on the second back button press. The solution? Read back a little.

“I have a MainPage.xaml with a button on a SplitView, and when you click that, it navigates to the next page”

It's one of those mental leaps you sometimes have to make as a developer. It turns out that the exact method Navigation_Click works flawlessly when the button calling the method is not on a SplitView. Somehow, some way, having a SplitView open messes up the navigation back stack.
The solution, once you know that, is very simple of course:
private void Navigation_Click(object sender, RoutedEventArgs e)
{
  rootSplitView.IsPaneOpen = false;
  Frame rootFrame = Window.Current.Content as Frame;
  rootFrame.Navigate(typeof(Page2));
}
Go back to the click event handler, and add a line that closes the SplitView before initiating the navigation. It’s the little things like this that makes developer life so interesting. ;)

Code used in this post can be found here.

Special thanks to Scott Lovegrove and the other people from the Open Live Writer team for making this post possible – it’s the first one using Open Live Writer that now supports blogger – right after Google shut down the outdated web api endpoint that Live Writer still used. Thank you all folks!

12 December 2015

UWP Map data binding with WpWinNlMaps explained

Intro

Recently I released WpWinlMaps for the Universal Windows Platform, a NuGet package that allows you to data bind map shapes to the awesome new map control for Windows 10. This map control got recently even more awesome with SDK 10586, when multipolygons, aka polygons with holes, aka donuts where added to it. For those who have read this blog before, this binding code should not come as a surprise - I basically did this already in 2012 for the Bing Maps control for Windows, and there are incarnations of this for Windows Phone 8 and the Here Maps control for Windows Phone 8.1. The UWP binding - of course built as a behavior - is an evolution of the Windows Phone 8.1 behavior. It's most important new features are:
  • It's built on top of the new UWP Behaviors NuGet Package
  • MapShapeDrawBehavior can now also draw multi polygons (with holes)
  • The geometry type used to support Geopath only (even if you wanted to draw just a MapIcon). Now you can use a BasicGeoposition for MapIcon, a Geopath for MapPolyline or a 'normal' MapPolygon, and an IList<Geopath> to create the new type of polygons-with-holes that I mentioned earlier.
  • MapShapeDrawBehavior supports the new MapElementClick event for selecting objects on the map (and still supports the old MapTapped event, as well as Tapped, although the last one is still not recommended for use)
  • The EventToCommandMapper is renamed to EventToHandlerMapper; now it can not only call a command, but also directly a method of the view model. This is to align with the way x:Bind introduces calling direct events as well.
  • Speaking of - x:Bind to the MapShapeDrawBehavior's ItemSource is fully supported, although that's 99% thanks to the platform and 1% to my coding.

Getting started

Create a project, add the WpWinNl NuGet package to it. This will pull in the WpWinNlBasic package as well, as well as - of course Microsoft.Xaml.Behaviors.Uwp.Managed, and Rx-Linq because I use that to dynamically react on events.

Then, of course, you will need some MVVM framework, be it something that you make yourself or something that is made by someone else. In my sample I opted for pulling in MVVMLight, this being more or less an industry standard now. I also pulled in WpWinNl full, because I use some more features from it in my sample code. And that automatically pulls in MVVMLight too, so that saves you the trouble of doing that yourself ;)

Concepts

These are basically still the same, but I will repeat them here for your convenience.
Typically, maps are divided into layers. You can think of this as logical units representing one class of real-world objects (or ‘features’ as they tend to be called in the geospatial word). For instance, “houses”, “gas stations”, “roads”. In WpWinNlMaps, a layer translates to one behavior attached to the map.

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

Sample

As always, a sample says more than a 1000 words. Our view model has a property
MultiPolygons = new ObservableCollection<MultiPathList>();
And a MultiPathList indeed as a
public List<Geopath> Paths { get; set; }
Drawing a set of polygons with holes in it, is as easy as
<maps:MapControl x:Name="MyMap" Grid.Row="0">
  <interactivity:Interaction.Behaviors>
    <mapbinding:MapShapeDrawBehavior LayerName="MultiShapes" 
        ItemsSource="{x:Bind ViewModel.MultiPolygons,
          Converter={StaticResource MapObjectsListConverter}}" 
        PathPropertyName="Paths">
      <mapbinding:MapShapeDrawBehavior.EventToHandlerMappers>
        <mapbinding:EventToHandlerMapper EventName="MapElementClick" 
                                         MethodName="Select"/>
      </mapbinding:MapShapeDrawBehavior.EventToHandlerMappers>
      <mapbinding:MapShapeDrawBehavior.ShapeDrawer>
        <mapbinding:MapMultiPolygonDrawer 
          Color="OrangeRed" StrokeColor="Crimson" 
          Width="2" StrokeDashed="True"/>
      </mapbinding:MapShapeDrawBehavior.ShapeDrawer>
    </mapbinding:MapShapeDrawBehavior>
  </interactivity:Interaction.Behaviors>
</maps:MapControl>
So what we have here is a MapShapeDrawBehavior that binds to ViewModel.MultiPolygon, using a converter. Unfortunately, due to the nature of x:Bind, you will always need to use this converter. If you don't, you will run into this error: "XamlCompiler error WMC1110: Invalid binding path 'ViewModel.MultiPolygons' : Cannot bind type 'System.Collections.ObjectModel.ObservableCollection(WpWinNl.MapBindingDemo.Models.MultiPathList)' to 'System.Collections.Generic.IEnumerable(System.Object)' without a converter". So I give it a converter to make it happy, although the convert method of the MapObjectsListConverter in fact only is this
public override object Convert(object value, Type targetType, 
                               object parameter, CultureInfo culture)
{
  return value;
}
If you have been working as a career developer for 23 you learn it's best just not get wound up about these kinds of things and just happily accept a feasible work-around :)

Event handling

Next up is the EventToHandlerMapper; in its EventName you can put the following event names:
  • MapElementClick
  • MapTapped
  • Tapped
And I recommend you use MapElementClick as that provides the least overhead and is the preferred new event. The other two will work too. Any other events, how valid they might be, are ignored.
The EventToHandlerMapper has two other properties: MethodName and CommandName. The first one is checked first, so if you are a smartypants who defines them both, only MethodName is used. Once again - this is a method or a command on the bound object, not the view model that hosts the ItemSource. The method or command should take a MapSelectionParameters object as a parameter. In the sample code you will see a class GeometryProvider that actually implements both, utilizing standard MVVMLight code:
public class GeometryProvider : ViewModelBase
{
  public string Name { get; set; }

  public ICommand SelectCommand => new RelayCommand<MapSelectionParameters>(Select);

  public void Select(MapSelectionParameters parameters)
  {
    DispatcherHelper.CheckBeginInvokeOnUI(
      () => Messenger.Default.Send(
         new MessageDialogMessage(Name, "Selected object", "Ok", "Cancel")));
  }
}
I use this as a base class for all types that I bind to the MapShapeDrawBehavior to provide an easy base for event handling.

Shape drawers

These are classes that for actually converting the geometry into an actual shape, that is, a MapIcon, a MapPolyline, or a MapPolygon. Out of the box, there are four drawers with the following properties:
  • MapIconDrawer
  • MapPolylineDrawer
    • Color - line color
    • StrokeDashed - dashed or solid line
    • Width - line width
  • MapPolygonDrawer
    • Color - shape fill color
    • StrokeDashed - dashed or solid shape outline
    • StrokeColor - shape outline color
    • Width - shape outline width
  • MapPolylineDrawer
    • Same as MapPolygonDrawer
In addition, all drawers support a Z-index property.

Thematic maps - making your own shape drawers

I wish to stress that is does not end with the four default drawers. If you want map elements to change color or other properties based upon values in object that you bind to - there is nothing that keeps you from doing that. You can do this by making by sub classing an existing drawer (or make a completely new one). Suppose you have this business object:
public class CustomObject
{
  public string Name { get; set; }
  public BasicGeoposition Point { get; set; }

  public int SomeValue { get; set; }
}
And you want to have the color of the line to change based on the SomeValue property, you can achieve this by writing something like this:
public class MyLineDrawer : MapPolylineDrawer
{
  public override MapElement CreateShape(object viewModel, Geopath path)
  {
    var shape = (MapPolyline)base.CreateShape(viewModel, path);
    var myObject = (CustomObject)viewModel;
    switch (myObject.SomeValue)
    {
      case 0:
        {
          shape.StrokeColor = Colors.Black;
          break;
        }
      case 1:
        {
          shape.StrokeColor = Colors.Red;
          break;
        }

      //etc
    }
    return shape;
  }
}

Drawer class hierarchy

The class drawers are built according to the following class hierarchy
I'd recommend overriding only the concrete classes when creating custom drawers. Be aware there are three virtual methods in MapShapeDrawer that you can override:
public abstract class MapShapeDrawer
{
  public virtual MapElement CreateShape(object viewModel, BasicGeoposition postion)
  {
    return null;
  }

  public virtual MapElement CreateShape(object viewModel, Geopath path)
  {
    return null;
  }

  public virtual MapElement CreateShape(object viewModel, IList<Geopath> paths)
  {
    return null;
  }

  public int ZIndex { get; set; }
}
Make sure you override the right method for the right goal:
  • CreateShape(object viewModel, BasicGeoposition postion) when you are dealing with icons
  • CreateShape(object viewModel, Geopath path) when you are dealing with lines or polygons
  • CreateShape(object viewModel, IList<Geopath> paths) when are are dealing with multipolygons

Limitations

Be aware this binding method respond to changes in the list of bound objects - that is, if you add or remove an object to or from the bound list, it will be drawn of the map or removed from it. If you change properties within the individual objects after binding and drawing, for instance the color, those will not reflect on the map - you will have to replace the object in the list.

Sample solutions

This article comes not with one but two samples - it's amazing Mike! ;). The first one is actually in the code on GitHub and you can find it here. The drawback of that sample it that it actually requires you to compile the whole library as it uses the sources directly - it was my own test code. So for your convenience I made more or less the same solution, but then using the NuGet packages. You can find that here - it's an old skool downloadable ZIP file as I don't want to confuse people on GitHub. Both solutions work the same and show the same data as in an earlier post where I described the multipolygon feature first.

imageA little word of guidance – after your start either demo app, first go to the ellipses on the right, tap those and hit “show area”. That will bring you to the right location to show all the features that this app can draw.

Conclusion

I hope that this little library is as useful as its earlier incarnations proved to be. I have actually utilized the wp8.1 version in one of my own apps, I know there is a good chance of more apps are using it, and also that some Microsofties are actually recommending it ;). I hope it's useful for you. Let me know what you think about this. In the mean time, happy mapping!

09 December 2015

Release of WpWinNl 3.0.2 alpha with UWP support - and map data binding

I just released my open source library WpWinNl to 3.0.2-alpha (that is, pre-release) to NuGet. WpWinNl now supports Windows Phone 8, Windows Phone 8.1, Windows 8.1 and the new Universal Windows Platform. All the basic libraries are updated to the latest version.
The library builds three NuGet packages, which can be found on NuGet as of now:
  1. WpWinNlBasic, which contains everything except the stuff that hangs on MVVMLight
  2. WpWinNl, builds on top of WpWinNlBasic and MVVMLight (and has dependencies on both)
  3. WpWinNlMaps, that builds on top of WpWinNlBasic. This is code that allows you to bind data to maps utilizing a special behavior
For Windows Phone 8, Windows Phone 8.1 and Windows 8.1 basically nothing changes. With respect to UWP, the following changes have been made:
  1. The behaviors are built on top of the new UWP Behaviors NuGet Package
  2. WpWinNlMaps has a slightly expanded API to support the new features by the new maps control, as well align more with the x:Bind scenario
There is a sample application for WpWinNlMaps in GitHub that I will explain more in detail in a follow-up post. Make sure that if you have a look at the code, take the UWP branch.
A few words of warning:
  • Apart from WpWinNlMaps, this has not yet been extensively tested, hence the pre-release designation.
  • No attempt has been made to share code between UWP and the earlier incarnations of these packages. Effectively I have ceased working on wp8/wp81/win81 code, as of now only the UWP code will move forward and at one point in time these packages will stop supporting them at all.
  • Parts of this library, especially the behaviors, may be removed entirely and end up in the Microsoft Behaviors NuGet package.
However, for the time being, I think especially the map binding is pretty important, so is was time to release it. Any feedback is welcome.

06 December 2015

Fixing "Payload file rd.xml does not exist" when creating a Windows 10 UWP NuGet Package

Just as I thought I finally was done adding Universal Windows Platform support for WpWinNl, I ran into a weird error. I had updated the nuspec file after some guidance from my fellow MVP Dave Smits, created the NuGet package itself, but when I wanted to use it, I ran into an error that looked like this:
Payload file 'C:\Users\joost_000\.nuget\packages\WpWinNlBasic\3.0.0-alpha\lib\uap10.0\WpWinNl.External\Properties\WpWinNl.External.rd.xml' does not exist.
Another fellow MVP, Scott Lovegrove, gave me the first pointer in this article. Apparently, even if you don't have any special directive in the rd.xml file, it still needs to be there. Scott explains how you need to put this file in a subfolder of where the actual assemblies of your package reside. This works, but it takes (as I have found out) quite some hassle making a script that gets the right directory structures. Typos and mistakes are easily made. Or it may be just that I am a bit thick. Anyway, another well-known community hero developer (who I think should really be made MVP at the first possible occasion), Pedro Lamas, actually gave me a way easier way to fix this:
just change the "Build Action"  of the missing rd.xml a Build Action to "embedded resource"
image
Ironically, Pedro gave me this pointer when he made a pull request to the newly open sourced Behaviors library for Windows 10 UWP apps that I help to manage. Here rd.xml files were pulled into the package by file - kind of like I was doing it first, in stead of using the embedded resource way. Which goes to show that this knowledge isn't very common and apparently not even clear to developers within Microsoft - and this is why I decided to write this little post to make the solution to this problem easier to find.
In his pull request Pedro points to this article on the .NET blog from May 2014 where there is actually something written about including the rd.xml file as a resource, but it 's like an aside, with no actual indication the file actually has to be there in a NuGet package, whether you are actually using it or not.
As to the actual functions of the RD.xml file, that's in the .NET blog article. Be sure to read that when you are planning to use reflection. I hope Pedro will indeed find some time to write some more clarification on this, as he seems to be planning to do.
Update - Pedro tweeted in a response to this article that you apparently can also delete the whole rd.xml file from your project, and then it will work as well. So that makes three possible ways to solve the error.

03 December 2015

Drawing polygons with holes (donuts) on Windows 10 UWP apps

imageNovember 30th was the day a long standing wish of me came true - Windows 10 Universal Windows Platform gets on par with some professional geographical information systems. As of SDK version 10586, you can draw polygons with holes in them on the map (we GIS people call those 'donuts' - remember this term if you want to sound like a smartass educated in the GIS area) as well as multipolygons (single map objects consisting out of multiple disjoint shapes. What I mean by this, can be seen on the right. On this map are actually only two shapes. The 'smiley' on the bottom is one shape, and rest is actually also one shape.
To achieve this, the maps team have extended the MapPolygon. This type, that is used to draw polygons, already had a property Path of type GeoPath. Now it has a property Paths - plural. This property is of type IList<GeoPath>. The smiley exists of four of these GeoPaths. The first one is the outer shape, the second, third and forth one fall on top of a shape that is earlier in the list, and thus creates a hole. You can get some pretty interesting side effects as you look a the top shape - if you draw a second or later path outside of the first shape, you just get a second shape. But for the map, this is one object and if you click on it you will get the same text. Even more interesting is drawing a second shape partly on top of an earlier shape - the overlapping part becomes a hole, the rest just a filled shape.
Other possibilities are drawing a shape, on top of that a smaller shape (creating a hole), inside the hole an even smaller shape (that will be a normal shape again), on top of that an yet even smaller shape - that will create a hole... etc... and so you can create rings. The odd shapes are shapes (when you start counting with 1!), the even shapes holes.
I have extended the solution that I used in my 7-part map series to make clear how this happens. I have added a class MultiPathList that is basically a very long and boring list of coordinates. It looks like this:
new BasicGeoposition{Latitude = 52.1782977506518, Longitude = 5.40948953479528},


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

namespace Manipulation_Drawing
{
    public class MultiPathList : IMapObject
    {
      public MultiPathList()
      {
          Paths = new List<Geopath>();
      }
      
      public string Name { get; set; }

      public List<Geopath> Paths { get; set; }


      public static List<MultiPathList> GetMultiPolygons()
      {
        var paths = new List<MultiPathList>
        {
          new MultiPathList
          {
            Name = "MultiArea 1",
            Paths = new List<Geopath>
            {
               new Geopath( new[]
               {
                 new BasicGeoposition{Latitude = 52.1840454731137, Longitude = 5.40299842134118},
                 new BasicGeoposition{Latitude = 52.182151498273, Longitude = 5.40619041770697},
                 new BasicGeoposition{Latitude = 52.1841113548726, Longitude = 5.40994542650878},
                 new BasicGeoposition{Latitude = 52.1861041523516, Longitude = 5.40627088397741}
               }),
               new Geopath( new[]
               {
                 new BasicGeoposition{Latitude = 52.184210177511, Longitude = 5.40817516855896},
                 new BasicGeoposition{Latitude = 52.185066556558, Longitude = 5.40637808851898},
                 new BasicGeoposition{Latitude = 52.1842925716192, Longitude = 5.4054393991828},
                 new BasicGeoposition{Latitude = 52.1834195964038, Longitude = 5.40739741176367}
               }),
             }
             //etc
          },
          new MultiPathList
          {
            Name = "Smiley (MultiArea 2)",
            Paths = new List<Geopath>
            {
               new Geopath( new[]
               {
                 new BasicGeoposition{Latitude = 52.1787753514946, Longitude = 5.40471511892974},
                 new BasicGeoposition{Latitude = 52.1801093313843, Longitude = 5.40570753626525},
                 new BasicGeoposition{Latitude = 52.1801258437335, Longitude = 5.40860432200134},
                 new BasicGeoposition{Latitude = 52.1789400558919, Longitude = 5.4108305554837},
                 new BasicGeoposition{Latitude = 52.1772930957377, Longitude = 5.40975767187774},
                 new BasicGeoposition{Latitude = 52.1764037758112, Longitude = 5.40750461630523},
                 new BasicGeoposition{Latitude = 52.1769636869431, Longitude = 5.40490287356079},
               }),
              //etc
             }
          }
        };
        return paths;
    }
  }
}
it just creates two lists of GeoPaths (I have omitted most of them - there are 9 of these paths in grand total). And then in MainPage.xaml.cs you will find this simple method that actually draws the shapes:
private void DrawMultiShapes(object sender, RoutedEventArgs e)
{
  if (!DeleteShapesFromLevel(4))
  {
    var strokeColor = Colors.Crimson;
    var fillColor = Colors.OrangeRed;
    fillColor.A = 150;

    foreach (var dataObject in MultiPathList.GetMultiPolygons())
    {
      var shape = new MapPolygon
      {
        StrokeThickness = 1,
        StrokeColor = strokeColor,
        FillColor = fillColor,
        StrokeDashed = false,
        ZIndex = 4
      };
      foreach (var path in dataObject.Paths)
      {
        shape.Paths.Add(path);
      }
      shape.AddData(dataObject);

      MyMap.MapElements.Add(shape);
    }
  }
}
image[9]In stead of setting the MapPolygon's shape, we add a number of GeoPaths to the Paths property, and that is all. You can view the shapes for yourself by downloading the sample solution from GIT (take branch "multipolygon" )
That is all there is to it. This is a very important for when you are are drawing things like zoning permits, buildings with an inner garden, parking areas in cities (where the fee increases when you come closer to the center), so they are basically rings in rings in rings - etc. - anything were less-than-trivial mapping is concerned. And it's very easy to use now.
And of course, all this goodness can be used on Windows 10 mobile as well. Enjoy mapping!