18 May 2016

Generic databinding for MapIcons in Universal Windows Apps

Preface

Ever since I introduced databinding for the UWP map control (and it’s previous incarnations for Windows Phone 8.x and Windows 8.x) I have been asked to write ‘real’ data binding for map shapes. I have patiently tried to explain that the very nature of the map control makes this impossible as the map shapes are not templated controls drawn by the XAML renderer, but drawn by the map control itself – and that is what makes it so fast. So I encouraged people to write their own MapShapeDrawer child classes that converted a view model into a map shape. How hard can it be, I thought. Judging by the number of requests I got, apparently it is hard, indeed, or too inflexible. So I decided to take a shot at creating a more or less generic class for converting view models to MapIcons – the most commonly used scenario. Thus GenericMapIconDrawer was created.

If the previous paragraphs made no sense at all to you, because this is the first time you ever have encountered my map binding library, I encourage you to read this article first.

Demo

image

I have once again updated the demo application that goes with the WpWinNl project. If you first hit Show Area, then hit “Flags” a number of time, you will see the map slowly getting covered with round flags of Belgium, Germany, Italy, Netherlands, Sweden and the UK. Don’t ask me why I choose this particular group of countries – I just did. If you then hit the “Pirate!” button, one of the nation’s flags will turn into Jolly Rogers, the descriptive label will change in “Arrrr!” and the icon will seem to jump upward a little. If you press the “Pirate!” button again, the current Jolly Roger flags will disappear, and another nation is selected to turn into pirates. Unless it selects a nation that has already turned pirate, then nothing will happen. If you press the “Pirate!” button long enough, all nations will turn into pirates and then disappear. Crime does not pay, in the end. At least in this demo. See video below.

The purpose of this – admittedly – rather peculiar demo, which I created when I was entirely sober indeed, is to show that by merely changing properties things change on the map. So even when it is not strictly data binding, it sure acts like data binding is happening. By the way, on my Surface Pro 4 you hardly see the flickering – it seems like my trusted old 2011 i7 970 machine that I used for recording this video is finally showing it’s age.

How the demo works (aka how you should use the new library feature)

There is actually way more code to the demo than the actual changes to the WpWinNl.Maps package comprise. First of all, the base class for my geometry providing view models has been changed so that it's name property is an actual view model property raising a NotifyPropertyChanged, using the standard MVVMLight syntax

public class GeometryProvider : ViewModelBase
{
  private string _name;
  public string Name
  {
    get { return _name; }
    set { Set(() => Name, ref _name, value); }
  }
}

Then there is the FlagList class, a child class of GeometryProvider, that provides a list of randomly located flags from a randomly selected nation within a rectangle defined by two Geopoints

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

namespace WpWinNl.MapBindingDemo.Models
{
  public class FlagList : GeometryProvider
  {
    public static readonly string[] Countries =
      { "Belgium", "Germany", "Italy", "Netherlands", "Sweden", "UK" };


    private Uri _iconUri;
    public Uri Icon
    {
      get { return _iconUri; }
      set { Set(() => Icon, ref _iconUri, value); }
    }

    private BasicGeoposition _point;
    public BasicGeoposition Point
    {
      get { return _point; }
      set { Set(() => Point, ref _point, value); }
    }

    private Point _anchorPoint = new Point(0.5, 0.5);
    public Point AnchorPoint
    {
      get { return _anchorPoint; }
      set { Set(() => AnchorPoint, ref _anchorPoint, value); }
    }

    private bool _isVisible = true;
    public bool IsVisible
    {
      get { return _isVisible; }
      set { Set(() => IsVisible, ref _isVisible, value); }
    }

    public static IEnumerable<FlagList> GetRandomFlags(
                          Geopoint point1, Geopoint point2, int nrOfPoints)
    {
      var flags = new List<FlagList>();
      var points = PointList.GetRandomPoints(point1, point2, nrOfPoints);
      var r = new Random(DateTime.Now.Millisecond * 2);
      foreach (var point in points)
      {
        var flagIdx = (int)Math.Round(r.NextDouble() * 5);
        flags.Add(new FlagList
        {
          Name = Countries[flagIdx],
          Icon = new Uri($"ms-appx:///Assets/{Countries[flagIdx]}.png"),
          Point = point.Point
        });
      }
      return flags;
    }
  }
}

Notice here, as well, that all properties are raising INotifyPropertyChanged, that IsVisible is true by default and that we have a default icon anchor point of 0.5, 0.5 – that is, the center of the icon falls on the location specified by "Point". 

On the MapBindingViewModel there is a new public property ObservableCollection<FlagList> Flags that only gets loaded up with initial data in the LoadFlags method

public void LoadFlags()
{
  Flags.AddRange(FlagList.GetRandomFlags(new Geopoint(_area.NorthwestCorner),
    new Geopoint(_area.SoutheastCorner), 50));
}
Which is called when you press the "Flags" button. It add 50 icons every time you press it. Then there is the method that changes a random flag into pirate flags
public void ChangeToPirate()
{
  foreach (var flag in Flags.Where(p => p.Name == "Arrr!").ToList())
  {
    flag.IsVisible = false;
  }

  var r = new Random(DateTime.Now.Millisecond * 2);
  var flagIdx = (int)Math.Round(r.NextDouble() * 5);
  var flagName = FlagList.Countries[flagIdx];)
  foreach (var flag in Flags.Where(p => p.Name == flagName).ToList())
  {
    flag.Name = "Arrr!";
    flag.Icon = new Uri("ms-appx:///Assets/JollyRoger.png");
    flag.AnchorPoint = new Point(0.5, 1);
  }
}

Any existing pirate flags are made invisible first, then new ones are created by setting the Name, the Icon and the AnchorPoint property. Thus the label changes, the icon, and the icon on the map seems to jump up about half it’s size as it’s anchor point is now center/bottom in stead of center/center (boy does that terminology bring back memories of my very first job)

In XAML, things are more or less still the same, with some additions:

<maps:MapControl x:Name="MyMap" Grid.Row="0" 
   ZoomLevel="{x:Bind ViewModel.ZoomLevel, Mode=OneWay}" 
   Center="{x:Bind ViewModel.Center, Mode=OneWay}">
  <interactivity:Interaction.Behaviors>
  
    <mapbinding:MapShapeDrawBehavior LayerName="Flags" 
      ItemsSource="{x:Bind ViewModel.Flags, Converter={StaticResource MapObjectsListConverter}}" 
                    PathPropertyName="Point">
      <mapbinding:MapShapeDrawBehavior.EventToHandlerMappers>
        <mapbinding:EventToHandlerMapper EventName="MapElementClick" 
                                         CommandName="SelectCommand" />
      </mapbinding:MapShapeDrawBehavior.EventToHandlerMappers>
      
      <mapbinding:MapShapeDrawBehavior.ShapeDrawer>
        <mapbinding:GenericMapIconDrawer 
           ImageUriPropertyName="Icon" 
           AnchorPropertyName="AnchorPoint" 
           TitlePropertyName="Name" 
           IsVisiblePropertyName="IsVisible" 
           CollisionBehaviorDesired="RemainVisible"/>
      </mapbinding:MapShapeDrawBehavior.ShapeDrawer>
      
    </mapbinding:MapShapeDrawBehavior>
  </interactivity:Interaction.Behaviors>

</maps:MapControl

As drawer we have the new GenericMapIconDrawer that has a boatload of new properties, basically instructing the GenericMapIconDrawer from which view model properties to get the values that should be applied to the MapIcon it is creating. So, technically, this is still not data binding – the properties are pulled from the view model using reflection. Which means that you really should test this using .NET Native tooling to see if those properties are not pulled out by the compiler – or else suffer the pain I suffered when I tried to publish my app.

How the code works

The GenericMapIconDrawer is surprisingly simple. It's basic setup is like this: a few properties and a CreateShape method that actually creates the Icon from the viewmodel:

using Windows.Devices.Geolocation;
using Windows.Foundation;
using System;
using System.Reflection;
using Windows.Storage.Streams;
using Windows.UI.Xaml.Controls.Maps;

