06 February 2016

An AdaptiveTrigger that works with StateTrigger inside WindowsStateTriggers’ CompositeStateTrigger

Intro

Still working on porting Map Mania to the Universal Windows Platform, I ran into a bit of a snag. It wanted to have a kind of decision-tree Visual State

  • In wide screen, show the UI elements next to each other
  • In narrow screen, show the UI element on top of each other and allow the user to determine which one to show with a kind of menu

And being a stubborn b*st*rd, I felt I should be able to solve by using the Visual State Manager and Adaptive Triggers only, with three Visual States.

  • WideState
  • NarrowState_Red
  • NarrowState_Green

And of course, using MVVM. There is a StateTrigger, which you can bind to a boolean, that will activate upon that boolean being true – using something like a BoolInvertConverter, that will nicely do as flip-flop switch. There is also the well-known AdaptiveTrigger. So for both the narrow state, I can add a StateTrigger and an AdaptiveTrigger. I envisioned something like this:

<VisualState x:Name="NarrowState_Red">
  <VisualState.StateTriggers>
    <AdaptiveTrigger MinWindowWidth="0"></AdaptiveTrigger>
    <StateTrigger IsActive="{x:Bind ViewModel.TabDisplay, Mode=OneWay}"/>
  </VisualState.StateTriggers>
  <VisualState.Setters>
    <!-- -->
  </VisualState.Setters>
</VisualState>

<VisualState x:Name="NarrowState_Green">
  <VisualState.StateTriggers>
    <AdaptiveTrigger  MinWindowWidth="0" ></AdaptiveTrigger>
    <StateTrigger IsActive="{x:Bind ViewModel.TabDisplay, Mode=OneWay, 
         Converter={StaticResource BoolInvertConverter}}"/>
  </VisualState.StateTriggers>
  <VisualState.Setters>
    <!-- -->
  </VisualState.Setters>
</VisualState>

<VisualState x:Name="WideState">
  <VisualState.StateTriggers>
    <AdaptiveTrigger MinWindowWidth="700" ></AdaptiveTrigger>
  </VisualState.StateTriggers>
    <VisualState.Setters>
    <!-- -->
  </VisualState.Setters>
</VisualState>

Unfortunately, that will activate the state if either of one triggers is true. So even in Wide state, I still get one of the Narrow states (and/or the Visual State Manager went belly-up), depending on whether TabDisplay is true or not. I needed something to fire only if all of the triggers are true. Enter…

WindowsStateTriggers to the rescue (sort of)

My smart fellow MVP Morten Nielsen has created a GitHub repo (as well as a NuGet package) containing all kinds of very useful triggers. One of the most awesome is CompositeStateTrigger, which allows you to put a number of triggers inside it, and have them evaluated as one. The default behavior of CompositeStateTrigger is to only fire when all of the triggers inside it are true – exactly what I need! Then I had a bit of a setback discovering the default Microsoft AdaptiveTrigger cannot be used inside the CompositeStateTrigger, but fortunately there was a pull request by one Tibor Tóth in October 2015 that provided an implementation that could. So happy as a clam I pulled all the stuff in, changed my Visual State Manager to this:

<VisualState x:Name="NarrowState_Red">
  <VisualState.StateTriggers>
    <windowsStateTriggers:CompositeStateTrigger>
      <windowsStateTriggers:AdaptiveTrigger  MinWindowWidth="0" />
      <StateTrigger IsActive="{x:Bind ViewModel.TabDisplay, Mode=OneWay}"/>
    </windowsStateTriggers:CompositeStateTrigger>
  </VisualState.StateTriggers>
  <VisualState.Setters>
    <!-- -->
  </VisualState.Setters>
</VisualState>

<VisualState x:Name="NarrowState_Green">
  <VisualState.StateTriggers>
    <windowsStateTriggers:CompositeStateTrigger>
      <windowsStateTriggers:AdaptiveTrigger  MinWindowWidth="0" />
      <StateTrigger IsActive="{x:Bind ViewModel.TabDisplay, Mode=OneWay, 
          Converter={StaticResource BoolInvertConverter}}"></StateTrigger>
    </windowsStateTriggers:CompositeStateTrigger>
  </VisualState.StateTriggers>
  <VisualState.Setters>
    <!-- -->
  </VisualState.Setters>
