20 April 2014

Windows Phone 8.1–moving your cursor. Don’t panic!

No code today, but a simple how-to. I was caught off-guard by this, a lot of people – including people who I concern to be power users, are pretty much confused by the way you move your cursor around in Windows Phone 8.1. It used to be

  • Long press text
  • Wait for a cursor to pop up above the text
  • Then swipe a little down and move the cursor to the place where you actually want it to be.

Help! Long press does not show the floating cursor anymore. Your cheese has been moved! Now what? Here’s my advice

Keep calm and tap twice

  • Tap a word. That will select it, and show the well-known selection handles – two dots that you can grab and move to increase or decrease the selection
  • Tap the word again. This will deselect it again, place the cursor in front of it, with a single dot-handle below it.
  • Now tap on that dot and move your finger around. The cursor will follow your finger – even up and down.

See below. Left - after one tap, right – after the second tap. I added the red arrow to indicate the dot that I mean

wp_ss_20140420_0001wp_ss_20140420_0002

It’s displayed in your phone’s accent color – mine is currently set to lime green. I find that kind of fitting with spring, don’t you think? ;-). Once you are used to it, it’s really easy. Double-tap, move the cursor. No more long pressing and swiping down.

For those who rather see it explained on video: see below.

How to move your cursor.

02 April 2014

Code sharing strategies between Windows Phone 8.1 and Windows 8.1 with the new Universal Windows apps

With the announcement of the new SDK for Windows Phone Microsoft now really starts to stress the companionship of Windows and Windows Phone, and makes it a lot easier to build apps that run on both platforms. Windows Phone apps now reside under ‘Store Apps’ and although you can still write Windows Phone only apps, it’s pretty clear to me what the preferred way to go for new Windows Phone apps is

image

Two new kinds of Windows Phone apps

Let me make one thing clear: no-one is going to push you. Windows Phone 8.1, like it’s predecessor, will run 8.0 apps fine. You can go on re-using the skillset you have acquired, still using the XAML style you are used to. In fact, Microsoft stresses this point even more by including Windows Phone Silverlight 8.1 apps, which are a bit of a halfway station: your XAML largely stays the same, but it gives you access to the new APIs. Yet I feel the crown jewel of the new SDK is the new Universal Windows app and, in it’s slipstream, the enhanced PCL capabilities. But once again – no-one is forcing you this way. Microsoft are very much about going forward, but also about protecting your existing skills and assets.

One solution, two apps

One thing up front: ‘one app that runs everywhere’ is a station that we still have not reached. You are still making two apps – one for Windows Phone, one for Windows. In this sense, it’s basically the same approach as before where you used code sharing with linked files. That was quite a hassle, and now Visual Studio supports a formal way to share files between projects. This makes maintaining oversight dramatically easier, and it gets rid of the confounded “This document is opened by another project’ dialog too. Plus – and that’s a very big plus – the code you can use on both platforms has become a lot more similar.

Going universal

imageOne warning in advance – if you going for the Universal Windows app, you’re going all the way. It means that for your Windows Phone app in most cases you are basically writing a Windows 8 app - using XAML and APIs that are familiar for Windows Store apps programmers, but not necessarily for Windows Phone developers. If you already have some experience writing Windows Store this won’t be a problem. If you have never done that before, some things may look a bit different.

So, I have created a new blank Universal Windows app and it shows like displayed on the right:

Initially, it only shows the App.Xaml.cs as shared. This has one great advantage already – you can go in and disable the frame rate counter for both apps in one stroke :-):

        protected override void OnLaunched(LaunchActivatedEventArgs e)
        {
#if DEBUG
            if (System.Diagnostics.Debugger.IsAttached)
            {
                //this.DebugSettings.EnableFrameRateCounter = true;
            }
#endif

Switching contexts

If you go through the shared App.Xaml.cs you will notice a few more things: at several places in the file it says #if WINDOWS_PHONE_APP, and I also want to point out this little thing on top, the context switcher.

image

You can set the context switcher to ‘MyNewApp.Windows and you will see the Windows app code greyed out. This way, you can very quickly see which code is executed in which version and which not

image

Sharing code – sharing XAML

So I went to the Windows Store app, opened Blend, added some text and two tool bar buttons:

<Page
    x:Class="MyNewApp.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:MyNewApp"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">
  <Page.BottomAppBar>
    <CommandBar>
      <AppBarButton Icon="Accept" Label="AppBarButton" Click="AppBarButton_Click"/>
      <AppBarButton Icon="Cancel" Label="AppBarButton"/>
    </CommandBar>
  </Page.BottomAppBar>

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
      <TextBlock HorizontalAlignment="Left" 
                 Height="37" Margin="90,53,0,0" TextWrapping="Wrap" 
                 Text="Hello World" 
                 VerticalAlignment="Top" Width="383" FontSize="29.333"/>

    </Grid>
</Page>

And added some code behind.

private async void AppBarButton_Click(object sender, RoutedEventArgs e)
{
  var m = new MessageDialog("Hello world");
  await m.ShowAsync();
}

Now this won’t make for a spectacular app. If you run it, it will basically show:

imageimage

It amaaaaazing, right ;-)? But then I went a bit overboard – I imagemoved MainPage.xaml and imageMainPage.xaml.cs to the Shared project, and removed it from the Windows Phone 8.1 project. Run the Windows Store App again – still works. Run the Windows Phone 8.1 app, and sure enough…