namespace WpWinNl.Maps
{
  public class GenericMapIconDrawer : MapShapeDrawer
  {
    protected object ViewModel;

    protected MapIcon Icon;

    public string TitlePropertyName { get; set; }

    public string AnchorPropertyName { get; set; }

    public string ImageUriPropertyName { get; set; }

    public string IsVisiblePropertyName { get; set; }

    public MapElementCollisionBehavior CollisionBehaviorDesired { get; set; }

    public override MapElement CreateShape(object viewModel, BasicGeoposition pos)
    {
      ViewModel = viewModel;

      Icon = new MapIcon
      {
        Location = new Geopoint(pos),
        CollisionBehaviorDesired = CollisionBehaviorDesired,
        ZIndex = ZIndex
      };

      SetPropertyValuesFromViewModel();

      return Icon;
    }
  }
}

The SetPropertyValuesFromViewModel is pull the additional four properties from the view model (the position is already being taken care of by the MapShapeDrawBehavior itself)

private void SetPropertyValuesFromViewModel()
{
  string title = null;
  if (TryGetPropertyValue(ViewModel, TitlePropertyName, ref title))
  {
    Icon.Title = title;
  }

  Point anchorPoint;
  if (TryGetPropertyValue(ViewModel, AnchorPropertyName, ref anchorPoint))
  {
    Icon.NormalizedAnchorPoint = anchorPoint;
  }

  Uri imageUri = null;
  if (TryGetPropertyValue(ViewModel, ImageUriPropertyName, ref imageUri))
  {
    Icon.Image = RandomAccessStreamReference.CreateFromUri(imageUri);
  }

  bool isVisble = true;
  if (TryGetPropertyValue(ViewModel, IsVisiblePropertyName, ref isVisble))
  {
    Icon.Visible = isVisble;
  }
}

And because I am a lazy b*****d I wrote a little helper method do to the repetitive heavy lifting for that

private static bool TryGetPropertyValue<T>(object obj, string propertyName, 
                                           ref T outValue)
{
  if (!string.IsNullOrWhiteSpace(propertyName))
  {
    var prop = obj.GetType().GetRuntimeProperty(propertyName);
    var result = prop?.GetValue(obj);
    if (result is T)
    {
      outValue = (T) prop.GetValue(obj);
      return true;
    }
  }
  return false;
}

Note, however, that only the position, label text, icon uri, anchor point and visibility are pulled from the view model. Z-index and collisionbehavior are not. Deep down in the MapShapeDrawBehavior, in the CreateShape method, there is another change that I want to draw your attention to:

var evt = viewModel.GetType().GetRuntimeEvent("PropertyChanged");
if (evt != null)
{
  var observable = Observable.FromEventPattern<PropertyChangedEventArgs>(
     viewModel, "PropertyChanged")
    .Subscribe(se =>
    {
      if (!LegacyMode || se.EventArgs.PropertyName == PathPropertyName)
      {
        ReplaceShape(se.Sender);
      }
    });

  TrackObservable(viewModel, observable);
}

Previously, the shape would only be replaced if the geometry changed. Now, unless the new property LegacyMode is set to true, this will happen at every PropertyChanged event. If you look carefully at the video, you will actually see the Jolly Rogers flickering, which is correct – since three properties are changed (Name, Icon and AnchorPoint) each flag is redrawn three times. This is quite inefficient, but unfortunately the way it works. You cannot change an Icon, only replace it. So for every property change it actually gets replaced indeed, and to that extent I also had to make a little change to ReplaceShape itself.

private void ReplaceShape(object viewModel)
{
  var shape = AssociatedObject.MapElements.FirstOrDefault(p => p.ReadData() == viewModel);
  if (shape != null)
  {
    var shapeLocation = AssociatedObject.MapElements.IndexOf(shape);
    if (shapeLocation != -1)
    {
      var newShape = CreateShape(viewModel);
      if (newShape != null)
      {
        // Previous code
        // AssociatedObject.MapElements[shapeLocation] = CreateShape(viewModel); 
        AssociatedObject.MapElements.RemoveAt(shapeLocation);
        AssociatedObject.MapElements.Insert(shapeLocation, newShape);
      }
    }
  }
  else
  {
    AddNewShape(viewModel);
  }
}

So this experiment did not only bring new (or at least easier to use) functionality – it also instilled a bug fix. Of course, you can work around the repeated drawing/flickering by making a view model that does not fire PropertyChanged on every property change, but handle this manually when you are done. But that kind of performance tweaking is outside of the scope of this article.

Concluding remarks

Data binding shapes in the classical way still is not possible, so I had to resort to something that acts like it. I hope this makes using this package for mapping a bit easier. Be advised that for massive changes to large datasets this may not be the most efficient way to get things done, but for your average project it makes things way easier.

It’s now downloadable from NuGet as version 3.0.6, and you can find the sources of the demo app here.

13 May 2016

Keeping input fields above the keyboard in UWP apps

Before you all think I am stark raving mad – it appears that it is actually possible to create XAML constructions that confuse the UWP renderer to such an extent that although it moves the user interface upwards - as it should - it does not always move it up far enough. This can be observed in the video below - as well as the fact that it is fixable.

A user observed this on my app Map Mania (it has been fixed since). I have only been able to repro this on Windows 10 mobile. Apparently it has something to do with going rampant on adaptive triggers, and another key part is the use of an bottom app bar.

The XAML is a simplified version of what I used for the post about a CompositeTrigger-enabled AdaptiveTrigger – basically, I use a simple viewmodel and VisualStateGroup with some Triggers to change what I see on the screen. The XAML is not too complicated:

<Grid >
  <Grid.RowDefinitions>
    <RowDefinition Height="Auto"></RowDefinition>
    <RowDefinition Height="*"></RowDefinition>
  </Grid.RowDefinitions>
  <controls:PageHeader Text="Fix" FontSize="30" VisualStateNarrowMinWidth="0" 
            VisualStateNormalMinWidth="700"></controls:PageHeader >
  <Grid Grid.Row="1" Margin="12" x:Name="TopGrid">
    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="*"></ColumnDefinition>
    </Grid.ColumnDefinitions>
    <Grid>
      <Grid.RowDefinitions>
        <RowDefinition Height="60"></RowDefinition>
        <RowDefinition Height="*"></RowDefinition>
        <RowDefinition Height="Auto"></RowDefinition>
      </Grid.RowDefinitions>

      <Grid Margin="0,6,0,6" x:Name="NarrowMenu" >
        <Grid.RowDefinitions>
          <RowDefinition Height="Auto"></RowDefinition>
          <RowDefinition Height="Auto"></RowDefinition>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
          <ColumnDefinition Width="Auto"></ColumnDefinition>
          <ColumnDefinition Width="Auto"></ColumnDefinition>
        </Grid.ColumnDefinitions>
        <TextBlock Text="Green" Grid.Row="0"  FontSize="20"
                   Margin="0,0,6,0" Tapped="{x:Bind ViewModel.ToggleDisplay}" >
        </TextBlock>

        <TextBlock Text="Red" Grid.Row="0" Grid.Column="1"  FontSize="20"
                   Margin="6,0,0,0" Tapped="{x:Bind ViewModel.ToggleDisplay}">
        </TextBlock>
        <Grid Height="2" Background="White" Grid.Row="1" Grid.Column="0" Margin="0,0,6,0" 
        x:Name="GreenUnderline"/>
        <Grid Height="2" Background="White" Grid.Row="1" Grid.Column="1" Margin="6,0,0,0" 
        x:Name="RedUnderline"/>
      </Grid>

      <Grid Background="Green" Grid.Row="1" x:Name="GreenArea"></Grid>
      <Grid Background="Red" Grid.Row="1" x:Name="WideRedArea"></Grid>

      <StackPanel Grid.Row="2" Orientation="Vertical" HorizontalAlignment="Stretch" 
         VerticalAlignment="Bottom" >
        <TextBlock  Text="Some label" x:Uid="MapName"  Margin="0,0,0,6"/>
        <TextBox TextWrapping="NoWrap"/>
      </StackPanel>
    </Grid>
  </Grid>
</Grid>