</VisualState>

<VisualState x:Name="WideState">
  <VisualState.StateTriggers>
    <windowsStateTriggers:AdaptiveTrigger MinWindowWidth="700"/>
  </VisualState.StateTriggers>
  <VisualState.Setters>
    <!-- -->
  </VisualState.Setters>
</VisualState>

And got this – only both Narrow states showed.

And then I felt a bit like this

And about the same age too.

Investigating my issue

The WindowsStateTriggers AdaptiveTrigger is a child class of the default AdaptiveTrigger, and I think the issue boils down to these lines:

private void OnCoreWindowOnSizeChanged(CoreWindow sender, 
  WindowSizeChangedEventArgs args)
{
  IsActive = args.Size.Height >= MinWindowHeight && 
             args.Size.Width >= MinWindowWidth;
}

private void OnMinWindowHeightPropertyChanged(DependencyObject sender, 
  DependencyProperty dp)
{
  var window = CoreApplication.GetCurrentView()?.CoreWindow;
  if (window != null)
  {
    IsActive = window.Bounds.Height >= MinWindowHeight;
  }
}

private void OnMinWindowWidthPropertyChanged(DependencyObject sender, 
  DependencyProperty dp)
{
  var window = CoreApplication.GetCurrentView()?.CoreWindow;
  if (window != null)
  {
    IsActive = window.Bounds.Width >= MinWindowWidth;
  }
}

If you have 4 triggers, for screen sizes 0, 340, 500 and 700, and a screen size of 600 – then the three triggers for 0, 340 and 500 will set IsActive to true, three states will be valid at once, your Visual State Manager barfs and nothing happens. In fact, there is no way in Hades the 0 width trigger will not fire, as there are no ways to make a negative sized active element that I am aware of. I don’t know what Microsoft have done with their implementation, but apparently it ‘magically’ finds sibling-triggers inside the Visual State Manager and proceeds to fire only the one that has the highest value smaller than the screen size, and not all those with lower values. Ironically – by making this AdaptiveTrigger trigger a child class of the Microsoft AdaptiveTrigger, it will function perfectly – as long as you don’t put it into a CompositeTrigger, so it can do the ‘Microsoft Magic’. It’s an insidious trap, and it took me the better part of a day to find out why the hell this was not working.

Now what?

The key thing to understand – and what took quite some for me to have the penny dropped - is that the Visual State Manager will only work if always, under any circumstances, one and only one state is true. Therefore, one and only one set of triggers may be true. So, based upon what I learned from looking at Tibor’s code, I made my own AdaptiveTrigger, which is unfortunately not so elegant.

To a working/workable AdaptiveTrigger

As I don’t have any clue as to how Microsoft does the “looking-up-sibling-triggers-magic”, I have made two major changes to the WindowsStateTrigger’s AdaptiveTrigger:

  1. I did not make a it a child class from the Microsoft AdaptiveTrigger (but from WindowsStateTrigger’s StateTriggerBase)
  2. Next to the properties the Microsoft AdaptiveTrigger has – MinWindowHeight en MinWindowWidth – I added two new properties, MaxWindowHeight and MaxWindowWidth

And then added the following code:

public class AdaptiveTrigger : StateTriggerBase, ITriggerValue
{
  public AdaptiveTrigger()
  {
    var window = CoreApplication.GetCurrentView()?.CoreWindow;
    if (window != null)
    {
      var weakEvent = new WeakEventListener<AdaptiveTrigger, CoreWindow, 
                                            WindowSizeChangedEventArgs>(this)
      {
        OnEventAction = (instance, s, e) => OnCoreWindowOnSizeChanged(s, e),
        OnDetachAction = (instance, weakEventListener) 
          => window.SizeChanged -= weakEventListener.OnEvent
      };
      window.SizeChanged += weakEvent.OnEvent;
    }
  }
  private void OnCoreWindowOnSizeChanged(CoreWindow sender, 
    WindowSizeChangedEventArgs args)
  {
    OnCoreWindowOnSizeChanged(args.Size);
  }

