18 February 2014

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.

11 February 2014

Drawing translucent shapes and lines on a Windows Phone 8 Map

Shapes1For those who think the MVP title comes with divine wisdom, I have a disappointing announcement to make: it does not. Recently I discovered (or actually was told) something pretty elementary that I missed all that time: how to draw more or less translucent lines or shapes on a Windows Phone 8 map.

To the right you see a few shapes that I draw on the screen during my 3rd WpDevFusion Mapping session demo. The basic piece for such a shape is very simple:

var line = new MapPolygon
{
  StrokeThickness = 2,
  FillColor=Colors.Purple,
  StrokeColor = Colors.Red,
  StrokeDashed = false
};

Shapes2But what we want is more or less translucent shapes. That turns out to be insanely easy:

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

A Color has four elements : R(ed), G(reen), B(lue) and A(lpha). The last ones determines the opacity of XAML elements, but apparently also that of map shapes. Both fill and stroke are now about 80/255th (or 31%) opaque – or 69% translucent.

And that’s all there is to it. If you don’t mind, I will leave the demo solution for as exercise for the reader this time ;)

01 February 2014

Build for both–a very simple wp8 style app bar hint control for Windows Store apps

Windows Phone users are very familiar with the concept of an app bar at the bottom that, in collapsed state, gives a kind of indication that it’s available. In Windows Store apps app bars are just invisible, unless you specifically activate them by swiping in from the top or the bottom. There have been earlier attempts to solve this UI difference by my fellow Dutch MVP Fons Sonnemans who created the Peeking App Bar. Microsoft itself has apparently become aware of the fact default invisible app bars may not always be ideal, and has created a kind of a hint of an app bar that very much looks like they had some inspiration from Windows Phone – which is now used in the Windows Mail app:

image

Now there are already solutions out there, most notably this one by recently appointed MVP Dave Smits. They work indeed. But I like to make things simple, so I made a very simple. So in a typical programmer fashion, I rolled my own :-)

So I created my HintBar control, which is actually laughably simple. The XAML is only this, and most of it is declaration too:

<UserControl
    x:Class="WpWinNl.Controls.HintBar"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="14"
    d:DesignWidth="400" x:Name="ControlRoot">

  <Grid x:Name="HintBarGrid" 
        Background="{Binding Background, ElementName=ControlRoot}" 
        Tapped="HintBarGridTapped">
    <TextBlock  x:Name="HintBarText" Text="&#xE10C" 
                FontFamily="Segoe UI Symbol" FontSize="21.333" 
                HorizontalAlignment="Right"  RenderTransformOrigin="0.5,0.5" 
                VerticalAlignment="Center" Margin="0,0,50,0"/>
  </Grid>
</UserControl>

So it’s basically a simple grid that does something when the user taps on it. In the grid there’s only a TextBlock with one ‘glyph’ in it – using the special Segoe UI Symbol character set that Microsoft uses to store all kinds of symbols in. &#xE10C is simply the code for a symbol containing three dots.

The code is not very complex either. It’s basically two attached dependency properties:

  • AssociatedCommandBar, to which you can bind the CommandBar to open when the hint bar is tapped
  • ForeGround, which you can use to change the color of the three dots

If you don’t understand what attached dependency properties are: they are basically the property equivalent of extension methods, and what’s more important – you can data bind to them.

So the AssociatedCommandBar looks like this. It does nothing special

#region Attached Dependency Property AssociatedCommandBar
public static readonly DependencyProperty AssociatedCommandBarProperty =
	 DependencyProperty.RegisterAttached("AssociatedCommandBar",
	 typeof(CommandBar),
	 typeof(HintBar),
	 new PropertyMetadata(default(CommandBar)));

// Called when Property is retrieved
public static CommandBar GetAssociatedCommandBar(DependencyObject obj)
{
  return obj.GetValue(AssociatedCommandBarProperty) as CommandBar;
}