Now this may seem pretty cool – and in fact it is - but it would not be something I would recommend using just like that. Windows Phone is a different beast, the way people use a phone app differs from how they use a Store app on a tablet, and usually you have to think different about how the layout works on a tablet. Case in point – the app bar. Windows Phone has only room for four buttons. The Secondary Commands show up as menu items, not as buttons. The Top bar does not show up at all. So blindly copying a UI from Windows Phone to Windows Store and back is not a smart thing to do. But the fact that it works, is in itself pretty cool.

Sharing code – in a more practical way

In practice, I have found you almost never share whole pages between phone apps and store apps, for a number of reasons:

  • Phone design does not always translate easily to tablet design and vice versa (as pointed out above).
  • For phone, space is a premium, and you don’t want to drag along all kinds of stuff that’s only used on a tablet - think super-high-res stuff, or all kinds of elaborate XAML constructions to accommodate that
  • If you use controls on one platform that simply are not present on the other, or if you use native controls (for example, maps)

To maximize code sharing, you can for instance use partial classes. That works like this: in your code behind MainPage.Xaml.cs you declare the class “partial”.

namespace MyNewApp
{
  /// <summary>
  /// An empty page that can be used on its own or navigated to within a Frame.
  /// </summary>
  public sealed partial class MainPage : Page
  {
    public MainPage()
    {
      this.InitializeComponent();
    }
  }
}
And then in the shared project you create a new file MainPage.Xaml.cs (or MainPage.cs), with just this:
namespace MyNewApp
{
  /// <summary>
  /// An empty page that can be used on its own or navigated to within a Frame.
  /// </summary>
  public sealed partial class MainPage : Page
  {
    public MainPage()
    {
      this.InitializeComponent();
    }
  }
}

And this still gives the same result. Partial classes in shared projects can be pretty powerful. From the partial class in shared code you can call for instance back to methods in the non-shared portion, provided that those called methods are present in both the non-shared portions of the class (so both the MainPage.Xaml.cs). You can even call methods in referenced PCLs. Just keep in mind that in the background it just works like shared files and you can do a lot to minimize duplication.

More ways of sharing

Another way of sharing code is putting most of it in the shared portion – even stuff that’s not present on both platforms – but then using the #ifdef WINDOWS_PHONE_APP and #ifdef WINDOWS_APP directives. This is largely a matter of preference. For smaller classes with little differences I tend to choose this route, for larger classes or code behind files I tend to go for partial classes.When I am working on stuff that is particularly phone-related, I don’t want my code cluttered by Windows code, and vice versa.

A third way of sharing pieces of UI would be creating user controls in shared code. They can be reused within pages that are in itself not shared.

Finally – don’t forget that everything can be shared in the shared images. Apart from XAML and code this includes, for instance:

  • Images
  • Resource files (styles, templates, localization, etc)
  • Data files

And PCL?

In the past, I have never been a fan of PCL, because of their limitations. This has been greatly improved by the new model and I actually start to like it. If you make a PCL for use in Windows 8.1 and Windows Phone 8.1 you can put almost everything in it that can be stuffed in a shared project, including user controls – although of course you can’t do elaborate partial classes callback tricks into non-shared code. This is because a PCL needs to stand on itself. So only everything that is available in both platforms fits in, so for instance almost everything map-related is out (with a few notable and very important exceptions: Geolocation (GPS tracking) and Geofencing (that is now available on Windows Phone too!).

Still – PCL is only useful if you are building stuff that

  • Needs to be reusable over multiple different apps
  • Needs to be usable on both Windows Phone and Windows Store

With this respect, there is not much difference in building platform-specific libraries and distributing them vi NuGet. This is more for the advanced programmer who is starting to build his own toolkit. This means you, by the time you are working on your third app :-)

Code sharing recap

  • Code, XAML or assets that can be shared within one app for both platform can go into a shared project. For code that does not fit entirely you can use
    • Partial classes
    • #ifdef directives
  • Code, XAML or assets that can be shared over multiple apps for both platform can go into a PCL

Conclusion

