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 ;)