// Called when Property is set
public static void SetAssociatedCommandBar(
   DependencyObject obj,
   CommandBar value)
{
  obj.SetValue(AssociatedCommandBarProperty, value);
}
#endregion

The other property is only a brush defining the text color, and the only thing it does is transferring its value to the text:

#region Attached Dependency Property ForeGround
public static readonly DependencyProperty ForeGroundProperty =
	 DependencyProperty.RegisterAttached("ForeGround",
	 typeof(Brush),
	 typeof(HintBar),
	 new PropertyMetadata(default(Brush), ForeGroundChanged));

// Called when Property is retrieved
public static Brush GetForeGround(DependencyObject obj)
{
  return obj.GetValue(ForeGroundProperty) as Brush;
}

// Called when Property is set
public static void SetForeGround(
   DependencyObject obj,
   Brush value)
{
  obj.SetValue(ForeGroundProperty, value);
}

// Called when property is changed
private static void ForeGroundChanged(
 object sender,
 DependencyPropertyChangedEventArgs args)
{
  var thisObject = sender as HintBar;
  if (thisObject != null)
  {
    thisObject.HintBarText.Foreground = args.NewValue as Brush;
  }
}
#endregion

And the rest of the ‘real code’ of the control is just this:

using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;

namespace WpWinNl.Controls
{
  public sealed partial class HintBar : UserControl
  {
    public HintBar()
    {
      InitializeComponent();
    }

    private void HintBarGridTapped(object sender, 
	                           TappedRoutedEventArgs e)
    {
      var commandBar = GetAssociatedCommandBar(this);
      if (commandBar != null)
      {
        commandBar.IsOpen = true;
      }
    }
  }
}

Not exactly rocket science, right? You tap the grid, the command bar opens. :-)

Using it is pretty simple too. You have a page with a command bar:

<Page
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:controls="using:WpWinNl.Controls"
    x:Class="HintBarDemo.MainPage"
    mc:Ignorable="d" VerticalAlignment="Bottom">

  <Page.BottomAppBar >
    <CommandBar x:Name="BottomBar"  Background="Blue">
    <!-- Content omitted -->
    </CommandBar>
  </Page.BottomAppBar>

  <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

    <controls:HintBar Background ="Blue" Height="14" VerticalAlignment="Bottom"
     	   AssociatedCommandBar="{Binding ElementName=BottomBar}"/>

  </Grid>
</Page>

Give the command bar a name and bind it to AssociatedCommandBar property of the control. Then it’s just a matter of setting it to the bottom of the page, setting it to the desired height, and choosing a color for your bar. That is all. Life is sometimes very easy.

As always, the demo solution can be found here.

11 December 2013

Build for both–introducing WpWinNl

Intro

As I alluded to in a few previous posts, I have been working on a merged version of both my Windows Phone and Windows 8 libraries on Codeplex: wp7nl and win8nl. I have now come to the point that I think it is ready for use and/or some community feedback, so I just released WpWinNl to CodePlex and NuGet. The first ‘good’ version is 1.0.1, immediately superseded by 1.0.3 containing the new cross platform DevicePairConnectHelper.

I’ve cut some old stuff that merely introduced dependencies and that no-one used anyway, and I ported stuff that was missing in Win8nl from Windows Phone and back. Provided it makea sense – since there is no FlipView in Windows Phone, I did not port the FlipViewPanoramaBehavior

People may be shocked to see that it is mainly a Windows 8.1 project, and that the Windows Phone project contains mainly links. No, I am not trying leave Windows Phone – I just found out that most Windows 8 code compiles unchanged on Windows Phone, but not the other way around. See the things that I ran into when I was porting reflection-riddle code – it’s better to bite the bullet on Windows 8, then Windows Phone usually follows suit. The investment the Phone team made moving to a common kernel really pays off that way

It is still a work in progress, as any good library should be.