Build for Both has become a lot easier. You still have to deliver two apps, but making them as one has become a lot easier. Making clever use of the of partial classes and #ifdef makes it easier too, although this requires some thinking. Still, you have to take into account how both platforms behave differently.

A new exiting story for Windows Phone and Windows developers has begun.

The ‘demo solution’, which is hardly worth it’s name in this case, can be downloaded here.

26 February 2014

Drawing circles on Windows Phone Here Maps and Windows Store Bing Maps

One of the areas where the mapping systems for Windows Phone and Windows 8 are lacking, is the ability to draw circles. In particular in Windows Store apps, where you have the ability to use geofences, it would come in pretty handy to be able to draw the outline of a geofence (typically a circle, as this is the only supported type right now) on the map – if only for testing purposes.

To this end I have created a set of extension methods, both for Windows Phone 8 and for Windows Store apps. In the first case, they cannot be used to display the location of a geofence as there are no geofences in Windows Phone 8, but well – drawing circles may come in handy anyway.

The results look something like this

imageScreenshot (16)

These are not circles – they look like circles. Basically they are just normal polygons, but their points are drawn in a form to resemble a circle. Would you zoom in very far, you would actually be able to see this. Would you calculate it’s surface area, you would actually find it  fraction smaller than a real circle area. But what the heck – it serves the purpose.

I have created a few extension methods to get this done. I will start with Windows Phone code - hey, I am not a Windows Phone MVP for nothing, right? ;-) -which starts off with this piece of math:

using System;
using System.Collections.Generic;
using System.Device.Location;

namespace WpWinNl.Utilities
{
  public static class GeoCoordinateExtensions
  {
    public static GeoCoordinate GetAtDistanceBearing(this GeoCoordinate point, 
                                                     double distance, double bearing)
    {
      const double degreesToRadian = Math.PI / 180.0;
      const double radianToDegrees = 180.0 / Math.PI;
      const double earthRadius = 6378137.0;

      var latA = point.Latitude * degreesToRadian;
      var lonA = point.Longitude * degreesToRadian;
      var angularDistance = distance / earthRadius;
      var trueCourse = bearing * degreesToRadian;

      var lat = Math.Asin(
          Math.Sin(latA) * Math.Cos(angularDistance) +
          Math.Cos(latA) * Math.Sin(angularDistance) * Math.Cos(trueCourse));

      var dlon = Math.Atan2(
          Math.Sin(trueCourse) * Math.Sin(angularDistance) * Math.Cos(latA),
          Math.Cos(angularDistance) - Math.Sin(latA) * Math.Sin(lat));

      var lon = ((lonA + dlon + Math.PI) % (Math.PI * 2)) - Math.PI;

      var result = new GeoCoordinate(lat * radianToDegrees, lon * radianToDegrees);

      return result;
    }
  }
}

This is a method that, given a coordinate, a distance in meters, and a bearing (in degrees) gives you another point. So a entering 50 for distance and 180 for bearing would give you a point 50 meters south of your original point. An impressive piece of math, if I may say so. Which I totally stole from here - as I am into maps, not so much into math. Having this, it’s pretty easy to add a second extension method:

public static IList<GeoCoordinate> GetCirclePoints(this GeoCoordinate center, 
                                   double radius, int nrOfPoints = 50)
{
  var angle = 360.0 / nrOfPoints;
  var locations = new List<GeoCoordinate>();
  for (var i = 0; i <= nrOfPoints; i++)
  {
    locations.Add(center.GetAtDistanceBearing(radius, angle * i));
  }
  return locations;
}

It divides the circle of 360 degrees in the number of points you want, and then simply adds points to the shape at the same distance but at every 360/points degrees. The higher the number of points, the more the shape will look like a real circle. Drawing the circle on a Windows Phone map is child’s play now:

void MainPageLoaded(object sender, RoutedEventArgs e)
{
  var location = new GeoCoordinate(52.181427, 5.399780);
  MyMap.Center = location;
  MyMap.ZoomLevel = 16;

  var fill = Colors.Purple;
  var stroke = Colors.Red;
  fill.A = 80;
  stroke.A = 80;
  var circle = new MapPolygon
  {
    StrokeThickness = 2,
    FillColor = fill,
    StrokeColor = stroke,
    StrokeDashed = false,
  };

  foreach( var p in location.GetCirclePoints(150))
  {
    circle.Path.Add(p);
  }

  MyMap.MapElements.Add(circle);
}

This draws a circle of 150 meters around my house. 

For Windows Store, the story is nearly the same. The differences are:

  1. You will need to install the Bing Maps SDK for Windows 8.1 Store apps
  2. The Bing Maps SDK does not understand System.Device.Location.GeoCoordinate you will need to make the extension method on Bing.Maps.Location