  private void OnCoreWindowOnSizeChanged(Size size)
  {
    IsActive = size.Height >= MinWindowHeight && size.Width >= MinWindowWidth &&
               size.Height <= MaxWindowHeight && size.Width <= MaxWindowWidth &&
               MinWindowHeight <= MaxWindowHeight && MinWindowWidth <= MaxWindowWidth;
  }

  private void OnWindowSizePropertyChanged()
  {
    var window = CoreApplication.GetCurrentView()?.CoreWindow;
    if (window != null)
    {
      OnCoreWindowOnSizeChanged(new Size(window.Bounds.Width, window.Bounds.Height));
    }
  }

  private bool _isActive;

  public bool IsActive
  {
    get
    {
      return _isActive;
    }
    private set
    {
      if (_isActive != value)
      {
        _isActive = value;
        SetActive(value);
        IsActiveChanged?.Invoke(this, EventArgs.Empty);
      }
    }
  }

  public event EventHandler IsActiveChanged;
}

Key part of course is the OnCoreWindowOnSizeChanged method, that not only checks for lower, but also upper boundaries. Usage then is as follows:

<VisualState x:Name="NarrowState_Red">
  <VisualState.StateTriggers>
    <windowsStateTriggers:CompositeStateTrigger>
      <wpwinnltriggers:AdaptiveTrigger  MinWindowWidth="0" MaxWindowWidth="699"/>
      <StateTrigger IsActive="{x:Bind ViewModel.TabDisplay, Mode=OneWay}"></StateTrigger>
    </windowsStateTriggers:CompositeStateTrigger>
  </VisualState.StateTriggers>
  <VisualState.Setters>
    <!-- -->
  </VisualState.Setters>
</VisualState>

<VisualState x:Name="NarrowState_Green">
  <VisualState.StateTriggers>
    <windowsStateTriggers:CompositeStateTrigger>
      <wpwinnltriggers:AdaptiveTrigger  MinWindowWidth="0" MaxWindowWidth="699"/>
      <StateTrigger IsActive="{x:Bind ViewModel.TabDisplay, Mode=OneWay, 
      Converter={StaticResource BoolInvertConverter}}"></StateTrigger>
    </windowsStateTriggers:CompositeStateTrigger>
  </VisualState.StateTriggers>
  <VisualState.Setters>
    <!-- -->
  </VisualState.Setters>
</VisualState>

<VisualState x:Name="WideState">
  <VisualState.StateTriggers>
    <wpwinnltriggers:AdaptiveTrigger MinWindowWidth="700" />
  </VisualState.StateTriggers>
  <VisualState.Setters>
    <!-- -->
  </VisualState.Setters>
</VisualState>

Having to watch for both min and max sizes and having them align closely is a bit cumbersome and not so elegant, but it works for my needs – as the first video on this post shows.

Credits

These go first and foremost to Tibor Tóth for making a good first attempt at making a composable AdaptiveTrigger, teaching me about things like CoreApplication windows that I had previously not encountered. Second of course Morten Nielsen himself, for making WindowsStateTriggers in the first place (it’s quite a popular library by the looks of it) and providing some pointers to improve on my initial code.

Some concluding remarks

A demo project, that sports pages with show both mine and Tibor’s trigger and demonstrates the issue I encountered, can be found here. In fact, the video’s displayed in this post are screencasts of that very app. You will also notice WeakEventListener from WindowsStateTriggers being copied in there – this is because inside WindowsStateTriggers the WeakEventListener is not publicly accessible. The fun thing about open source is that, well, it’s open, and it allows things like copying out internal classes and making use of them anyway. There are several ways to do open source – I tend to make open extensible tool boxes, other people make libraries with a surface as small as possible. Both are valid.

In any case, I hope I have given people who want to do some more advanced Visual State Manager magic some things to reach their goal.

30 January 2016

Using a style replacing behavior for making UWP adaptive layouts

Intro

As I was in the process of porting my app Map Mania to the Universal Windows Platform, I of course extensively used Adaptive Triggers. Now that works pretty darn well, unless you have a whole lot of elements that need to be changed. The credits page of my app looks like this