FAQ

  • What? Are you abandoning Windows Phone 7?
    • Yup. Sorry. Gotta move forward. I push you forward into the new era whether you want it or not ;-) . Seriously, I have enough #ifdef code as it is now. And for those that don’t want to go along – the old library wp7nl still exists, and most of the code will still compile under Windows Phone 7.
  • What? Only support for Windows 8.1, no Windows 8 anymore?
    • Yup. See previous question.
  • Is this fully and thoroughly tested?
    • Nope. This is the tool I needed to make to make cross-platform apps myself. I will probably run into errors. So will you. Contributions and fixes are accepted
  • You are now the only dev registered on the project. Are you taking sole control of the project again?
    • Don’t be silly – if you want to contribute as a developer, just let me know. I just did not get around to adding the people that contributed to the ‘parent projects’.
  • Why only platform specific libraries? Why not PCL?
    • Most of the code I write makes at some point use of the System.Windows or the Windows.UI.Xaml namespace. That does not fit into PCL anyway, so at this point I think the benefits of having PCL do not outweigh the extra hassle of having source code in three potential spots.

Enjoy!

09 December 2013

Understanding and preventing Multilingual App Toolkit ‘lost translations’

Update: the MAT was updated on February 7, 2014, adding new capabilities to handle this issue better

In the old days, making apps in multiple languages was quite a hassle, maintaining multiple resource files and keeping them in sync. In 2012 came the Multilingual App Toolkit, a brilliant piece of technology that changed everything. Suddenly professional localization tooling landed into the lap of John Developer. The tool has a lot of great capabilities – for instance, it can generate translations automatically using Bing Translator. For short texts this works very well, for longer texts your mileage may vary – but you can request for translation from inside the tool, mail it off to a buddy and later import it again. The machine translation at the very least greatly decreases the work of your translator – he or she can skip the 12 instances of Ok, Cancel, Yes, No and stuff like that.

So why wouldn’t you use this? Well, I have heard rumors of people claiming to have lost translations. After getting translated texts and importing that translations into your app – at some point the imported translations where partly reset back to English. So I know of some people who kind of avoid the MAT. I did not understand what their problem was, until I ran into it myself. Fortunately I have had the honor of actually meeting the MAT Product Manager twice this year (at both MVP Summits), and that gives me kind of an advantage in gaining understanding what was going on. So I will try to explain to you what happens, and how you can prevent yourself from shooting in the foot with MAT.

If you start making a localized app, you start out with just making one resource file (using the aforementioned resource file editor), for the default language – usually US/English. Once your apps is done, you fire up the MAT, and start adding languages. The MAT makes an XLF file for every language you add, and – in a Windows Phone app – automatically generates resx files for that. You can prefill those translations using the machine translation option, and like I said, optionally send it off to someone else to review the translation – using the much more translator-friendly editor than the resx file editor. What’s more, they don’t have to have Visual Studio at all – the MAT can run from Visual Studio but also as a simple stand-alone application.

So when do you ‘lose’ translations? Very simple – if you go back to your original resx file with US/English and change the text in there, upon the next build, MAT will assume the text will need to be re-translated, replaces the changed texts with US/English again and marks them red (“Needs translation”) again. The logic behind this stems from the way Microsoft itself uses the application – at one point the UI gets frozen, and then localization happens. But in the mobile app development world, sometimes people go back to the original text and change that too. In my case, I wasn’t really happy with the original wording, or I changed a comma or something. And bam – gone were 5 translations of a single text. But now I understand what’s going on and why, the problem is already halfway solved. The other half I get around by using the following procedure:

  • Use source control, for example Visualstudio.com. Simply check in your files before you start messing around in your translations and particularly when you want to change your source language texts.
  • imageUse the MAT state feature – you can mark all translations as ‘signed off’ (green)when you are satisfied
  • The net result – if you suddenly see all your XLF files checked out, MAT has done some potential unwanted processing. You can easily find what is actually changed by finding everything that’s marked red. If German texts are now suddenly English, you have probably been messing around with the original texts. If you want to retrieve the texts that were reset, you can still retrieve them from TFS.