This stuff is based on Template10, but the actual usage is very limited. So first we have some heading, then the menu, then the two areas (green and red) that are used to fill the middle of the screen – it stands in for actual content – and then all the way below, in red and bold, the stackpanel that has some problems, as displayed in the video. When you click on the menu text(“Red” and “Green”) a command in the view model is called that flips a property “TabDisplay”. This triggers the VisualStateManager, which is in fact

The VisualStateManager is actually pretty simple:

<VisualStateManager.VisualStateGroups>
  <VisualStateGroup x:Name="WindowStates" >
    <VisualState x:Name="NarrowState_Red">
      <VisualState.StateTriggers>
        <StateTrigger IsActive="{x:Bind ViewModel.TabDisplay, Mode=OneWay}"/>
      </VisualState.StateTriggers>
      <VisualState.Setters>
        <Setter Target="WideRedArea.Visibility" Value="Visible"></Setter>

        <Setter Target="GreenUnderline.Visibility" Value="Collapsed"></Setter>
      </VisualState.Setters>
    </VisualState>

    <VisualState x:Name="NarrowState_Green">
      <VisualState.StateTriggers>
        <StateTrigger 
          IsActive=
"{x:Bind ViewModel.TabDisplay, Mode=OneWay,Converter={StaticResource BoolInvertConverter}}"/> </VisualState.StateTriggers> <VisualState.Setters> <Setter Target="WideRedArea.Visibility" Value="Collapsed"></Setter> <Setter Target="RedUnderline.Visibility" Value="Collapsed"></Setter> </VisualState.Setters> </VisualState> </VisualStateGroup> </VisualStateManager.VisualStateGroups

So far, so good but when you use a construction like this, and you put anything below it, your might run into issues as I described. Unless you add a little something to the stackpanel:

<StackPanel Grid.Row="2" Orientation="Vertical" HorizontalAlignment="Stretch"
   VerticalAlignment="Bottom" >
  <interactivity:Interaction.Behaviors>
    <behaviors:KeepAboveInputPaneBehavior/>
  </interactivity:Interaction.Behaviors>
  <TextBlock  Text="Some label" x:Uid="MapName"  Margin="0,0,0,6"/>
  <TextBox TextWrapping="NoWrap"/>
</StackPanel

And people who know me won’t be surprised is it actually a behavior again :)

using Windows.UI.ViewManagement;
using Windows.UI.Xaml;
using Microsoft.Xaml.Interactivity;

namespace WpWinNl.Behaviors
{
  public class KeepAboveInputPaneBehavior : Behavior<FrameworkElement>
  {
    private Thickness _originalMargin;

    protected override void OnAttached()
    {
      base.OnAttached();
      AssociatedObject.Loaded += AssociatedObjectLoaded;
      _originalMargin = AssociatedObject.Margin;
    }

    private void AssociatedObjectLoaded(object sender, RoutedEventArgs e)
    {
      AssociatedObject.Loaded -= AssociatedObjectLoaded;
      InputPane.GetForCurrentView().Hiding += InputPaneHiding;
      InputPane.GetForCurrentView().Showing += InputPaneShowing;
    }

    protected override void OnDetaching()
    {
      InputPane.GetForCurrentView().Hiding -= InputPaneHiding;
      InputPane.GetForCurrentView().Showing -= InputPaneShowing;
    }

    private void InputPaneShowing(InputPane sender, InputPaneVisibilityEventArgs args)
    {
      AssociatedObject.Margin = 
        new Thickness(_originalMargin.Left, _originalMargin.Top, 
        _originalMargin.Right, _originalMargin.Bottom + args.OccludedRect.Height);
    }

    private void InputPaneHiding(InputPane sender, InputPaneVisibilityEventArgs args)
    {
      AssociatedObject.Margin = _originalMargin;
    }
  }
}

When the attached object is loaded, it’s original margins are recorded. When the input pane is showing, the height of the ‘OcculedRect’ is added to it, moving the attached object op to exactly above the input bar.

This is possibly a bug, or the SDK team just never imagined people doing odd things with the Visual State Manager – “A fool may ask more questions in an hour than a wise man can answer in seven years”, right ;). Whatever – I like I tell people: you can moan about things like this or cry foul at Microsoft, but I find it much more fun to try and fix them. QED

A sample solution, with the behavior, can be found here.

25 April 2016

Behavior for view model driven animated popups in Xamarin Forms

Preface

popupIn my previous post I showed the basics for animation behaviors in Xamarin Forms. Here’s another one I made, that in conjunction with some nifty element binding makes for a pretty fluid popup appearing from the middle. Of course, you can use the native alert box but I’d rather want to see is something as displayed to the right, especially with a nice animation. The advantage of such a custom made popup is that is looks much more consistent across platforms, which is a huge win especially for LOB apps.

You can see the behavior in action below:

... and all there is to it...

The actual code is pretty small now all the heavy lifting has been done by the base classes from the previous post:

using Wortell.XamarinForms.Behaviors.Base;
using Xamarin.Forms;

namespace Wortell.XamarinForms.Behaviors
{
  public class AnimateScaleBehavior : AnimateFoldBehaviorBase
  {
    protected override void Init(bool newValue)
    {
      if (newValue)
      {
        FoldOutPosition = 1;
        FoldInPosition = 0;
        AssociatedObject.Scale = FoldInPosition;
      }
    }

    protected override void ExecuteAnimation(double start, double end, 
                                             uint runningTime)
    {
      var animation = new Animation(
        d => AssociatedObject.Scale = d, start, end, Easing.SinOut);

      animation.Commit(AssociatedObject, "Unfold", length: runningTime,
        finished: (d, b) =>
        {
          if (AssociatedObject.Scale.Equals(FoldInPosition))
          {
            AssociatedObject.IsVisible = false;
          }
        });
    }
  }
}

In stead of animating TranslationX and TranslationY, like in the previous post, now the Scale property is animated from 0 to 1 and back. Attentive readers may have seen something else though - as soon as the animation starts, a grey haze appears over the underlying screen, that only disappears when the animation is done. This is entirely done in XAML - using element binding.

The whole popup sits in MenuControl.xaml – this is a user control

<?xml version="1.0" encoding="utf-8" ?>
<Grid xmlns="http://xamarin.com/schemas/2014/forms"
      //Rest of namespace stuff omitted
      IsVisible="{Binding IsVisible, Source={x:Reference AnimatedGrid}}">
  <ContentView BackgroundColor="{StaticResource SeeThrough}"
               HorizontalOptions="Fill" VerticalOptions="Fill" >

    <ContentView Padding="20,0,20,0" x:Name="AnimatedGrid" IsVisible="False">
      <ContentView.Behaviors>
        <behaviors:AnimateScaleBehavior IsVisible="{Binding IsMenuVisible}" 
           ViewIsInitialized="{Binding ViewIsInitialized}"/>
      </ContentView.Behaviors>
      <ContentView Style="{StaticResource MenuStyle}" HorizontalOptions="Fill"
                    VerticalOptions="CenterAndExpand" Padding="0,0,0,10">
        <Grid>
          <Grid.RowDefinitions>
            <RowDefinition Height="40"></RowDefinition>
            <RowDefinition Height="Auto"></RowDefinition>
          </Grid.RowDefinitions>

          <ContentView  Grid.Row="0" Style="{StaticResource PopupHeaderStyle}">
            <Label Style="{StaticResource PopupHeaderTextStyle}" Text="Popup header" 
                VerticalOptions="Center"/>
          </ContentView>

          <StackLayout Orientation="Vertical" Grid.Row="1">

            <StackLayout Style="{StaticResource ContentStyle}"  Orientation="Vertical">
              <Label Style="{StaticResource MenuTextStyle}" Text="Here be text" />  
              <Label Style="{StaticResource MenuTextStyle}" Text="Here be more text"
                  VerticalOptions="Center"/>
            </StackLayout>

            <ContentView Style="{StaticResource Separator}"></ContentView>

            <StackLayout Style="{StaticResource ContentStyle}"  Orientation="Vertical">
              <Label Style="{StaticResource MenuTextStyle}" Text="Here be text" />
              <Label Style="{StaticResource MenuTextStyle}" Text="Here be more text" />
              <Label Style="{StaticResource MenuTextStyle}" 
                  Text="Here be more whatever UI elements you want" />
            </StackLayout>

            <Grid HeightRequest="10"></Grid>

            <Grid Style="{StaticResource ContentStyle}">
              <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*"></ColumnDefinition>
                <ColumnDefinition Width="10"></ColumnDefinition>
                <ColumnDefinition Width="*"></ColumnDefinition>
              </Grid.ColumnDefinitions>
                 <Button Text="Ok" Command="{Binding CloseMenuCommand}"></Button>
              <Button Text="Cancel" Grid.Column="2" 
                  Command="{Binding CloseMenuCommand}"></Button>
            </Grid>
          </StackLayout>
        </Grid>
      </ContentView>
    </ContentView>

    <ContentView.GestureRecognizers>
      <TapGestureRecognizer Command="{Binding CloseMenuCommand}"></TapGestureRecognizer>
    </ContentView.GestureRecognizers>
  </ContentView>