image

that is to say, that is what is looks on a narrow screen. When I have a wider screen, like a Surface Pro, I want it to look like this:

image

The font size of all the texts need to increase. This is rather straightforward, but also a rather cumbersome experience when there are a lot of elements to change. To get this done with the normal Adaptive Triggers in a VisualStateGroup, you have to:

  • Give all elements an x:Name
  • Refer to them by name into your trigger’s setter
  • For every element you have set the change to the font size.

This very quickly becomes a copy & paste experience, especially if you have a lot of elements and states. And of course, when you add a new element (adding new credits, for example translators), you might forget to add it to the VSM, so you have to go back again… Don’t get me wrong, the Adaptive Trigger is an awesome idea, but in some situations it’s a lot of work to get things done. If you have been reading this blog for some time you already know what is coming: my castor oil for all XAML related challenges – a behavior.

Introducing StyleReplaceBehavior

I have created a simple behavior that, in a nutshell, does the following:

  • You supply the StyleReplaceBehavior with the names of styles you want to have ‘observed’. For instance “MySmallStyle”, “MyMediumStyle”, “MyBigStyle”. This is the property ObservedStyles.
  • From the Visual State Manager, you set the behavior’s CurrentState property to the style you want to be activated. For instance, “MyMediumStyle”
  • The StyleReplaceBehavior traverses the whole visual tree from it’s location and finds every element that has an observed style that is not the currently wanted style, and changes it’s style to the wanted style. So following with the example I just used, if finds everything that is “MySmallStyle” or “MyBigStyle” and changes it to “MyMediumStyle”.

Usage example

As usual, I have supplied a demo project, but I will explain in more detail how it works. About halfway down in the MainPage.Xaml you will find the XAML that defines the credits grid:

<Grid x:Name="ContentPanel" Height="Auto" VerticalAlignment="Top" >
  <Grid.RowDefinitions>
    <RowDefinition Height="Auto"/>
    <RowDefinition Height="Auto"/>
    <RowDefinition Height="Auto"/>
    <RowDefinition Height="Auto"/>
    <RowDefinition Height="Auto"/>

  </Grid.RowDefinitions>
  <Grid.ColumnDefinitions>
    <ColumnDefinition Width="Auto"/>
    <ColumnDefinition Width="Auto"/>
  </Grid.ColumnDefinitions>

  <HyperlinkButton Content="Laurent Bugnion" Tag="http://www.twitter.com/lbugnion"
                   Style="{StaticResource CreditHyperlinkButtonStyle}" 
                   Click="HyperlinkButtonClick" />
  <TextBlock Text="MVVMLight" Grid.Row="0" Grid.Column="1 " 
             Style="{StaticResource CreditItemStyle}" />

  <HyperlinkButton Content="Mike Talbot" Tag="http://whydoidoit.com/" Grid.Row="1" 
                   Style="{StaticResource CreditHyperlinkButtonStyle}" 
                   Click="HyperlinkButtonClick"  />
  <TextBlock Text="SilverlightSerializer" Grid.Row="1" Grid.Column="1" 
             Style="{StaticResource CreditItemStyle}" />

  <HyperlinkButton Content="Matteo Pagani" Tag="http://www.twitter.com/qmatteoq" 
                   Grid.Row="2" 
                   Style="{StaticResource CreditHyperlinkButtonStyle}" 
                   Click="HyperlinkButtonClick"  />
  <TextBlock Text="General advice, explanation Template10" Grid.Row="2" Grid.Column="1" 
             Style="{StaticResource CreditItemStyle}" />

  <HyperlinkButton Content="Morten Nielsen" Tag="http://www.twitter.com/dotmorten" 
                   Grid.Row="3" 
                   Style="{StaticResource CreditHyperlinkButtonStyle}" 
                   Click="HyperlinkButtonClick" />
  <TextBlock Text="WindowsStateTriggers" Grid.Row="3" Grid.Column="1" 
             Style="{StaticResource CreditItemStyle}" />

  <HyperlinkButton Content="Microsoft" Tag="http://www.microsoft.com/" Grid.Row="4" 
                   Style="{StaticResource CreditHyperlinkButtonStyle}" 
                   Click="HyperlinkButtonClick"   />
  <TextBlock Text="Windows, Visual Studio & Template10" Grid.Row="4" Grid.Column="1" 
             Style="{StaticResource CreditItemStyle}" />