For the sake of completeness - if you add a new text, you will more or less see the same result (all XLF files checked out) and suddenly an English text between your translated texts – but that is logical, because it’s a new text that has to be translated, either by machine translation or human translators (again).

Currently there is no way to change the MATs behavior in this respect, but f you follow this procedure and keep in mind the rules that drive the MAT, you can use this awesome tool without shooting yourself in the foot. Maybe the tool will be changed to accommodate the more flexible/chaotic way mobile development goes, but that’s for later. Stay tuned.

Update: as you might have seen, the MAT team just started their own blog on MSDN. More importantly: the first article mentions a uservoice for MAT! I think I know what I am going to add for a suggestion ;-)

04 December 2013

Building for both: a tap+send/Bluetooth connection helper for Windows Phone 8 AND Windows 8.1

On my way to the November 2013 MVP Summit, at Matthijs Hoekstra’s house in Sammamish, and back again in the Netherlands I spent considerable time trying to get my tap+send and Bluetooth connection helper to not only work on Windows 8.1 as well, but also to make it provide cross-platform communication between Windows Phone 8 and Windows 8.1. Unfortunately the MSDN PixPresenter sample suggest you can connect between Windows Phone and Windows 8 by sharing app ids in the PeerFinder.AlternateIdentities property, and so does the MSDN documentation on said property. That way, my friends, lay only mud pies and frustration. I don’t doubt that it will work for tap+send, but as the majority of the current Windows 8.x devices (including Microsoft’s own excellent Surface devices) do not include NFC capabilities, the chances of making a successful connection between a Windows Phone and a random Windows 8 computer using tap+send are very small.

I seem to have a thing with Italians these days – I had a lot of fun with a bunch of them at the Summit, and it was not until this Italian developer called Andrea Boschin pointed me to a recent blog post by him that I found a way out of this. Using this knowledge I adapted DevicePairConnectionHelper once again, so now it’s not only cross platform, but it can also connect between Windows Phone 8 and Windows 8.1. If you are completely new to this subject, I suggest you read the previous two articles about this subject as well.

What it can do

In the demo solution, that sports both a Windows 8.1 Store application and a Windows Phone 8 application, you will find a shared file DevicePairConnectionHelper2.cs containing the updated DevicePairConnectionHelper – now supporting  the following communication paths:

  • Windows Phone 8 to Windows Phone 8 – tap+send
  • Windows Phone 8 to Windows Phone 8 – Bluetooth browsing
  • Windows 8.1 to Windows 8.1 – WifiDirect browsing
  • Windows 8.1 to Windows Phone 8 – Bluetooth.

In theory it should support Windows 8.1 to Windows 8.1 over tap+send as well, but lacking even a single NFC enabled Windows 8.1 device, this is hard to test for me.

How to set it up and use it

A very important precondition– the devices can only find each other when the phone is paired to the Windows 8.1 computer. Now the odd thing is – as you have paired the phone to the computer, you will see that is “connected” on both the computer and the phone, for a very short time. And then it flashes off again, so they are not connected. This is a bit confusing, but it is normal. They now ‘know’ each other, and that is what’s important.

As far as the the updated DevicePairConnectionHelper goes, it works nearly the same as the previous one, but it has two extras, as far as using it is concerned.

  • In the constructor, you can now optionally provide a GUID that describes a Bluetooth Service
  • There is a new boolean property ConnectCrossPlatform that you can set to true – then the Window 8 phone will try to connect to a Windows 8 machine using Bluetooth.

To set it up, you have to take the following things into account:

  • In you Windows Phone 8 app manifest, you have to select the ID_CAP_PROXIMITY capability
  • In you Windows 8 app manifest, you will have to the following capabilities
    • Private Networks (Client & Server)
    • Proximity
  • After you have saved you manifest, you have to open the Package.appmanifest manually (right click, hit View Code) and add a Bluetooth rfcomm service. Andrea explains how this is done, and I repeat it here for completeness:
<Capabilities>
  <Capability Name="internetClient" />
  <Capability Name="privateNetworkClientServer" />
  <DeviceCapability Name="proximity" />
  <m2:DeviceCapability Name="bluetooth.rfcomm">
    <m2:Device Id="any">
      <m2:Function Type="serviceId:A7EA96BB-4F95-4A91-9FDD-3CE3CFF1D8BC" />
    </m2:Device>
  </m2:DeviceCapability>
</Capabilities>

The stuff in Italics is what you add – verbatim. Except for the serviceId. Make and id up yourself, generate a guid, but don’t copy this one. Don’t re-use it over applications. But keep it ready, for you will need it in your code.

So, in your Windows 8 app, you now create a DevicePairConnectionHelper and fire it off like this:

var d = new DevicePairConnectionHelper("A7EA96BB-4F95-4A91-9FDD-3CE3CFF1D8BC");
d.ConnectCrossPlatform = true;
d.Start(ConnectMethod.Browse);

And you do exactly the same on Windows Phone 8. Usually the best way to connect is:

  • Start both the Windows 8.1 and the Windows Phone 8 app.
  • First start connect on the Windows 8.1 computer. That usually pretty quickly returns with zero peers found, but it keeps advertising it’s service. Then hit connect on the Windows Phone 8
  • After some time – sometimes half a minute – the Windows Phone 8 gets the peers

Below is the Windows 8 demo app as it has found my main desktop computer “Karamelk”) (that sports a Bluetooth dongle), and next to it the screen of the Windows 8.1 computer, still searching for contacts

wp_ss_20131204_0001     image

Then I hit “Select contact”. The first time, the Windows 8.1 you get a popup asking if the phone can use the connection. And if you give it permission, the apps connect, and it then it looks like this:

wp_ss_20131205_0001      imagewp_ss_20131205_0003

Now you can send messages back and forth.

You can still use this component to connect two Windows Phones to each other like you used to, and you now can also can connect two computers to each other and exchange messages.

A final piece of warning as far as usage is concerned – if you set ConnectCrossPlatform to true, the Windows Phone 8 will do cross-platform connect – but that’s the only thing it will do. Apparently it can’t find other Windows Phone 8 devices in that mode – just Windows 8.1 computers. For a Windows 8.1 computer, it does not matter – whether ConnectCrossPlatform is on or off, it will find other computers as well as phones. The text “Bluetooth” next to the right radio button is actually wrong on Windows 8.1, since the connection between computer actually uses WiFi Direct.

 

How it works

Just like last time – when I added Bluetooth – there was surprisingly little to change. The actual difficult stuff was already found out by Andrea ;-). It turns out that on the Windows Phone side you don’t have to do much, but on the Windows 8.1 side you have to resort to some trickery and use the Bluetooth Rfcomm API.

First of all we need a property to instruct the component to connect cross-platform (or not) and some stuff to hold the service GUID that we need:

public bool ConnectCrossPlatform { get; set; }

private Guid rfcommServiceUuid;

public string RfcommServiceUuid
{
  get
  {
    return rfcommServiceUuid == Guid.Empty ? null : rfcommServiceUuid.ToString();
  }
  set
  {
    Guid tmpGuid;
    if (Guid.TryParse(value, out tmpGuid))
    {
      rfcommServiceUuid = tmpGuid;
    }
  }
}

The RfcommServiceUuid property looks a bit complex, but it’s only to ensure there’s an actual GUID in it. Then comes the actual cross-platform connecting stuff. Most is just simply taken from Andrea, and for an explanation of what he is actually doing I kindly refer to his post (because I understand about half of it).