</Grid>

I have marked the interesting parts in red and bold. On top you see that the visibility of the control itself is bound to the visibility of the grid called “AnimatedGrid”. This grid is inside a ContentView which has a background color defined by the resource “SeeTrough. This you can find in app.xaml defined as being color #B2000000, aka "70% black" in designer lingo (i.e. black that is 30% transparent). AnimatedGrid is the top level element of the actual popup – it’s visibility is controlled by the AnimateScaleBehavior. As soon as the animation starts by IsVisible flipping value, the AnimatedGrid is first made visible, and it’s parent – and the SeeThrough ContentView – popup into view, only to disappear when the animation is completely finished. This is exactly what I wanted to achieve – the 70% haze turns the focus of the user to the little task at hand now, and also blocks tapping access to whatever UI elements are still visible below it.

Note, there no values supplied for FoldInTime and FoldOutTime so the default values of 150 and 250ms are used. But you can easily change that by adding them to the behavior in XAML.

As a final thing, I have added a TapGestureRecognizer that calls the popup closing command to the 70% gray area as well. This dismisses the popup when you tap outside it, which is exactly what one expects in a mobile app. It’s effectively the same thing as hitting Cancel (and in this case, Ok, as that does nothing but closing the popup as well).

Viewmodel

Hardly worth mentioning, but to make the picture complete:

using Xamarin.Forms;

namespace XamarinFormsDemos.ViewModels
{
  public class PopupViewModel : MenuViewModelBase
  {
    public Command CloseMenuCommand { get; private set; }

    public PopupViewModel() : base()
    {
      CloseMenuCommand = new Command(() => IsMenuVisible = false);
    }
  }
}
Command makes boolean false. So menu disappears again. Doh ;)

Usage

<?xml version="1.0" encoding="utf-8" ?>
<demoViewFramework:BaseContentPage xmlns="http://xamarin.com/schemas/2014/forms"
    // stuff omitted
  <Grid>
    <!-- Page content -->
    <Grid.RowDefinitions>
      <RowDefinition Height="25*"></RowDefinition>
      <RowDefinition Height="75*"></RowDefinition>
    </Grid.RowDefinitions>
    <Grid Row="0" Style="{StaticResource HeaderOutsideStyle}">
      <ContentView Style="{StaticResource ContentStyle}">
        <Label Text="Popup menu" Style="{StaticResource HeaderStyle}"  
        VerticalOptions="CenterAndExpand"></Label>
      </ContentView>
    </Grid>

    <ContentView Grid.Row="1" Style="{StaticResource ContentStyle}" 
        HorizontalOptions="Fill">
      <Label Text="Here be page content" 
        Style="{StaticResource ContentTextStyle}"></Label>

      <ContentView  VerticalOptions="Start" Style="{StaticResource ContentStyle}" >
          <Button Text="Open popup menu" Command="{Binding ToggleMenuCommand}" 
                  HorizontalOptions="Fill" VerticalOptions="Start"></Button>
      </ContentView>
    </ContentView>

    <!-- Menu -->
    <controls:PopupControl Grid.Row="0" Grid.RowSpan="2"></controls:PopupControl>

  </Grid>
</demoViewFramework:BaseContentPage;/Grid>

Once again, the control is added last and convers the whole screen, as to appear over the existing UI.

Some final words

When you are developing cross-platform apps, UI testing is crucial. For instance, while I was making this app, I noticed that when you change this

<Grid 
      //Namespaces omitted
      IsVisible="{Binding IsVisible, Source={x:Reference AnimatedGrid}}">
  <ContentView BackgroundColor="{StaticResource SeeThrough}"
               HorizontalOptions="Fill" VerticalOptions="Fill">

in this:

       
<Grid 
      //Namespaces omitted
      >
  <ContentView IsVisible="{Binding IsVisible, Source={x:Reference AnimatedGrid}}"
               BackgroundColor="{StaticResource SeeThrough}"
               HorizontalOptions="Fill" VerticalOptions="Fill">

you get a visually a identical result on all three platforms covered in the test project. On Android and Windows 10 it even works the same. But if you try to run on iOS, you will notice the “Open popup menu” is not clickable. This is because the outer grid covers the whole screen and is always visible - and although it’s empty, it apparently blocks all user input. As usual, Apple does things different ;)

The demo project – with updated code – can be found here

This article appeared earlier – in Dutch – on the Wortell company blog

13 April 2016

Behaviors for animated scroll-down and slide-in menus in Xamarin Forms

Preface

Some time ago I wrote about a proof-of-concept for viewmodel driven animations using behaviors in Xamarin Forms. In the mean time, time has moved on, so has Xamarin Forms, and the idea I had back then made it into a kind of framework solution I now used professionally. And it’s time to show how it’s done now in detail.

This article is about building animated scroll-into-view menu’s in Xamarin Forms, and to make sure we all understand what that means, I made this little video showing the code in action on an Android emulator, a Windows 10 Universal Windows Platform app, and an iPhone simulator:

Framework

The demo app contains a simple framework (project DemoViewFramework) that supports a number of things crucial to an app sporting viewmodel driven behaviors, that is:

  • facilitation of  MVVM (using MVVMLight)
  • registration of which view belongs to what viewmodel
  • navigation from viewmode to viewmodel (in stead of from page to page)
  • exposing essential events in the life cycle of the page to it’s accompanying viewmodel.

I won’t go into very much detail about the framework – it’s a pretty na├»ve implementation anyway – but there are two key things to take away. The first one is the class BaseContentPage

using Xamarin.Forms;

namespace DemoViewFramework
{
  public class BaseContentPage : ContentPage
  {
    protected override void OnAppearing()
    {
      base.OnAppearing();
      Context?.OnViewAppearing();
    }

    protected override void OnSizeAllocated(double width, double height)
    {
      base.OnSizeAllocated(width, height);
      Context?.OnViewInitialized(true);
    }

    protected override void OnDisappearing()
    {
      base.OnDisappearing();
      Context?.OnViewInitialized(false);
      Context?.OnViewDisappearing();
    }

    private IPageViewModelBase Context => (IPageViewModelBase)BindingContext;
  }
}
This class is intended to be a base class for your pages, and it's sole purpose is to channel the OnAppearing, OnSizeAllocated and OnViewDisappearing events to the object in the Page's BindingContext - that is, the view model. To this intent, every view model should implement the interface IPageViewModelBase so the corresponding methods can be called:
namespace DemoViewFramework
{
  public interface IPageViewModelBase
  {
    void OnViewInitialized(bool value);

    void OnViewAppearing(object state = null );

    void OnViewDisappearing();

    INavigationService NavigationService { get; set; }
  }
}
If you are using your own Navigation framework, you can forget about the NavigationService property. To make implementation easier, there's a base class for view models – which is the second key takeaway. In the demo project is a child class from MVVMLight's ViewModelBase, but of course you are very free to implement your own INotifyPropertyChanged base class as well.
using GalaSoft.MvvmLight;

namespace DemoViewFramework
{
  public class PageViewModelBase : ViewModelBase, IPageViewModelBase
  {
    public virtual void OnViewInitialized(bool value)
    {
      ViewIsInitialized = value;
    }