</Grid>

This may look impressive, but I can assure you it is not – it’s only a bunch of texts and buttons, and the only interesting thing is the usage of CreditHyperlinkButtonStyle and CreditItemStyle. A little but further up, in the page’s resources, you will see the actual styles that are used getting defined:

<Style x:Key="PageTextBaseStyle" TargetType="TextBlock" 
       BasedOn="{StaticResource BaseTextBlockStyle}">
  <Setter Property="FontSize" Value="15"/>
</Style>

<Style x:Key="CreditItemStyle" TargetType="TextBlock" 
       BasedOn="{StaticResource PageTextBaseStyle}" >
  <Setter Property="FontStyle" Value="Italic"/>
  <Setter Property="VerticalAlignment" Value="Center"/>
  <Setter Property="TextWrapping" Value="Wrap"/>
  <Setter Property="Margin" Value="20,5,0,0"/>
</Style>

<Style x:Key="CreditItemStyleBig" TargetType="TextBlock" 
       BasedOn="{StaticResource CreditItemStyle}">
  <Setter Property="FontSize" Value="25"/>
</Style>

<Style x:Key="HyperlinkTextStyle" TargetType="TextBlock"  
       BasedOn="{StaticResource PageTextBaseStyle}" >
</Style>

<Style x:Key="HyperlinkTextStyleBig" TargetType="TextBlock"
       BasedOn="{StaticResource PageTextBaseStyle}" >
  <Setter Property="FontSize" Value="25"/>
</Style>

And basically the only difference between the normal and the –Big styles is that their font size is not 15 but 25.

The behavior – which needs to be attached to the page itself - is used as follows:

<interactivity:Interaction.Behaviors>
  <behaviors:StyleReplaceBehavior x:Name="CreditItemStyler">
    <behaviors:StyleReplaceBehavior.ObservedStyles>
      <behaviors:ObservedStyle Name="CreditItemStyle"></behaviors:ObservedStyle>
      <behaviors:ObservedStyle Name="CreditItemStyleBig"></behaviors:ObservedStyle>
    </behaviors:StyleReplaceBehavior.ObservedStyles>
  </behaviors:StyleReplaceBehavior>

  <behaviors:StyleReplaceBehavior x:Name="HyperlinkTextStyler">
    <behaviors:StyleReplaceBehavior.ObservedStyles>
      <behaviors:ObservedStyle Name="HyperlinkTextStyle"></behaviors:ObservedStyle>
      <behaviors:ObservedStyle Name="HyperlinkTextStyleBig"></behaviors:ObservedStyle>
    </behaviors:StyleReplaceBehavior.ObservedStyles>
  </behaviors:StyleReplaceBehavior>
</interactivity:Interaction.Behaviors>

In fact, you see that there are two, each for every style pair that needs to be changed. Also note the behaviors have names. This we need to be able to address them from the Visual State Manager:

<VisualStateManager.VisualStateGroups>
  <VisualStateGroup x:Name="WindowStates">

    <VisualState x:Name="NarrowState">
      <VisualState.StateTriggers>
        <AdaptiveTrigger MinWindowWidth="0"></AdaptiveTrigger>
      </VisualState.StateTriggers>
      <VisualState.Setters>
        <Setter Target="CreditItemStyler.CurrentStyle" Value="CreditItemStyle"/>
        <Setter Target="HyperlinkTextStyler.CurrentStyle" Value="HyperlinkTextStyle"/>
      </VisualState.Setters>
    </VisualState>

    <VisualState x:Name="WideState">
      <VisualState.StateTriggers>
        <AdaptiveTrigger MinWindowWidth="700"></AdaptiveTrigger>
      </VisualState.StateTriggers>
      <VisualState.Setters>
        <Setter Target="CreditItemStyler.CurrentStyle" Value="CreditItemStyleBig"/>
        <Setter Target="HyperlinkTextStyler.CurrentStyle" Value="HyperlinkTextStyleBig"/>
      </VisualState.Setters>
    </VisualState>
    
  </VisualStateGroup>