It’s very unfortunate that the mapping APIs for Windows Phone and Windows Store lack convergence even at the point of something as basic the naming of location types. I hope this will get more attention in the future, as mapping is something that I really care about - in case you had not noticed that from this blog ;-).

However, clever use of C# aliasing brings this convergence a bit closer. I changed the top of this file to

using System;
using System.Collections.Generic;
#if WINDOWS_PHONE
using GeoCoordinate = System.Device.Location.GeoCoordinate;
#else
using GeoCoordinate=Bing.Maps.Location;
#endif

et voilá, this file compiles under both Windows Phone and Windows Store (provided you have the Map SDK installed). You can forget about PCLs, but sharing code is definitely a possibility now as far as this little method goes.

Drawing a shape in the Windows Store app is now nearly the same:

void MainPageLoaded(object sender, RoutedEventArgs e)
{
  var location = new Location(52.181427, 5.399780);
  MyMap.Center = location;
  MyMap.ZoomLevel = 18;
  var fill = Colors.Purple;
  fill.A = 80;
  var circle = new MapPolygon
  {
   FillColor = fill,
  };

  foreach (var p in location.GetCirclePoints(150))
  {
    circle.Locations.Add(p);
  }

  var layer = new MapShapeLayer();
  layer.Shapes.Add(circle);

  MyMap.ShapeLayers.Add(layer);
}

Although attentive readers might notice there is no stroke color defined (as MapPolygon in Windows Store does not feature a separate outer shape border) and adding shapes to a map regrettably also works a bit differently. The API of both mapping systems definitely could benefit from some attention here. But anyway – it get’s the job done now at this point.

Full demo solution is downloadable here. One more time – to be able to compile this you will need to install the Bing Maps SDK for Windows 8.1 Store apps first!

23 February 2014

A behavior to replace LayoutAwarePage in Windows Store and Windows Phone apps

Sometimes I think up things all by myself, sometimes I have to read something first to let the ole’ light bulb in my brain go ‘ping’. This post is of the 2nd category, and was inspired by this brilliant piece by C# MVP Iris Classon who is – apart from a very smart and respected coder – also a living and shining (not to mention colorful) axe at the root of the unfortunate gender bigotry that is still widespread in the IT world – a lot of people still think seem convinced “women can’t code, if they do they do it badly, and those who do are socially inept, boring and ugly”. Which is demonstrably untrue – coding requires just a brain and determination - but I am convinced it still shies a lot of brain power out of IT.

Anyway – what I basically did was take Iris’ idea an turn it into – you guessed it – a behavior, to make it more reusable.

To demonstrate how it works, I created the following Store app with a very compelling UI:

image

There is only one text of 75pt there. And then I created three Visual States:

  • Default, which does basically nothing
  • Medium, which changes the font size to 50pt
  • Small, which changes the font size to 25pt

How this is done is amply explained by Iris’ post so I don’t feel very compelled to repeat that here. After creating the states I brought in my WpWinNl nuget package, and added my new SizeVisualStateChangeBehavior to the page. Then I added three entries to “SizeMappings” to the behavior by clicking on the indicated ellipses on the right:

image

  • “Default” with width 801
  • “Medium” with width 800
  • “Small” with width 500

From 801 up to infinity the the Default state will be used (showing the text in it’s default size), between 800 and 501 the Medium state will be used (50pt), and from 500 and lower, the Small state (25pt). And voilá, automatic sizing of items done by filling in some boxes in Blend – or typing in some XAML in Visual Studio if that’s more of your thing. Notice you can add any number of Visual States for any range of widths, just as long as there is one “Default” state that has a width that’s one higher than the largest none-default width. Notice SizeVisualStateMappings can have any name you like as well, as long as they correspond with names of Visual States.

For extra credit, by the way, I made this behavior attachable to Control objects rather than Page, so it can be used inside (user) controls as well. And with some #ifdef directives it also works for Windows Phone, which might come in handy with more and more of resolutions entering the Windows Phone arena.

As to how this behavior works, it’s not quite rocket science. First, we have this hugely complex class :-P that holds one SizeVisualStateMapping:

namespace WpWinNl.Behaviors
{
  public class SizeVisualStateMapping
  {
    public string VisualState { get; set; }
    public int Width { get; set; }
  }
}

The actual behavior’s setup almost immediately shows what I am doing – I simply latch on to the SizeChanged event of the object that the behavior is attached to:

using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
#if WINDOWS_PHONE
using System.Windows;
using System.Windows.Controls;
#else
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
#endif

namespace WpWinNl.Behaviors
{
  public class SizeVisualStateChangeBehavior : SafeBehavior<Control>
  {
    protected override void OnSetup()
    {
      AssociatedObject.SizeChanged += AssociatedObjectSizeChanged;
      base.OnSetup();
      UpdateVisualState();
    }
    
