25 January 2015

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

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

image

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

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

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

image

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

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

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

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

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

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

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

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

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

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

10 January 2015

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

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

I was building a solution as follows

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

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

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

using Xamarin.Forms;

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

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

 

 

 

 

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

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

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

      LoadApplication(new App()); 

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

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

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

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

UPDATE January 11, 2015

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

05 January 2015

Binding shapes to the Windows Phone 8.1 Here Maps control

Intro

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

Map display elements recap

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

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

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

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

Enter WpWinNlMaps

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

Concepts

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

A MapShapeDrawBehavior contains a few properties

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

A sample up front

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

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

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

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

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

So:image

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

The result being something as displayed to the right.

Map shape drawers

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

  • MapIconDrawer
  • MapPolyLineDrawer
  • MapPolygonDrawer

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

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

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

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

The drawer class model is like this:

image

Trapping events and activating commands with EventToCommandMapper

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

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

Some limitations and caveats

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

Conclusion

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

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

Happy mapping!