</VisualStateManager.VisualStateGroups>

Which is, apart from that it is setting behavior properties in stead of element properties, as bog standard as you can think of. And that is all. An Adaptive Trigger and some settings.

Caveats and limitations

  • The styles you use just must be defined in the page’s resources, it does not look for the resources in the app, or further down the visual tree. If you want to re-use app resources, you will need to define a resource based on an app resource inside the page’s resources. That is basically a local alias.See for example the HyperlinkTextStyle I used.
  • The behavior is not very forgiving. If you use a style that is not applicable to the element you want to change, it will probably hard crash on you. If  you use none-existing styles for the CurrentStyle, it will crash too. It does the job, but it’s not intended to catch all your mistakes. Intentionally, by the way, because it might become silently defunct if you change style names and you forget to edit the behavior’s use.

A little peek behind the curtains

The behavior itself pretty simple – two dependency properties, ObservedStyles and CurrentStyle, but those are not interesting in itself, and one little method that actually does all the work when the behavior initializes, or CurrentStyle changes:

private void ReplaceStyles(string newStyleName)
{
  //Find the current style in the object's resources by name
  var wantedStyle = AssociatedObject.Resources.FirstOrDefault(
      p => p.Key.ToString() == newStyleName && p.Value is Style).Value as Style;

  // Find all other styles observed by this behavior
  var otherStyleNames = ObservedStyles.Where(
      p => p.Name != newStyleName).Select(p => p.Name);
  var otherStyles = AssociatedObject.Resources.Where(
      p => otherStyleNames.Contains(p.Key.ToString())).Select(p => p.Value);

  // Find all the elements having the other styles
  var elementsToStyle = AssociatedObject.GetVisualDescendents().Where(
      p => otherStyles.Contains(p.Style));

  // Style those with the new style
  foreach (var elementToStyle in elementsToStyle)
  {
    elementToStyle.Style = wantedStyle;
  }
}

And since nearly every line has comments to describe it, I will refrain from further detailing, except for one thing – GetVisualDescendents is not a normal method, but an extension method sitting in WpWinNl – the one and only reason I attached my package to it.

Conclusion

I realize this is huge overkill if you have only two or three elements to change, but when it becomes a lot, this may help you to you make your UWP apps even more awesome pretty easily. For me it saved a lot of work. Once again, the link to the demo project, where you can see everything running.

23 January 2016

What themeresources are available for Windows 10 UWP apps?

If you use Blend, and what to color your elements using a system resource, you get a depressing number of resources in the pop-up. So much, that you actually have no idea what you are looking at. image
If you want to see what resources are actually available, go to the following directory:
C:\Program Files (x86)\Windows Kits\10\DesignTime\CommonConfiguration\Neutral\UAP\<framework version>\Generic
So for instance
C:\Program Files (x86)\Windows Kits\10\DesignTime\CommonConfiguration\Neutral\UAP\10.0.10586.0\Generic

You will see two files:
  • generic.xaml
  • themeresources.xaml
Themeresources.xaml actually contains all of the theme resources, colors and whatnot, and generic.xaml the implementation of the various themes (default, high contrast and light).
Of course, once you know that, you can easily find some documentation on that, like this one on MSDN. It was not immediately obvious to me, and I then assume it won’t be for others, so I thought it best to jot this down quickly – if only for future reference.

27 December 2015

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

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

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

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

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

Code used in this post can be found here.

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

12 December 2015

UWP Map data binding with WpWinNlMaps explained

Intro

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

Getting started

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

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

Concepts

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

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

Sample

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

Event handling

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

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

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

Shape drawers

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

Thematic maps - making your own shape drawers

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

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

      //etc
    }
    return shape;
  }
}

Drawer class hierarchy

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

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

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

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

Limitations

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

Sample solutions

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

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

Conclusion

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

09 December 2015

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

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

06 December 2015

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

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