    private bool _viewIsInitialized;
    public bool ViewIsInitialized
    {
      get { return _viewIsInitialized; }
      set { Set(() => ViewIsInitialized, ref _viewIsInitialized, value); }
    }

    public virtual void OnViewAppearing(object state = null)
    {
      ViewHasAppeared = true;
    }

    public virtual void OnViewDisappearing()
    {
      ViewHasAppeared = false;
    }

    private bool _viewHasAppeared;
    public bool ViewHasAppeared
    {
      get { return _viewHasAppeared; }
      set { Set(() => ViewHasAppeared, ref _viewHasAppeared, value); }
    }

    public INavigationService NavigationService { get; set; }
  }
}
Key thing here is that there are two properties - ViewHasAppeared and ViewIsInitialized - available in every view model and they are set automatically by the framework. Behaviors handling animations need to know when they actually can start doing stuff and they can bind to one of these properties. As I explained in a previous blog post, from Xamarin 2.1 apparently you can only start after OnSizeAllocated, which translates to ViewIsInitialized in the view model, so the initialization code needs to be fired when that property is set to true. The actual activation of the behavior (that is, the animation), needs to come from another property. That may sound complicated, but I assure you it's not that bad. Just read on.

The (base) view model for driving animations

Basically, we need something to kick off the animations. To this extent, we are using this very simple view model

using DemoViewFramework;
using Xamarin.Forms;

namespace XamarinFormsDemos.ViewModels
{
  public class MenuViewModelBase : PageViewModelBase
  {
    private bool _isMenuVisible;

    public MenuViewModelBase()
    {
      ToggleMenuCommand = new Command(() => IsMenuVisible = !IsMenuVisible);
    }

    public bool IsMenuVisible
    {
      get { return _isMenuVisible; }
      set { Set(() => IsMenuVisible, ref _isMenuVisible, value); }
    }

    public Command ToggleMenuCommand { get; private set; }
  }
}

A command that toggles a simple boolean property. I mean, how hard can it be, right?

Leveling the playing field

If you are coming from Windows behaviors, like I do, you are in for a surprise. Xamarin behaviors are a prime example of something that quacks like a duck and walks like a duck, can be a goose after all. Some things are a bit different and – like a goose – can bite you pretty badly. First of all, there is no standard binding context, and there is no AssociatedObject property to easily refer to. This can make things a bit complex when 'translating' behaviors from Windows to Xamarin and back, hence this base class to make sure we have the same - or at least a more similar - base

using System;
using Xamarin.Forms;

namespace Wortell.XamarinForms.Behaviors.Base
{
  public abstract class BindableBehaviorBase<T> : Behavior<T> 
    where T : VisualElement
  {
    protected T AssociatedObject { get; private set; }

    protected override void OnAttachedTo(T bindable)
    {
      AssociatedObject = bindable;
      bindable.BindingContextChanged += Bindable_BindingContextChanged;
      base.OnAttachedTo(bindable);
    }

    protected override void OnDetachingFrom(T bindable)
    {
      bindable.BindingContextChanged -= Bindable_BindingContextChanged;
      base.OnDetachingFrom(bindable);
      AssociatedObject = null;
    }

    private void Bindable_BindingContextChanged(object sender, EventArgs e)
    {
      if (AssociatedObject != null)
      {
        BindingContext = AssociatedObject.BindingContext;
      }
    }
  }
}
It's a bit of an oddball class, but it's purpose is simple - after this has run, you can bind to any of the behavior's dependency properties, and in your code you can refer to a typed AssociatedObject, just like you would in Windows XAML behaviors.

Getting a bit animated

Now earlier in this blog post I mentioned the fact that behaviors doing animations probably need to know when a view is ready initializing, so they can initialize themselves. To get to this point, there is this base class on top of  BindableBehaviorBase, with a similar ‘original name:

using Xamarin.Forms;

namespace Wortell.XamarinForms.Behaviors.Base
{
  public abstract class ViewInitializedBehaviorBase<T> : BindableBehaviorBase<T> 
    where T : VisualElement
  {
    #region ViewIsInitialized Attached Dependency Property      
    public static readonly BindableProperty ViewIsInitializedProperty =
       BindableProperty.Create(nameof(ViewIsInitialized), typeof(bool), 
       typeof(ViewInitializedBehaviorBase<T>),
       default(bool), BindingMode.TwoWay,
       propertyChanged: OnViewIsInitializedChanged);

    public bool ViewIsInitialized
    {
      get { return (bool)GetValue(ViewIsInitializedProperty); }
      set { SetValue(ViewIsInitializedProperty, value); }
    }

    private static void OnViewIsInitializedChanged(BindableObject bindable, 
            object oldValue, object newValue)
    {
      var thisObj = bindable as ViewInitializedBehaviorBase<T>;
      thisObj?.Init((bool)newValue);
    }

    #endregion

    protected abstract void Init(bool viewIsInitialized);
  }
}

When you bind the behavior’s ViewIsInitialized property to the viewmodel’s ViewIsInitialized property, the behavior knows ‘when the view is ready (or not anymore). Whatever, if the property in the view model changes, the behavior calls its (now abstract) method Init.

So what happens is

BaseContentPage –> PageViewModelBase –> (property binding) ViewInitializedBehaviorBase.Init –>
  Concrete Implementation.Init

And finally - menu animations

One more base class to go. I noticed pretty soon that animations handling the folding or scrolling of things almost always consider one property to be animated. So I created a yet another base class

using Xamarin.Forms;

namespace Wortell.XamarinForms.Behaviors.Base
{
  public abstract class AnimateFoldBehaviorBase : 
    ViewInitializedBehaviorBase<View>
  {
    protected double FoldInPosition;
    protected double FoldOutPosition;

    protected VisualElement GetParentView()
    {
      var parent = AssociatedObject as Element;
      VisualElement parentView = null;
      if (parent != null)
      {
        do
        {
          parent = parent.Parent;
          parentView = parent as VisualElement;
        } while (parentView?.Width <= 0 && parent.Parent != null);
      }

      return parentView;
    }

    protected override void OnAttachedTo(View bindable)
    {
      base.OnAttachedTo(bindable);
      bindable.IsVisible = false;
    }

    private void ExecuteAnimation(bool show)
    {
      if (show)
      {
        AssociatedObject.IsVisible = true;
        ExecuteAnimation(FoldInPosition, FoldOutPosition, (uint)FoldOutTime);
      }
      else
      {
        ExecuteAnimation(FoldOutPosition, FoldInPosition, (uint)FoldInTime);
      }
    }

    protected abstract void ExecuteAnimation(double start, double end, 
      uint runningTime);

    public static readonly BindableProperty IsVisibleProperty =
       BindableProperty.Create(nameof(IsVisible), typeof(bool), 
       typeof(AnimateFoldBehaviorBase),
       false, BindingMode.OneWay,
       propertyChanged: OnIsVisibleChanged);

    public bool IsVisible
    {
      get { return (bool)GetValue(IsVisibleProperty); }
      set {SetValue(IsVisibleProperty, value); }
    }

    private static void OnIsVisibleChanged(BindableObject bindable, 
       object oldValue, object newValue)
    {
      var thisObj = bindable as AnimateFoldBehaviorBase;
      thisObj?.ExecuteAnimation((bool)newValue);
    }   

    // FoldInTime Attached Dependency Property      
 
    // FoldOutTime Attached Dependency Property      
  }
}

This class handles a few important things. First of all, it gets the parent view – in a menu’s case the page. In fact, it finds the first object that has a width, thus a size. This is the behavior’s ‘playing field’. Then there is the ExecuteAnimation method, that actually starts the animation – or reverses it. Note also that the behavior actually hides the element that it’s animating – this whole setup assumes you will move something into view, while it’s not in view initially. You might notice that the Init method which is abstract in it's parent class is still not implemented. That happens only in the concrete class - that is actually pretty small, now all the ground work has been laid:

using Wortell.XamarinForms.Behaviors.Base;
using Xamarin.Forms;