    private void AssociatedObjectSizeChanged(object sender, SizeChangedEventArgs e)
    {
      UpdateVisualState();
    }
  }
}
The core of the behavior’s functionality is the UpdateVisualState method:
private void UpdateVisualState()
{
  if (SizeMappings != null)
  {
    SizeVisualStateMapping wantedMapping = null;
    var wantedMappings = 
SizeMappings.Where(p => p.Width >= AssociatedObject.ActualWidth); if (wantedMappings.Any()) { wantedMapping = wantedMappings.OrderBy(p => p.Width).First(); } else { var orderedMappings = SizeMappings.OrderBy(p => p.Width); if (AssociatedObject.ActualWidth < orderedMappings.First().Width) { wantedMapping = orderedMappings.First(); } else if (AssociatedObject.ActualWidth > orderedMappings.Last().Width) { wantedMapping = orderedMappings.Last(); } } if (wantedMapping != null) { VisualStateManager.GoToState(AssociatedObject, wantedMapping.VisualState,
false); } } }

Which simply tries to find a SizeVisualStateMapping that’s fit for the current width of the associated object. If it finds that, it tells the VisualStateManager to go to that state, which proceeds to do the actual work. And that’s basically all. All that's left are an Dependency Property SizeMapping of type List<SizeVisualStateMapping> SizeMappings that holds the actual mapings, and a Cleanup method that detaches the behavior from the SizeChanged event again.

Full details, and a working solution including this behavior, can be found here. If you run this app in split screen and slowly makes it’s window width smaller, you will notice the text getting smaller. Be aware that the change only fires when you actually let the window divider control go – as long as you keep it active (by touching or dragging it) nothing will happen.

18 February 2014

Commercial break 2 – introducing 2 Tablet Pong aka Play On Both

310x310Regular readers might remember the launch of 2 Phone Pong in August 2013, one of the first – if not the very first – action game that could be played on two Windows Phones, first only over NFC, later over Bluetooth. It’s been a moderate success, particularly popular in Brazil and Turkey.

Today I launch 2 Tablet Pong, essentially the same game, for Windows 8.1.

But this release has a little twist – not only can you play against any Windows 8.1 device (provided it supports Wi-FI direct) but also against a Windows Phone running the newest version of 2 Phone Pong, making it possible the first cross-platform action game in town.

The gameplay on two Windows 8.1 devices is exactly the same as on Windows Phone. Here you see my Surface Pro 1 (right) and my Surface 2 (left) being used in the game

Surface versus Surface Pro

This is actually the instruction movie inside the game. As is the next one, that shows how my Lumia 1020 (right) connects to the Surface Pro – which is called “Blue”* on my network

You will need to pair a phone to a Windows 8.1 over Bluetooth first, and then they will be able to find each other. That is to say, the phone finds the PC (after some time), never the other way around. With a PC-to-PC connection it’s a matter of who starts first – the last one always finds the connection.

There were some interesting challenges to the port, but thanks to the extensive use of MVVMLight and my WpWinNl library I was able to use 90% of my code and I think 75% of XAML. I mostly used shared code, sometimes partial classes. There are also some extra rules in the Windows version – the ball speed adapts to the size of the screen, so if you play the game on half a screen, it will only go half as fast, or else it will become unplayable.

The game is free, but will be ad-supported in a soon-to-be-released update. I hope it will enjoy a just as enthusiastic reception as it’s phone predecessor.

I will soon update WpWinNl with everything I learned from porting this game, and I have a lot to blog about – I hope the community can benefit from that. Enjoy!

 

* disclaimer: those who think the name “Blue” has any significance or hints at something – I have a total lack of fantasy as far as device names are concerned, so I have a long standing tradition of naming my PCs after my wife’s pet hamsters. “Blue” is a 26 month old Russian dwarf hamster – she is called that way because that’s the name of the color variety. Go Bing it if you don’t believe me. My next PC will be called Smokey ;)

Build for both–an instruction video pivot page for Windows Phone apps

For Windows Phone apps, the same goes a for Windows 8 store apps – a picture tells more than a thousand words and a video even more. So as promised earlier, I am going to show you how to make a video instruction pivot for Windows Phone, that works as showed here below.

Windows Phone video instruction demo

The Pivot part is easy, as this is already built-in – controlling the MediaElements proved to be substantially harder.

A recap of the objectives:

  • Start the video on the current visible pivot item automatically – and start the first video on the first pivot as soon as the user access the page.
  • Repeat that movie automatically when it ends
  • Stop it as soon as the user selects a different pivot item