#if WINDOWS_PHONE
    private async Task InitBrowseWpToWin()
    {
      var t = new Task(() =>
      {
        PeerFinder.AlternateIdentities["Bluetooth:SDP"] = 
          rfcommServiceUuid.ToString();
      });
      t.Start();
      await t;
    }

    private void StopInitBrowseWpToWin()
    {
      if (PeerFinder.AlternateIdentities.ContainsKey("Bluetooth:SDP"))
      {
        PeerFinder.AlternateIdentities.Remove("Bluetooth:SDP");
      }
    }
#endif

#if NETFX_CORE
    // Code in this part largely based on 
    // http://www.silverlightshow.net/items/Windows-8.1-Play-with-Bluetooth-Rfcomm.aspx

    private const uint ServiceVersionAttributeId = 0x0300;
    private const byte ServiceVersionAttributeType = 0x0A;
    private const uint ServiceVersion = 200;

    private RfcommServiceProvider provider;

    private async Task InitBrowseWpToWin()
    {
      provider = await RfcommServiceProvider.CreateAsync(
                       RfcommServiceId.FromUuid(rfcommServiceUuid));

      var listener = new StreamSocketListener();
      listener.ConnectionReceived += HandleConnectionReceived;

      await listener.BindServiceNameAsync(
        provider.ServiceId.AsString(),
        SocketProtectionLevel.BluetoothEncryptionAllowNullAuthentication);

      using (var writer = new DataWriter())
      {
        writer.WriteByte(ServiceVersionAttributeType);
        writer.WriteUInt32(ServiceVersion);

        var data = writer.DetachBuffer();
        provider.SdpRawAttributes.Add(ServiceVersionAttributeId, data);
        provider.StartAdvertising(listener);
      }
    }

    private void HandleConnectionReceived(StreamSocketListener listener,
      StreamSocketListenerConnectionReceivedEventArgs args)
    {
      provider.StopAdvertising();
      listener.Dispose();
      DoConnect(args.Socket);
    }
    
    // Borrowed code ends
    
    private void StopInitBrowseWpToWin()
    {
      if (provider != null)
      {
        provider.StopAdvertising();
        provider = null;
      }
    }
#endif

Important to notice is that I use Bluetooth:SDP in stead of Bluetooth:Paired like Andrea does. If I use the paired option, I get a list of all devices paired to my phone, including my Jabra headset, my Surface Pro, a Beewi Mini Cooper Coupé Red I got on the last MVP Summit and the desktop computer that I try to connect to. I use SDP (Service Discovery Protocol) to find the device that actually provides the one service I am looking for – not so much a Bluetooth connection from a device that happens to be in the list of paired devices. Notice the Windows Store portion actually makes a “RfcommServiceProvider” with the Guid as identifier, and the Windows Phone portion sets that same Guid as a string to the AlternateIdentities.

Oh – the Windows Phone version of InitBrowseWpToWin method is a bit convoluted – it’s just a way to make whatever is inside async as well, so it’s compatible with the signature of the Windows 8.1 version.

Another important thing to notice is that the Window 8.1 part needs an extra using:

#if NETFX_CORE
using Windows.Devices.Bluetooth.Rfcomm;
#endif

Anyway, where it all starts is here: at the constructor. Not much has changed – there’s only the extra optional parameter that’s being recorded for future use:

public DevicePairConnectionHelper2(string crossPlatformServiceUid = null)
{
  RfcommServiceUuid = crossPlatformServiceUid;
  Messenger.Default.Register<NavigationMessage>(this, ProcessNavigationMessage);
  PeerFinder.TriggeredConnectionStateChanged += PeerFinderTriggeredConnectionStateChanged;
  PeerFinder.ConnectionRequested += PeerFinderConnectionRequested;
}

The Start method has been slightly adapted- it’s now async, to accommodate the async InitBrowseWpToWin method