namespace Wortell.XamarinForms.Behaviors
{
  public class AnimateSlideDownBehavior : AnimateFoldBehaviorBase
  {
    protected override void Init(bool newValue)
    {
      if (newValue)
      {
        var parentView = GetParentView();
        if (parentView != null)
        {
          FoldInPosition = -parentView.Height;
          AssociatedObject.TranslationY = FoldInPosition;
        }
      }
    }

    protected override void ExecuteAnimation(double start, 
       double end, uint runningTime)
    {
      var animation = new Animation(
        d => AssociatedObject.TranslationY = d, start, end, Easing.SinOut);

      animation.Commit(AssociatedObject, "Unfold", length: runningTime,
        finished: (d, b) =>
        {
          if (AssociatedObject.TranslationY.Equals(FoldInPosition))
          {
            AssociatedObject.IsVisible = false;
          }
        });
    }
  }
}

So on Init, the menu is moved up exactly it’s own height and - assuming it’s on top of the page - it will appear just outside the view. The actual animation code itself is laughably simple – it just animates over TranslationY, and when it find it’s ending at the FoldInPosition, it will make the animated object invisible

All that’s left now is defining the menu in XAML and then adding the behavior to it. It’s important to add the menu after the actual page content so it will be drawn over it. Otherwise it will just slide behind it, and that’s not very useful.

<!-- Menu -->
<ContentView Grid.Row="0" Grid.RowSpan="2">
  <ContentView.Behaviors>
    <behaviors:AnimateSlideDownBehavior
      IsVisible="{Binding IsMenuVisible}"
      ViewIsInitialized="{Binding ViewIsInitialized}"
      FoldOutTime="400" FoldInTime="300"/>
  </ContentView.Behaviors>
  <Grid>
    <Grid.RowDefinitions>
      <RowDefinition Height="75*"></RowDefinition>
      <RowDefinition Height="25*"></RowDefinition>
    </Grid.RowDefinitions>
    <ContentView Style="{StaticResource MenuStyle}">
      <StackLayout Style="{StaticResource ContentStyle}" 
        Orientation="Vertical" VerticalOptions="Start">
        <Image Source="chevronup.png" HeightRequest="40" 
          VerticalOptions="Start" HorizontalOptions="Start">
          <Image.GestureRecognizers>
            <TapGestureRecognizer Command="{Binding ToggleMenuCommand}"/>
          </Image.GestureRecognizers>
        </Image>
        <Label Text="Menu" Style="{StaticResource HeaderStyle}"
          VerticalOptions="Start"/>
        <Label Text="Here be menu content" 
         Style="{StaticResource MenuTextStyle}"></Label>
      </StackLayout>
    </ContentView>

    <!-- Leftover space -->
    <ContentView Grid.Row="1"  HorizontalOptions="Fill" VerticalOptions="Fill">
      <ContentView.GestureRecognizers>
        <TapGestureRecognizer Command="{Binding ToggleMenuCommand}"/>
      </ContentView.GestureRecognizers>
    </ContentView>
  </Grid>
</ContentView>

Note the binding of ViewIsInitialized to ViewIsInitialized for the initialization, and of IsVisible to IsMenuVisible for the actual execution of the animation. Also notice the FoldOutTime and FoldInTime – the menu will fold out in 400, and fold in in 300ms. These are optional values – if you don’t specify them, the behavior will use default values. Finally, note the fact that the menu actually covers the whole screen, but the lower part is empty. Yet, if you tap that part, the menu will move away as well, as you would expect in an app.

The AnimateSlideInBehavior is nearly the same but animates TranslateX – just have a look at the sample code, it’s yours for the taking

Some concluding remarks

Of course you can also code these animations into code behind or in a control’s code behind, but that way you will need to copy and paste code into your views time and time again, and potentially you will need to fix the same bugs on multiple places. What I usually do is indeed make a control, but use the behavior in that and then re-use the control over multiple pages. Using a behavior decouples the code from a specific control or page, and you can re-use the dynamic behavior everywhere.

One final bit – the Xamarin iOS linker is still sometimes ‘optimizing’ away code, just like .NET Native, something that already bit me last year. So in the Wortell.XamarinForms there is this extremely odd class Initializer:

namespace Wortell.XamarinForms
{
  public static class Initializer
  {
    public static void Init()
    {
    }
  }
}
that indeed does completely nothing, except for being called from the iOS project's AppDelegate
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
    global::Xamarin.Forms.Forms.Init();
    global::Wortell.XamarinForms.Initializer.Init();
    LoadApplication(new App());

    return base.FinishedLaunching(app, options);
}
Which still does nothing, but  tricks the compiler into thinking the code is actually used (which is true) so the assembly actually gets packaged with the app.

I hope this blog will inspire you to try your hand at animations with Xamarin Forms as well. It’s actually pretty easy when you get the hang of it. And there is more to come on this blog.

Parts of this text appeared earlier on the Wortell company blog, in Dutch

30 March 2016

Xamarin Forms 2.1 upgrade–some surprises (and how to get around it)

Back in June I wrote about a proof-of-concept for view model driven animations using behaviors in Xamarin Forms. This concept made it into my employer’s standard Xamarin library (more about that soon) but after an upgrade I noticed the animation behaviors did not work anymore – at least on Android. I have no idea what happened along the way, but fact is that the the behavior no longer worked after upgrading to Xamarin Forms 2.1. A prolonged debugging session learned me the following:

  1. At the ViewAppearing stage user interface elements don’t have a size allocated yet – whereas they used to have that.
  2. A parent object - especially something a grid – does not does not necessarily has to have it’s size set yet (width and height are still –1)

I will write about this soon in more detail, but what needs to be done to get this working again is:

  1. We need to initialize the behavior on the page’s OnSizeAllocated, not the OnViewAppearing method
  2. We need to go up recursively until we find an element that does not have a size of –1 in stead of blindly taking the parent.

So, in StartPage.xaml.cs:

protected override void OnAppearing()
{
  Context.OnViewAppearing();
  base.OnAppearing();
}

protected override void OnSizeAllocated(double width, double height)
{
  Context.OnViewAppearing();
  base.OnSizeAllocated(width, height);
}

It is a bit of a kludge, but it will do for now. In FoldingPaneBehavior we will first need to add a method to recursively find a parent with a size:

protected VisualElement GetParentView()
{
  var parent = associatedObject as Element;
  VisualElement parentView = null;
  if (parent != null)
  {
    do
    {
      parent = parent.Parent;
      parentView = parent as VisualElement;
    } 
    while (parentView?.Width <= 0 && parent.Parent != null);
  }

  return parentView;
}
And the first line of the Init method neededs to be changed from
var p = associatedObject.ParentView;
to
var p = GetParentView();

And then the behavior will work again.

A second surprise when upgrading to Xamarin Forms 2.1 is that the declaration of Dependency properties using generics is deprecated. It will still work, but not for long. So in stead of

public static readonly BindableProperty IsPopupVisibleProperty =
       BindableProperty.Create<FoldingPaneBehavior, bool>(t => t.IsPopupVisible,
       default(bool), BindingMode.OneWay,
       propertyChanged: OnIsPopupVisibleChanged);
You will know have to use
public static readonly BindableProperty IsPopupVisibleProperty =
       BindableProperty.Create(nameof(IsPopupVisible), typeof(bool), 
       typeof(FoldingPaneBehavior),
       default(bool), BindingMode.OneWay,
       propertyChanged: OnIsPopupVisibleChanged);
And the callback loses its type safety because that becomes
private static void OnIsPopupVisibleChanged(BindableObject bindable, object oldValue, object newValue)
in stead of
private static void OnIsPopupVisibleChanged(BindableObject bindable, bool oldValue, object bool)

and thus you have to cast oldValue and newValue to bool. This makes the code inherently more brittle and harder to read, but I assume there is a good reason for this. I have updated the xadp snippet for creating these properties accordingly.

A typical case of moved cheese, but fortunately not too far. The updated demo solution is still on GitHub

23 March 2016

.NET Native, SilverlightSerializer, MakeGenericType and MissingMetadataException