Setting the stage

  • Create a blank Windows Phone app
  • Bring in my WpWinNl library from Nuget
  • Add an empty page “Help.xaml”
  • Open WMAppManifest.xml, look for the box “Navigation Page” and change this from “MainPage.xaml” to “Help.xaml”. This will make your app start the Help page on startup. That’s not a smart move for production code but it will make a demo app a lot easier.

The XAML basics

Basically the page (expect for the header) is filled by a StackPanel containing a PivotItem and some styling for (mostly) the MediaElementand the PivotItem. That gives every MediaElement a specific alignment and size that will make it fit just in a portrait-oriented page.

<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,30,0,0">
  <TextBlock x:Name="ApplicationTitle" Text="MY APP" 
             Style="{StaticResource PhoneTextNormalStyle}" Margin="0,0,30,0"/>
  <phone:Pivot x:Name="VideoPivot" Title="How to do stuff" 
               HeaderTemplate="{StaticResource HeaderTemplate}" Margin="-25,0,0,0">
    <phone:Pivot.Resources>
      <Style TargetType="MediaElement">
        <Setter Property="Margin" Value="12,0,0,0"></Setter>
        <Setter Property="VerticalAlignment" Value="Top"></Setter>
        <Setter Property="HorizontalAlignment" Value="Left"></Setter>
        <Setter Property="Width" Value="478"></Setter>
        <Setter Property="Height" Value="268"></Setter>
      </Style>
      <Style TargetType="phone:PivotItem">
        <Setter Property="CacheMode" Value="{x:Null}"></Setter>
      </Style>
    </phone:Pivot.Resources>
    <phone:PivotItem Header="connect via tap+send">
      <MediaElement Source="Video/2PhonePongConnect.mp4" />
    </phone:PivotItem>
  <!-- More PivotItems with video -->
  </phone:Pivot>
</StackPanel>

Also note that the styling turns off CacheMode for the Pivot items. That seems to be necessary. The MediaElements themselves contain, apart from the url to the video, nothing else. The rest is set from code behind.

You might also notice that the Pivot used a HeaderTemplate: that’s fairly simple and just exists to limit the header text size a little:

<DataTemplate x:Key="HeaderTemplate">
   <TextBlock Text="{Binding}" FontSize="35"/>
</DataTemplate>

And again some code to make it work

There is an important difference between the FlipView and the Pivot – in what I think is an effort to conserve memory, not all the pivot panes are loaded into memory at startup: only the selected one (i.e. the first), and the ones left en right of that. So we have no way of retrieving all the MediaElements up front. So we have to gather and attach to those elements as we go along.

There is also an important issue with MediaElement on Windows Phone – it does not like to have multiple initialized MediaElements on one page. So I designed all kind of trickery to make this work. For starters, in order to make this work, we need a dictionary of all the video elements and their sources:

using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;
using Microsoft.Phone.Controls;
using WpWinNl;
using WpWinNl.Utilities;

namespace TwoPhonePong
{
  public partial class Help
  {
    private readonly Dictionary<MediaElement, Uri> sources = 
new Dictionary<MediaElement, Uri>(); } }
The constructor, of course, kicks off the whole process
public Help()
{
  InitializeComponent();
  VideoPivot.Loaded += VideoPivotLoaded;
  VideoPivot.LoadedPivotItem += VideoPivotLoadedPivotItem;
}
Then comes the funky stuff:
private void VideoPivotLoaded(object sender, RoutedEventArgs e)
{
  VideoPivot.GetVisualDescendents().OfType<MediaElement>().
ForEach(StoreMediaElementReference); PlayMovieOnPivotItem((PivotItem)VideoPivot.SelectedItem); } private void StoreMediaElementReference(MediaElement mediaElement) { sources.Add(mediaElement, mediaElement.Source); mediaElement.Source = null; mediaElement.AutoPlay = false;

mediaElement.MediaEnded += MovieMediaElementMediaEnded; mediaElement.MediaOpened += MovieMediaElementMediaEnded; }

I case you are wondering about your sanity (or mine) – don’t worry, what you seems to be reading is correct: VideoPivotLoaded finds all the active MediaElements on the Pivot, an the calls StoreMediaElementReference – which stores the URL of their source in the sources directory and then clears the MediaElement’s Source (basically un-initializing it) and sets AutoPlay to false. And then it add the MovieMediaElementMediaEnded method to both the MediaEnded and MediaOpened events of the MediaElement. It does this – of course – only for the PivotItems that are actually loaded.

In case you think thinks can’t get any weirder than this, wait till you see the PlayMovieOnPivotItem method