public async Task Start(ConnectMethod connectMethod = ConnectMethod.Tap, 
   string displayAdvertiseName = null)
{
  Reset();
  connectMode = connectMethod;
  if (!string.IsNullOrEmpty(displayAdvertiseName))
  {
    PeerFinder.DisplayName = displayAdvertiseName;
  }

  try
  {
    PeerFinder.Start();
  }
  catch (Exception)
  {
    Debug.WriteLine("Peerfinder error");
  }

  // Enable browse
  if (connectMode == ConnectMethod.Browse)
  {
    if (ConnectCrossPlatform)
    {
      await InitBrowseWpToWin();
    }

    await PeerFinder.FindAllPeersAsync().AsTask().ContinueWith(p =>
    {
      if (!p.IsFaulted)
      {
        FirePeersFound(p.Result);
      }
    });
  }
}}

A couple of things have changed here – first of all, the whole component is reset in stead of just the PeerFinder, because there's now potentially much more set up now than just the PeerFinder. The start of the PeerFinder is now in a try-catch because on Windows 8.1 some combinations of parameters cause an exception – while still the connection is set up. This is a typical ‘yeah whatever works’ solution. Then the cross platform stuff is set up – if that is required, and I also check first if the result of the task that returns from the PeerFinder is not faulted, which I did not do in earlier versions either.

The most important piece of refactoring is the DoConnect method. In the old version there was one – now there are three overloads:

private void DoConnect(PeerInformation peerInformation)
{
#if WINDOWS_PHONE
  if (peerInformation.HostName != null)
  {
    DoConnect(peerInformation.HostName, peerInformation.ServiceName);
    return;
  }
#endif

  PeerFinder.ConnectAsync(peerInformation).AsTask().ContinueWith(p =>
  {
    if (!p.IsFaulted)
    {
      DoConnect(p.Result);
    }
    else
    {
      Debug.WriteLine("connection fault");
      FireConnectionStatusChanged(TriggeredConnectState.Failed);
    }
  });
}

private void DoConnect(HostName hostname, string serviceName)
{
  var streamSocket = new StreamSocket();
  streamSocket.ConnectAsync(hostname, serviceName).AsTask().ContinueWith((p) => 
                            DoConnect(streamSocket));
}

private void DoConnect(StreamSocket receivedSocket)
{
  socket = receivedSocket;
  StartListeningForMessages();
  PeerFinder.Stop();
  FireConnectionStatusChanged(TriggeredConnectState.Completed);
}

It is a bit complicated, but these three methods cover all scenario’s

  1. If a Windows Phone connects to a Windows Phone, it calls the first method. Since this is not a cross-platform call, the HostName will be null, so it will do PeerFinder.ConnectAsync, receive a StreamSocket and proceed to call the 3rd DoConnect method.
  2. If a Windows 8.1 computer connects a Windows 8.1 computer – ditto
  3. If a Windows Phone connects a Windows 8.1 computer – the Phone will fine find that the HostName (and ServiceId) will be set, so it calls the 2nd DoConnect method. This will proceed to connect to the service, as pass on the result StreamSocket again to the 3rd method
  4. The Window 8.1 computer, upon being connected by a Windows Phone, will get a callback in HandleConnectionReceived (see above, in Andrea’s code) which will immediately result in a StreamSocket, so it call the 3rd DoConnect immediately.

For good measure – the 2nd DoConnect method is actually never used in a Windows 8.1 application.

And that’s about what I needed to do. The changes to the sample app are very limited – I added a checkbox on both of them, and a wrapping ConnectCrossPlatform property that is set by said checkbox. Also a minor detail – the Reset method in the NfcConnectViewModel no longer stops the PeerFinder first. This is especially important for the Windows 8.1 app – it never finds the phone, but the phone is still trying to make a connection. If the PeerFinder on Windows 8.1 is already stopped, you get all kind of funky errors when you select the computer on the phone.

For those who find the discussion a bit arcane – there is, as always, a working demo solution for this article. It is built on top of my new not-yet-published WpWinNl library – that’s basically a cross-platform (no, not PCL, just a NuGet Package) version of the wp7nl library. Well as cross platform as it can be. Stay tuned, it will be available soon. But tackling this problem took a bit longer than I hoped, so I am a bit behind schedule