Recently I have ported my app Map Mania to the  Universal Windows Platform, and at the moment of this writing it is being certified. (that, I least I hope). It’s the first UWP app I actually submit to the Store. I have been doing quite some IoT Core stuff and that does not require much submitting. But it had to happen at one time, if only to dogfood my WpWinNl port to UWP, and it was an educating experience.

I learned the hard way that stuff that does work with debug settings, does not necessarily work when you compile stuff ‘for real’. I ran into quite a serious issue with generics. As soon as I turned my app over to the .NET Native toolchain, it even crashed on startup. Mind you, most of the ‘business code’ in this app and large parts of the XAML were still the same as the original Windows Phone7 and 8 app, as was the WpWinNl base library – all ported to UWP, but largely unchanged.

The root cause of the problem turned out to be SilverlightSerializer. This is a serialization framework made by Mike Talbot that I have been using to store app state since 2011. It’s name quite dates it origins. Unfortunately the link to the original article is dead – actually the whole blog seems to have disappeared - but it’s code has been sitting in WpWinNl and it’s predecessors ever since that time. I have ported it to Windows Phone 8, Windows RT, and now to UWP.

Deep down in the core it uses the Type.MakeGenericType method to make a generic type with two type parameters to store types. So if I have ViewModel with a double property, it makes a GetSetGeneric<ViewModel, double>. This, now, does not go down well with .NET Native. Runtime, it will pop up the following error:

Exception thrown: 'System.Reflection.MissingMetadataException' in System.Private.CoreLib.dll

Additional information: Reflection_InsufficientMetadata_EdbNeeded: WpWinNl.Utilities.GetSetGeneric<BeautyOfMaps.Models.TileMapSource,System.Boolean>. For more information, visit http://go.microsoft.com/fwlink/?LinkId=623485

Now, in your app, in  the “Properties” folder, there is a Default.rd.xml file that allows you to instruct the compiler not to ‘optimize things away’, to make sure this code still works. The insidious part is that this code is in my libary, downloaded from NuGet, and since it uses generics there is no ‘'generic’ way for me to instruct the compiler in the libary’s rd.xml not to optimize away code in the app that is using it.

If I wanted to fix this particular problem, I have to add the following line to the app’s rd.xml:

<Type Name="WpWinNl.Utilities.GetSetGeneric{BeautyOfMaps.Models.ViewModel.MainViewModel,System.Boolean}" Dynamic="Required All"/>

And guess what – then you get the next error. Now it it’s complaining about missing

WpWinNl.Utilities.GetSetGeneric<LocalJoost.Maps.TileSources.Google,
LocalJoost.Maps.TileSources.GoogleType>

So it apparently needs a line like this for every class type and property type combination that goes past MakeGenericType. So while you already defined in code what these properties are, you have to declare (again) in XML which ones you want to keep. I have been in contact with the .NET Native team about the apparent lack of logic behind this, but is seems that according to their date very little people actually use this kind of reflection. While this is true without any doubt – Microsoft telemetry does not lie - I think the critical thinking error that was made here is that those people might be using libraries that do – like mine! - and then they are thoroughly in deep… er, manure.

Trying to add manually all class/property type combinations is where madness lies, my friends. I came to about the 6th type and then I was kind of done. I am a developer, so I am lazy by nature, and in stead of actually doing it manually I added a weird property to SilverlightSerializer called RdXmlEntries. It’s usage is as follows:

  • Run the app in DEBUG (so not .NET Native)
  • Serialize all objects you want to serialize
  • Directly behind the last serialization, add this code:
foreach (var entry in SilverlightSerializer.RdXmlEntries)
{
  Debug.WriteLine(entry);
}
  • Copy and paste the resulting data in your Default.rd.xml
  • Verify your code now runs under .NET Native.

This code is now in WpWinNlBasic version 3.0.5-alpha on NuGet and available for use. You now know why I keep this in the unstable branch – as long as I haven’t had the chance to dogfood all the stuff I have ported and created earlier, I want to thread carefully.

When I showed this solution to the .NET Native team I got feedback that I only needed to provide lines for when the second parameter is a value type, not when it’s a reference type. I have validated that in one case, but I serialize quite a complicated object graph and had spent more than enough time on it already, I did not really feel like experimenting further.

Bottom line – be very, very careful with advanced reflection in UWP apps. Don’t make the same mistake I made – test early under .NET Native and follow guidance provided here to maintain your sanity.

Special thanks to Tom Verhoeff for providing assistance based upon his earlier experience. And sorry, no code sample this time as this is more general guidance that a specific coding issue ;)

28 February 2016

Keeping UI elements above and below the Universal Windows Platform app bars

A little history - or how I messed up

I initially created this behavior  for Windows Phone 8.1, and then ported it to Windows 8.1 store apps. I presume at one point during creating WpWinNl for Universal Windows Platform I found out that whatever I tried back then did not work anymore, and Microsoft seemed to be moving away from the app bar anyway… whatever, I can’t remember. Fact is, the KeepFromBottomBehavior fell by the wayside during the beta times of Windows 10 and I forgot about it. Time moved on, Windows SDKs finalized and the app bar stayed after all… and last week I got an email from Craig Trought who apparently had used this behavior in previous apps and had found out that it was not in the prerelease WpWinNl UWP package, which kind of was a bit unfortunate as he was now trying to make his app run on UWP. Can’t have people being stuck on UWP development because of me dropping a ball, so I took on the challenge to get the behavior to work again. And in the process I discovered that indeed my approach to this solution did not work at all anymore. Apparently the way Microsoft implemented the app bars had changed considerably.I also found out Craig is quite a persistent fellow, who found I had missed a few points in my first two attempts to port it to UWP.

Purpose of the behavior(s)

The idea behind the KeepFromBottomBehavior (and now it’s little brother, KeepFromTopBehavior) is to prevent (parts of) the UI being covered by the app bar, whether it’s in its closed state or being opened. It should also work when the ApplicationView’s DesiredBoundsMode is set to ApplicationViewBoundsMode.UseCoreWindow and take into account that most Windows 10 mobile devices now have a ‘soft keys navigation bar’, that is – there are no separate buttons – either physical or capacitive– on the device anymore, but just a button bar drawn on the  of the screen. To make things more complicated, you can hide this button bar by swiping from bottom to top over it (making the same gesture brings it back, too) to let apps make full use of the screen real estate. Too make a little more clear what I mean, consider these three screen shots of the app in the mobile emulator:

screen1screen2screen3

To the left, you see how the app looks with both app bars closed. In the middle, you see what happens when both app bars are opened – they cover part of the UI. Ugly. The rightmost screenshot shows what happens when the behaviors are applied – key parts of the UI are moved to stay into view.

ApplicationViewBoundsMode? What ApplicationViewBoundsMode?

Usually the  ApplicationView’s DesiredBoundMode is set to  ApplicationViewBoundsMode.UseVisible. In that case, Windows 10 only provides you with the space between the top app bar and the bottom app bar, if those exist – in their closed state. In Windows 10 mobile, if there is no top bar, the area covered by status bar – the thing on top that shows your battery status and stuff like that –  is not available either.

If you, on the other hand, specify ApplicationViewBoundsMode.UseCoreWindow you get all of the screen to draw on, and stuff appears behind app bars, navigation bars and status bars. This mode is best used in moderation, but can be useful if you for instance want to draw a screen filling map (as in my sample) but then it’s your own responsibility to make sure things don’t get covered up. This is one of the use cases of the behavior. The other is to make sure that whenever the app bars get opened, they still don’t cover op part of the UI. To make things extra complicated, it must also support programmatically made changes to the appbar size (from minimal to compact and back) and support navigating back with NavigationCacheMode.Required.

To see how it looks like – more screenshots!

screen4screen5screen6screen7

To the left you see the app with closed app bars, which I intentionally made translucent so you can see what is going on. Although the map nicely covers the whole screen, the rest of the UI looks pretty horrible. The 2nd to left screenshot shows the app with both app bars opened, and that is a complete mess. The 3rd to left shows closed app bars with the behaviors applied, and the rightmost shows both app bars open, also with the behaviors applied. The parts of the UI we want to not be covered by anything, are moved up- and downwards to stay into view. You are welcome ;)