private void PlayMovieOnPivotItem(PivotItem e)
{
  var mediaElement = e.GetVisualDescendents().
OfType<MediaElement>().FirstOrDefault(); VideoPivot.GetVisualDescendents().OfType<MediaElement>(). Where(p => p != mediaElement).ForEach( p => { p.Stop(); p.Source = null; }); if (mediaElement != null) { mediaElement.Position = new TimeSpan(0); if (!sources.ContainsKey(mediaElement)) { StoreMediaElementReference(mediaElement); } mediaElement.Source = sources[mediaElement]; }

So, first it tries to find the MediaElement on the current PivotItem. Then it stops all the MediaElements that are not on this Pivot, and clears their source – effectively as I said, uninitializing them. For the MediaElement on the current PivotItem – if it’s not in the sources dictionary (so it was on the fourth or higher PivotItem) it is added to the sources using StoreMediaElementReference. However – the source of the MediaElement – that at this point is null, whether it was in the sources dictionary or not – is now set from that sources dictionary.

If the movie has finished loading the MediaOpened event is fired, and calls the MovieMediaElementMediaEnded method – remember that is was wired up in StoreMediaElementReference?

private void MovieMediaElementMediaEnded(object sender, RoutedEventArgs e)
{
  var mediaElement = sender as MediaElement;
  if (mediaElement != null)
  {
    mediaElement.Position = new TimeSpan(0);
    mediaElement.Play();
  }
}

This method starts video playback it – so as soon as the video loaded, it starts playing. And since this same method is wired up to the MediaEnded event too – this will enable the repeated playback of this video as well. Until it is not by code.

Of course – when a user changes the selected PivotItem by swiping left or right – a new video needs to be started and the other ones stopped. This is pretty easily done now:

private void VideoPivotLoadedPivotItem(object sender, PivotItemEventArgs e)
{
  PlayMovieOnPivotItem(e.Item);
}

This method was wired up to the Pivot’s LoadedPivotItem way up in the constructor of this page.

Your video instruction page now works. Now to prevent memory leaks and especially prevent blocking other MediaElements elsewhere in the application, this method clears all even wiring and the MediaElement’s Source properties

protected override void OnNavigatedFrom(NavigationEventArgs e)
{
  sources.Keys.ForEach(p =>
                       {
                         p.Source = null;
                         p.MediaEnded -= MovieMediaElementMediaEnded;
                         p.MediaOpened -= MovieMediaElementMediaEnded;
                       });
  VideoPivot.Loaded -= VideoPivotLoaded;
  VideoPivot.LoadedPivotItem -= VideoPivotLoadedPivotItem;
  base.OnNavigatedFrom(e);
}

And thus you have created a video instruction page that works on Windows Phone under all circumstances (at least under all circumstances I have encountered).

Enjoy – and find the demo solution here

14 February 2014

Build for both–an instruction video pivot page for Windows store apps

For my game 2 Phone Pong – and its Windows 8.1 counterpart 2 Tablet Pong, that at the time of this writing is going through some interesting certification challenges  – I created a page with video instructions. I did not feel much like writing a lot of text on how to connect the apps and play the game – a short video usually tells so much more. That proved to be less straightforward than I had hoped, so I thought it a good idea to share how I got it to work.

Both platforms have their own limitations and idiosyncrasies. Windows 8 does not have a Pivot, and Windows Phone makes life pretty difficult when it comes to playing multiple videos on one page. I have solved the first problems by creating FlipViewPanoramaBehavior and I was successfully able to reuse that behavior, although it’s now running on top of the official Windows RT Behavior SDK, no longer using my stopgap project WinRtBehaviors.

In this post I will show you how to build a video page for Windows Store apps – the next one will show you how to do the same for Windows Phone. The Windows version is actually the most simple

When I set out to create the video instruction page I wanted it to do the following:

  • Start the video on the current visible pivot item automatically – and start the first video on the first pivot as soon as the user access the page.
  • Repeat that movie automatically when it ends
  • Stop it as soon as the user selects a different pivot item

This gives the following effect:

Store video instruction page demo

How I got to this, is explained below

Setting the stage

The beginning is pretty simple - it usually is ;-)

  • Created an Blank XAML store app
  • Bring in my WpWinNl library from Nuget
  • Add an empty page “Help.xaml”
  • Go to App.xaml.cs and change rootFrame.Navigate(typeof(MainPage), e.Arguments); to rootFrame.Navigate(typeof(Help), e.Arguments);
  • Bring in a couple of video’s you want to use. I took three of my actual instruction videos from 2 Tablet Pong.

The XAML basics

The page contains the usual stuff for a nice page header, but as far as the video instruction part is concerned, it only contains the following things:

  • A FlipView with some (very minor) styling for the header text
  • My FlipViewPanoramaBehavior attached to it
  • A few FlipViewItems, each containing a grid with a header and a MediaElement with the Video in it

That looks more or less like this:

<FlipView x:Name="VideoFlipView"  >
  <FlipView.Resources>
    <Style TargetType="TextBlock">
      <Setter Property="FontSize" Value="30"></Setter>
    </Style>
  </FlipView.Resources>
  <interactivity:Interaction.Behaviors>
    <behaviors:FlipViewPanoramaBehavior/>
  </interactivity:Interaction.Behaviors>
  <FlipViewItem >
    <Grid>
      <Grid.RowDefinitions>
        <RowDefinition Height="40"></RowDefinition>
        <RowDefinition Height="Auto"></RowDefinition>
      </Grid.RowDefinitions>
      <TextBlock Text="Connect via WiFi" ></TextBlock>
      <MediaElement x:Name="FirstMovieMediaElement" 
        Source="Video/ConnectWiFi.mp4" Grid.Row="1" >
       </MediaElement >
    </Grid>
  </FlipViewItem>
  <!-- More FlipViewItems with video -->
</FlipView>

Important to see is that both the FlipView and the first FlipViewItem have a name, this is because we need to reference it form the page code.

Some code to make it work

First order or business is getting this thing on the road, so we need to define some starter events in the constructor:

using System.Linq;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
using WpWinNl;
using WpWinNl.Utilities;

namespace Win8VideoPivot
{
  public sealed partial class Help : Page
  {
    public Help()
    {
      InitializeComponent();
      VideoFlipView.SelectionChanged += SelectedIndexChanged;
      FirstMovieMediaElement.MediaOpened += 
        FirstMovieMediaElementMediaOpened;
    }
  }
}

Next up is a little helper property that give us a short cut to all the MediaElements on the page, using a helper method from WpWinNl:

private IEnumerable<MediaElement> AllMediaElements
{
  get
  {
    return VideoFlipView.GetVisualDescendents().OfType<MediaElement>();
  }
}
The main method of this page is StartMovieOnSelectedFlipViewItem - that finds the MediaElement on the current selected FlipViewItem, stops MediaElements on all the other FlipViewItems, and kicks off the current one to play it's movie:
private void StartMovieOnSelectedFlipViewItem()
{
  var pivotItem = (FlipViewItem)VideoFlipView.SelectedItem;
  var mediaItem = pivotItem.GetVisualDescendents().OfType<MediaElement>().FirstOrDefault();
  AllMediaElements.Where(p => p != mediaItem).ForEach(p => p.Stop());

  if (mediaItem != null)
  {
    mediaItem.Play();
  }
}

In the constructor we wired up FirstMovieMediaElementMediaOpened, to be fired when the first MediaElement has opened it's media file. It does, as you would expect, start the first movie file by simply calling StartMovieOnSelectedFlipViewItem  What it also does is setting all the MediaElement’s AutoPlay properties to false, and attach a method to their MediaEnded property, so that a movie is automatically restarted again when it ends.

private void FirstMovieMediaElementMediaOpened(object sender, RoutedEventArgs e)
{
  AllMediaElements.ForEach(p =>
  {
    p.MediaEnded += MovieMediaElementMediaEnded;
    p.AutoPlay = false;
  });
  StartMovieOnSelectedFlipViewItem();
}

private void MovieMediaElementMediaEnded(object sender, RoutedEventArgs e)
{
  ((MediaElement)sender).Play();
}

This might seem a mighty odd place to do it here, and not in the constructor – but I have found that it simply does not have the desired effect when I placed this code in a constructor. A typical ‘yeah whatever’ thing.

The only thing that is now missing is SelectedIndexChanged, that we also wired up in the constructor to be executed when a new FlipViewItem is selected:

private void SelectedIndexChanged(object sender, SelectionChangedEventArgs e)
{
  StartMovieOnSelectedFlipViewItem();
}

It simply restarts a movie that ends all by itself. Note – this is not fired when a movie is stopped by code.

And for good measure, to make sure we don’t introduce all kinds of memory leaks, we detach all the events again in the OnNavigatedTo

protected override void OnNavigatedFrom(NavigationEventArgs e)
{
  base.OnNavigatedFrom(e);
  AllMediaElements.ForEach(p =>
  {
    p.MediaEnded -= MovieMediaElementMediaEnded;
  });
  VideoFlipView.SelectionChanged -= SelectedIndexChanged;
  FirstMovieMediaElement.MediaOpened -= FirstMovieMediaElementMediaOpened;
}

That’s all there is to it – you will find that most of the work will actually go into making videos that are good enough to convey the minimal message without enormously blowing up the size of your app. The code itself, as you see, is pretty easy.

Full demo solution can be found here.

Oh by the way – I did not suddenly do a 180 of MVVM versus code behind – this is just pure view stuff, no business logic needed to drive it, so it’s OK to use code behind in this case.