So how does this work?

image

Very differently from the 8.1 era, I can tell you that. Using Microsoft’s new Live Visual Tree explorer (thanks tools team!) I found out something about the innards of the Windows 10 command bars – inside it there’s a popup that appears over the command par when you click the ellipses button. So if the app bar opens, we  only need to move the stuff we want to keep into view a number of pixels equal to the height of the popup minus the height of the app bar’s closed height. That works fine for the ApplicationViewBoundsMode.UseVisible mode, but for the ApplicationViewBoundsMode.UseCoreWindow things are quite a bit more complicated. The base setup of KeepFromBottomBehavior is as follows:

public class KeepFromBottomBehavior : Behavior<FrameworkElement>
{
  protected override void OnAttached()
  {
    if (!Initialize())
    {
      AssociatedObject.Loaded += AssociatedObjectLoaded;
    }
    base.OnAttached();
  }

  private void AssociatedObjectLoaded(object sender, RoutedEventArgs e)
  {
    AssociatedObject.Loaded -= AssociatedObjectLoaded;
    Initialize();
  }

  private bool Initialize()
  {
    var page = AssociatedObject.GetVisualAncestors().OfType<Page>().FirstOrDefault();
    if (page != null)
    {
      AppBar = GetAppBar(page);
      if (AppBar != null)
      {
        OriginalMargin = AssociatedObject.Margin;

        AppBar.Opened += AppBarManipulated;
        AppBar.Closed += AppBarManipulated;
        AppBar.SizeChanged += AppBarSizeChanged;
        UpdateMargin();
        ApplicationView.GetForCurrentView().VisibleBoundsChanged += VisibleBoundsChanged;
        return true;
      }
    }
    return false;
  }

  protected AppBar AppBar { get; private set; }

  protected Thickness OriginalMargin { get; private set; }

  protected virtual AppBar GetAppBar(Page page)
  {
    return page.BottomAppBar;
  }
}

In the AssociatedObjectLoaded I collect the things I am interested in:

  • The current bottom margin of the object that needs to be kept in view
  • The app bar that we are going to watch – if it’s being opened, closed, or changes size we need to act
  • Whether the visible bounds of the current view have changed. This is especially interesting when a phone is being rotated or the navigation bar is being dismissed.

The fact that the app bar is being collected by and overrideable method is to make a child class for keeping stuff from the top more easy. There is more noteworthy stuff: I try to initialize from OnAttached first, then from OnLoading is that fails. This is because if you use NavigationCacheMode.Required, the first time you hit the page OnAttached is called, but the visual tree is not read, so I have to call from OnLoaded. If you move away, then navigate back, OnLoaded is not called, only OnAttached. Navigate away and back again, then both events are called. So it’s “you never know, just try both”. Note also, by the way, the use of GetVisualAncestors – that’s a WpWinNl extension method, not part of the standard UWP API. It finds the page on which this behavior is located, and from that, the app bar we are interested in. So if one of the events we subscribed to fire, we need to recalculate the bottom margin, which is exactly as you see in code:

private void AppBarSizeChanged(object sender, SizeChangedEventArgs e)
{
  UpdateMargin();
}

private void VisibleBoundsChanged(ApplicationView sender, object args)
{
  UpdateMargin();
}

void AppBarManipulated(object sender, object e)
{
  UpdateMargin();
}

private void UpdateMargin()
{
  AssociatedObject.Margin = GetNewMargin();
}

And then we come to the heart of the matter. First we see the simple method GetDeltaMargin, that indeed calculates the difference in height between an opened and closed app bar:

protected double GetDeltaMargin()
{
  var popup = AppBar.GetVisualDescendents().OfType<Popup>().First();
  return popup.ActualHeight - AppBar.ActualHeight;
}

And then comes the real number trickery.

protected virtual Thickness GetNewMargin()
{
  var currentMargin = AssociatedObject.Margin;
  var baseMargin = 0.0;
  if (ApplicationView.GetForCurrentView().DesiredBoundsMode == 
       ApplicationViewBoundsMode.UseCoreWindow)
  {
    var visibleBounds = ApplicationView.GetForCurrentView().VisibleBounds;
    baseHeight = CoreApplication.GetCurrentView().CoreWindow.Bounds.Height - 
                   visibleBounds.Height + AppBar.ActualHeight;

    if(AnalyticsInfo.VersionInfo.DeviceFamily == "Windows.Mobile")
    {
      baseMargin -= visibleBounds.Top;
    }
  }

  return new Thickness(currentMargin.Left, currentMargin.Top, currentMargin.Right,
                          OriginalMargin.Bottom + 
                            (AppBar.IsOpen ? GetDeltaMargin() + baseMargin : baseMargin));

}

Right. If the simple flow is followed ( that is, for ApplicationViewBoundsMode.UseVisible), we simply need to add the difference in height of a closed and an opened app bar to the bottom margin. On the other hand, if ApplicationViewBoundsMode.UseCoreWindow was used, we need to calculate the base margin of the element we need to keep above the app bar, whether this bar is closed or not, for Windows doesn’t take care of that for us now. You wanted the whole window, you get the whole window. So the base margin is the whole height of the AppBar itself plus the difference between the windows height and the visible height -  that is, the height of that portion of the windows that would be used if we were just letting Windows take care of things. In addition, if we are on Windows 10 mobile, we have to subtract the visible top to take care of the status bar.

Also very important – the cleaning up if we leave the page! After all, we might return to a cached version of it! So all events are detached, and the original margin of the UI element handled by this behavior is restored.

protected override void OnDetaching()
{
  AppBar.Opened -= AppBarManipulated;
  AppBar.Closed -= AppBarManipulated;
  AppBar.SizeChanged -= AppBarSizeChanged;
  ApplicationView.GetForCurrentView().VisibleBoundsChanged -= VisibleBoundsChanged;
  ResetMargin();
  base.OnDetaching();
}

private void ResetMargin()
{
  AssociatedObject.Margin = OriginalMargin;
}

KeepFromTopBehavior

Basically we only have to override GetAppBar, GetOrginalMargin and of course GetNewMargin. This is a tiny bit simpler than KeepFromBottomBehavior – as we are setting the top we can now directly relate to the top margin differences, and don’t need to worry about differences between Windows 10 and Windows 10 mobile:

public class KeepFromTopBehavior : KeepFromBottomBehavior
{
  protected override Thickness GetNewMargin()
  {
    var currentMargin = AssociatedObject.Margin;
    var baseMargin = 0.0;
    if (ApplicationView.GetForCurrentView().DesiredBoundsMode ==
       ApplicationViewBoundsMode.UseCoreWindow)
    {
      var visibleBounds = ApplicationView.GetForCurrentView().VisibleBounds;
      baseMargin = visibleBounds.Top - 
        CoreApplication.GetCurrentView().CoreWindow.Bounds.Top + AppBar.ActualHeight;
    }
    return new Thickness(currentMargin.Left,
      OriginalMargin.Top + 
       (AppBar.IsOpen ? GetDeltaMargin() + baseMargin : baseMargin), 
        currentMargin.Right, currentMargin.Bottom);
  }

  protected override AppBar GetAppBar(Page page)
  {
    return page.TopAppBar;
  }
}

Concluding remarks

This is the first time I actually had to resort to checking for device family for UI purposes; the U in UWP makes application development so universal indeed that these kinds of things usually get abstracted pretty much away. As always, you can have a look at the sample solution, which is still the same as in the Windows 8.1 article, only I have added a UWP project. If you want to observe the difference between ApplicationViewBoundsMode.UseVisible and ApplicationViewBoundsMode.UseCoreWindows yourself, go to the App.Xaml.cs and uncomment the line that sets UseCoreWindow (line 68).

Oh by the way, this all works on Windows 10 desktop of course as well but making screenshots for mobile is a bit easier. The code for this is also in WpWinNl on Github already (be sure to head for the UWP branch), but I am still dogfooding the rest of the package so that is still not released as a NuGet package.

I want to thank Craig for being such a thorough tester – although he excused himself for being “such a pain” I would love to have more behaviors being put through the grinder like this, it really improves code quality.