MVVM binding a ListView to an Enum with translation

Sometimes we need to visualize a list of enum values to the user, so he can make a selection and if possible we would like to make this reusable for any enum used in our app.

But there are some problems when we would try to get this done… Enums are great for storing some kind of selection, but they are not easy to use in an MVVM list binding scenario! Certainly not if they need to be presented in a readable manner nor while different languages are supported.

But there is a way to get this done! Let me show you a solution while using a Windows Phone 8.1 store app, but the code will also work in Windows 8 store app and most of it will also run in a Windows Phone Silverlight app.

The enum we will be using…

   1: public enum Gender

   2: {

   3:     Unknown,

   4:     Male,

   5:     Female

   6: }

The setup of this example is simple, we have a page with a ListView that’s bound to an enum in our ViewModel.

   1: <StackPanel Grid.Row="0"

   2:             Orientation="Horizontal">

   3:     <TextBlock Text="selected value:"

   4:                 Margin="0,0,6,12"

   5:                 Style="{StaticResource BodyTextBlockStyle}"/>


   7:     <TextBlock x:Name="SelectedValue"

   8:                 Text="{Binding SelectedValue}"

   9:                 Style="{StaticResource BodyTextBlockStyle}"/>

  10: </StackPanel>


  12: <ListView x:Name="EnumListView"

  13:             Grid.Row="1"

  14:             ItemsSource="{Binding SelectionType, Mode=TwoWay, Converter={StaticResource EnumTypeToListConverter}}"

  15:             SelectedIndex="{Binding SelectedIndex, Mode=TwoWay}">

  16:     <ListView.ItemTemplate>

  17:         <DataTemplate>

  18:             <TextBlock Text="{Binding }" FontSize="30" Margin="0,0,0,6" />

  19:         </DataTemplate>

  20:     </ListView.ItemTemplate>

  21: </ListView>

You’ll notice that the ItemSource is not binding to some collection! It’s the actual type of the enum and through the user of a converter we will return the needed display values. We are also showing the selected value inside a textblock, to show you that we are really getting an enum value!

In our ViewModel we have following properties.

   1: private Type _selectionType;

   2: public Type SelectionType

   3: {

   4:     get { return _selectionType; }

   5:     set

   6:     {

   7:         if (_selectionType == value)

   8:             return;


  10:         _selectionType = value;

  11:         RaisePropertyChanged();

  12:     }

  13: }


  15: private int _selectedIndex;

  16: public int SelectedIndex

  17: {

  18:     get { return _selectedIndex; }

  19:     set

  20:     {

  21:         if (_selectedIndex == value)

  22:             return;


  24:         _selectedIndex = value;

  25:         this.SelectedValue = Enum.GetValues(this.SelectionType).GetValue(SelectedIndex).ToString();

  26:         RaisePropertyChanged();

  27:     }

  28: }

The actual enum will be returned through the SelectionType property and the return type is System.Type. Also when the ListView has a selected index change, you’ll see that we’re using that current selected index to determine what enum value was actually chosen and we set that to the SelectedValue property.

So how do you initialize this, just by getting the type of your enum! In this example we init it in our constructor.

   1: public MainViewModel()

   2: {

   3:     this.SelectionType = typeof (Enums.Gender);

   4:     _selectedIndex = -1;

   5: }

Now a ListView can’t do anything with an enum type, so we need to convert this in our EnumTypeToListConverter. The actual convert part looks like this.

   1: public object Convert(object value, Type targetType, object parameter, string language)

   2: {

   3:     if (value == null)

   4:         return null;


   6:     var enumType = value as Type;

   7:     if (enumType == null || !enumType.GetTypeInfo().IsEnum)

   8:         return null;


  10:     var values = Enum.GetValues((Type)value).Cast<Enum>();


  12:     return values.Select(item => resourceLoader.GetString(string.Format("{0}_{1}", enumType.Name, item))).ToList();

  13: }

We get the enum type as value parameter and use this to get all values of the given type through use of the general Enum.GetValues method. With this we have all values that the given enum has, but these aren’t yet usable to present inside the app! To fix this, we use a resource loader to get a nice display value for each enum value from the current loaded language resource! Finally we convert this to a list and return it to the ListView.

For this all to work you’ll need to add some resource files for each language you are going to support and inside each resource file you’ll need to add a display value for each enum value.

In our case we are using following format [enum type name]_[enum value]. So for our gender enum this gives following resource entries. For adding these resources, take a look at this good explanation here…


With all this in place you’ll get following result


As always a complete working version can be found on my github here…


Windows Phone Image button style

So you are creating your latest windows phone app and would like to add some fancy designed icons or images… But instead of just showing these, they’ll need to react as a button! By the way, we normally use vector data, but that is not always to our disposal. It could be we have a nicely designed icon in several resolutions.

Well that’s easy you think, we’ll just add a button and put that image inside it – here is the code

   1: <Button>

   2:     <Image Source="../Assets/May_The_4th_logo_BLK_BG.jpg"></Image>

   3: </Button>

But when you just use it like this, the image won’t look good! It will have the default button border around it and that’s not something you want.


Than you think a little bit about it, maybe search the internet and yes, behold Jeff Wilcox used something back in the wp7 days that could be usefull! An EmptyButtonStyle, it’s a button style stripped to the bare minimum! So you add that and use it – here is the code

   1: <Button Style="{StaticResource EmptyButtonStyle}">

   2:     <Image Source="../Assets/May_The_4th_logo_BLK_BG.jpg"></Image>

   3: </Button>

   1: <Style x:Key="EmptyButtonStyle" TargetType="ButtonBase">

   2:     <Setter Property="Background" Value="Transparent" />

   3:     <Setter Property="Padding" Value="0" />

   4:     <Setter Property="Template">

   5:         <Setter.Value>

   6:             <ControlTemplate TargetType="ButtonBase">

   7:                 <Border Background="{TemplateBinding Background}"

   8:                         Padding="{TemplateBinding Padding}">

   9:                     <ContentPresenter

  10:                         HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"

  11:                         VerticalAlignment="{TemplateBinding VerticalContentAlignment}"

  12:                         Content="{TemplateBinding Content}"

  13:                         ContentTemplate="{TemplateBinding ContentTemplate}"/>

  14:                 </Border>

  15:             </ControlTemplate>

  16:         </Setter.Value>

  17:     </Setter>

  18: </Style>

But again you’ll notice it’s not perfect… ok it has no visual references anymore to a button, but pressing the image will now also no longer give a visual indication!


On wp8, pressing a button, the content will slightly grey out to tell the user he’s pressing that button! So we need to check the state of the button to play with the opacity and enable this visual effect again! Behold the ImageButtonStyle

   1: <Button Style="{StaticResource ImageButtonStyle}">

   2:     <Image Source="../Assets/May_The_4th_logo_BLK_BG.jpg"></Image>

   3: </Button>

   1: <Style x:Key="ImageButtonStyle" TargetType="ButtonBase">

   2:     <Setter Property="Background" Value="Transparent"/>

   3:     <Setter Property="Padding" Value="0,0,0,0"/>

   4:     <Setter Property="Template">

   5:         <Setter.Value>

   6:             <ControlTemplate TargetType="Button">

   7:                 <Grid x:Name="grid" Background="Transparent">

   8:                     <Grid.Projection>

   9:                         <PlaneProjection/>

  10:                     </Grid.Projection>

  11:                     <VisualStateManager.VisualStateGroups>

  12:                         <VisualStateGroup x:Name="CommonStates">

  13:                             <VisualStateGroup.Transitions>

  14:                                 <VisualTransition GeneratedDuration="0" To="Pressed">

  15:                                     <Storyboard>

  16:                                         <DoubleAnimation Duration="0" To="0.75" Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="ContentContainer" />

  17:                                     </Storyboard>

  18:                                 </VisualTransition>

  19:                             </VisualStateGroup.Transitions>

  20:                             <VisualState x:Name="Normal"/>

  21:                             <VisualState x:Name="MouseOver"/>

  22:                             <VisualState x:Name="Pressed">

  23:                                 <Storyboard>

  24:                                     <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="ContentContainer">

  25:                                         <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PhoneBackgroundBrush}"/>

  26:                                     </ObjectAnimationUsingKeyFrames>

  27:                                 </Storyboard>

  28:                             </VisualState>

  29:                             <VisualState x:Name="Disabled">

  30:                                 <Storyboard>

  31:                                     <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="ContentContainer">

  32:                                         <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PhoneDisabledBrush}"/>

  33:                                     </ObjectAnimationUsingKeyFrames>

  34:                                 </Storyboard>

  35:                             </VisualState>

  36:                         </VisualStateGroup>

  37:                     </VisualStateManager.VisualStateGroups>

  38:                     <ContentControl x:Name="ContentContainer" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" Foreground="{TemplateBinding Foreground}" HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" Padding="{TemplateBinding Padding}" VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" Margin="0" HorizontalAlignment="Center" VerticalAlignment="Center"/>

  39:                 </Grid>

  40:             </ControlTemplate>

  41:         </Setter.Value>

  42:     </Setter>

  43: </Style>

So when running the app you’ll notice no difference with the empty button style, until you press it! It will now grey out the content a little bit.

As always a test project can be found on github here: ImageButton project


Xbox music integration – windows phone

So you’ve created a Windows Phone app that has something to do with music? Well chances are you can gain some money with it thanks to the Xbox music affiliate program!
All you need to know to get this up and running is available on MSDN here… but let me show you my implementation, because I find the docs on MSDN a bit chaotic.

So first things first, open up a publishers account on Rakuten Linkshare through this url http://www.linkshare.com/join/ This is needed, because they are the ones gathering the click information and doing the payments.

* Pro tip * Note that at one point you’ll need to fill in website info – here you’ll have to put the URL pointing to your app in the windows phone app store! This is important, because that way Xbox music can verify your request ( more on this later on ).

* Pro tip * Be sure to take into account that you’ll need to fill in form W-8BEN if you live outside of the United States, for being in order of payment taxes!

After you filled in all the needed info and your account is in order, open the LinkShare website and navigate to PROGRAMS > Categories – select category Entertainment > Music and look for Xbox Music and press the Apply button on the right side.


When you apply for a new advertiser, you’ll get an email notification stating that it will take 1 till 2 weeks before your application is approved! And here comes the important pro tip from above, this validation is done based on the app url you submitted earlier. So you’ll first need to add a working app to the store.

After a positive review, your app will be approved for generating links to LinkShare. So the coding/integration can begin!
In my app ConcertWall I wanted to show albums from a specific artist and when the user presses the image from that album, it will show Xbox music so you could play/purchase that album.

To get started with using the REST API service of Xbox Music, you’ll need to get a Client ID and Client Secret for your app. This can be obtained via https://datamarket.azure.com/ – you’ll need to register and add the app. All the details and steps are outlined on MSDN here…
With this info you then can set up your first method to interact with the service!

When you want to gather data, you’ll first need to request an access token from the OAuth service before you can perform any REST calls to the Xbox Music service. I’ve packed this call in an async method called RequestAccessToken.

   1: private async Task<string> RequestAccessToken()

   2: {

   3:     HttpClient client = new HttpClient();


   5:     //Create the request data

   6:     var requestData = new Dictionary<string, string>();

   7:     requestData["client_id"] = _clientID;

   8:     requestData["client_secret"] = _clientSecret;

   9:     requestData["scope"] = _xboxMusicService;

  10:     requestData["grant_type"] = "client_credentials";


  12:     //Post the request and retrieve the response

  13:     var response = await client.PostAsync(new Uri(_tokenService), new FormUrlEncodedContent(requestData));

  14:     response.EnsureSuccessStatusCode();

  15:     string responseBody = await response.Content.ReadAsStringAsync();


  17:     Token token = JsonConvert.DeserializeObject<Token>(responseBody);

  18:     return token.access_token;

  19: }

It needs your client id and secret ( the ones you created on the DataMarket.Azure site ), a given scope – this is a fixed string http://music.xboxlive.com all this has to be passed to the token service – this is https://datamarket.accesscontrol.windows.net/v2/OAuth2-13 – as a http POST. You’ll get a response back that contains the needed access token.

This token allows you to do any HTTP Get REST requests to the Xbox Music service for a specific period. In my case I just wanted to show all albums of a given artist. I wrapped this in a method called GetArtistAlbums

   1: public async Task<List<Album>> GetArtistAlbums(string artistName)

   2: {

   3:     List<Album> artistAlbums = null;

   4:     try

   5:     {

   6:         string accessToken = await this.RequestAccessToken();


   8:         //Use the token in a new request

   9:         HttpClient client = new HttpClient();


  11:         string serviceRequest = string.Format(_artistSearch, artistName);

  12:         serviceRequest = string.Concat(serviceRequest, "&filters=Albums");

  13:         serviceRequest = string.Concat(serviceRequest, _xboxMusicRESTRequest);


  15:         var response = await client.GetAsync(new Uri(serviceRequest + HttpUtility.UrlEncode(accessToken)));

  16:         string responseBody = await response.Content.ReadAsStringAsync();

  17:         AlbumsResponse albumsResponse = JsonConvert.DeserializeObject<AlbumsResponse>(responseBody);


  19:         if (!ReferenceEquals(albumsResponse, null) && !ReferenceEquals(albumsResponse.Albums, null))

  20:         {

  21:             artistAlbums = (from album in albumsResponse.Albums.Items

  22:                             from artist in album.Artists

  23:                             where artist.Artist.Name.Equals(artistName, StringComparison.OrdinalIgnoreCase)

  24:                             select album).ToList();

  25:         }

  26:     }

  27:     catch (Exception)

  28:     {

  29:         //There was a problem with getting the albums, return null to be sure the program continues

  30:     }


  32:     return artistAlbums;

  33: }

The Service Request string is composed of different parts, let me show you each one

  • First the base url: _artistSearch = https://music.xboxlive.com/1/content/music/search?q={0}
    • So we are going to perform a search with a given keyword
  • The keyword: artistName = the input parameter of this method
  • We want albums so add: &filters=Albums
    • You can search for other items, be sure to check out the MSDN documentation
  • Add the access token: _xboxMusicRESTRequest = “&accessToken=Bearer+”
  • Concat with the actual urlencoded token: new Uri(serviceRequest + HttpUtility.UrlEncode(accessToken))

With that in place, you just fire a client.GetAsync() and the response return value will be a JSON string containing all found Albums mathing your search string. I found that this can be a big set with to much hits, so I filter out all albums that don’t have the given artist name in the artists list of each album!

* Pro tip * Do note we aren’t yet earning any money! For this we need to add Deep Links to Xbox music on our phone.

I now present these albums in a list so the user can press the image, with this I’m activating the Deep Link url to Xbox Music on the phone.


The correct deep link uri for each album is actually already inside the response result we got from our search query, but to gain money through LinkShare we need to modify it!
We need to reroute our request through LinkShare with the correct id’s so that LinkShare can track the requests and if needed award us points!

The correct link format is show below, only thing missing is your affiliate key for Xbox music – this id can be found by generating a link on the LinkShare site for Xbox music after your request for this advertiser has been approved! Just press the Get Link button and look for the http://ad.linksynergy.com/fs-bin/show?id= part it shows you the id!

   1: string.Format("http://click.linksynergy.com/deeplink?id={0}&mid=39033&murl={1}", "{your Xbox music LinkShare id}", HttpUtility.UrlEncode(string.Concat(item.Link, "&action=play")))

But how do you use this deep link? Well put a WebBrowser control on your windows phone page and set it Collapsed.

   1: <phone:WebBrowser x:Name="HiddenWebBrowser" Visibility="Collapsed" />

Only thing left is triggering the navigation on this control with the given deep link url!

   1: HiddenWebBrowser.Navigate(new Uri(message.MusicLink))

* Pro tip * Instead of doing this yourself, you can use this XBox music API wrapper ( pcl ) ! It contains all needed REST calls and also provides the possibility to add your affiliate id!

Happy money gaining!


Navigating to same viewmodel but with different data and keep navigation stack correct with MVVM Light

Long title, I know… and even with that the scenario I’m trying to describe isn’t clear yet, so let me explain even more.

In my latest app ConcertWall you can open a Venue and look at the concerts that are available, when you open up a Concert you can – again – go through to the same Venue and pick another Concert to get the details.

Now translated to the technical MVVM background this means: Venue viewmodel A > Concert viewmodel A > Venue viewmodel A > Concert viewmodel B > etc…

But when you try to do this in a normal MVVM Light way, you’ll break the Navigation stack! Because when you’ll navigate back from concert viewmodel B to the venue viewmodel A to the concert viewmodel A, you’ll notice A will no longer be visible but you’ll get B again!

This problem is easy to explain, you only retain one instance of the concert viewmodel and if you do nothing when navigating, only the last loaded datacontext will be shown each time you get on the concert view!

Now how can we fix this, there are several ways ( like Nico Vermeir shows one here ), but I like to use the SimpleIoc functionality that is present in MVVM Light and let it handle my instances instead of adding extra in memory containers!

So how do you get this working, first you’ll need to add a special method inside your ViewModelLocator called GetViewModel<T>

   1: public static ViewModelBase GetViewModel<T>(string key) where T : ViewModelBase

   2: {

   3:     return ServiceLocator.Current.GetInstance<T>(key);

   4: }

This is actually the real solution, all it does is keep track of all separate instances of a given viewmodel based on a key you provide! So no hassle with keeping track of it myself.

Only thing left to do is using this method when navigating to the viewmodel you want to track based on a key ( in our example the Concert viewmodel ).

Best thing to use as key, is the unique identifier that is present in your data model that needs to be shown on that view ( in our example the Concert ID ) and add that to the navigation URI.

   1: Uri eventDetailPageUri = new Uri(string.Format("{0}?key={1}", ViewModelLocator.EventDetailPage, item.id), UriKind.Relative);

   2: _navigationService.NavigateTo(eventDetailPageUri);

Now when you navigate to the view, you can no longer use the MVVM Light datacontext binding in XAML! This because we’ll need to set it our self through use of the above provided GetViewModel method based on the key that is used in the navigation URI. So in the code behind of the given view, override the OnNavigatedTo method and inject the correct viewmodel datacontext!

   1: protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)

   2: {

   3:     base.OnNavigatedTo(e);

   4:     string navigationKey = "";

   5:     if (NavigationContext.QueryString.TryGetValue("key", out navigationKey))

   6:     {

   7:         this.DataContext = ViewModelLocator.GetViewModel<EventDetailViewModel>(navigationKey);

   8:         ((EventDetailViewModel) this.DataContext).RefreshLayout();

   9:     }

  10: }

I’m also calling a RefreshLayout method to refresh the current data on each view load, but depending on the app scenario this is maybe not needed.

With all that in place, you can go forward and call the same viewmodel with different data over and over, and keep your navigation back stack intact because of the added key inside the navigation URI.

Happy coding!


Picture library image control for Windows 8 store apps

So on a recent Windows 8 store app project, we wanted to let the user pick images from his hard drive to use them inside the app.

But with Windows 8 store apps we are a bit restricted on rights to get a hold on files anywhere on the pc’s hard drive. Sure when you select the file you can read it, but when the app restarts that location could be no longer available to the app itself due to rights restrictions.

So, how could we solve this? We could store the images binary inside some sort of database… but for our app that would be a bit too much. Other solution is just using the default Picture library that is available on the pc itself!
It’s easy to give the app rights to this folder… it’s right there in the Capabilities section all you have to do is switch it on!


With that in place we only needed to create an image control that would allow us to use a normal string, representing the path to the location of the image, as source. But the control should also let the user select images and copy those over to the picture library so we can reuse them later!

So let me introduce to you the PictureLibraryImage control!
It’s a user control that contains an Image and has some extra Dependency Properties.

The xaml looks like this – note there are 2 fading animations, they are used to let the Placeholder image fade out and the Source image fade in, after it has been loaded!

   1: <UserControl x:Name="PictureLibImageControl"

   2:              x:Class="PictureLibraryImageControl.Controls.PictureLibImage"

   3:              xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

   4:              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

   5:              xmlns:local="using:PictureLibraryImageControl.Controls"

   6:              xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

   7:              xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

   8:              mc:Ignorable="d"

   9:              d:DesignHeight="300"

  10:              d:DesignWidth="400">    

  11:     <Grid>

  12:         <Grid.Resources>

  13:             <Storyboard x:Name="ImageFadeOut">

  14:                 <FadeOutThemeAnimation Storyboard.TargetName="Image" />

  15:             </Storyboard>

  16:             <Storyboard x:Name="ImageFadeIn">

  17:                 <FadeInThemeAnimation Storyboard.TargetName="Image" />

  18:             </Storyboard>

  19:         </Grid.Resources>


  21:         <Image x:Name="Image"

  22:                Source="{Binding ElementName=PictureLibImageControl, Path=Placeholder}"

  23:                Height="{Binding ElementName=PictureLibImageControl, Path=ActualHeight}"

  24:                Width="{Binding ElementName=PictureLibImageControl, Path=ActualWidth}"

  25:                Tapped="OnImageTapped"               

  26:                />

  27:     </Grid>

  28: </UserControl>

The properties we are introducing are:

  • Placeholder : we use this to show a dummy image when there has no source been set yet
  • Source : the path to an image inside the picture library
  • FolderName : the name for the folder we use to put the user selected images

When you place the control in your app and run the app, you can tap on it to get a hold of a FilePicker, this will let the user choose an image! When an image is chosen we copy it over to the given FolderName location inside the Picture library… the code looks like this:

   1: private async void OnImageTapped(object sender, TappedRoutedEventArgs e)

   2: {

   3:     FileOpenPicker openPicker = new FileOpenPicker();

   4:     openPicker.ViewMode = PickerViewMode.Thumbnail;

   5:     openPicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;

   6:     openPicker.FileTypeFilter.Add(".jpg");

   7:     openPicker.FileTypeFilter.Add(".jpeg");

   8:     openPicker.FileTypeFilter.Add(".png");

   9:     StorageFile file = await openPicker.PickSingleFileAsync();

  10:     if (file != null)

  11:     {

  12:         // Application now has read/write access to the picked file 

  13:         using (await _lock.LockAsync())

  14:         {

  15:             byte[] buffer = null;

  16:             using (var stream = await file.OpenAsync(Windows.Storage.FileAccessMode.Read))

  17:             {

  18:                 var reader = new DataReader(stream.GetInputStreamAt(0));

  19:                 buffer = new byte[stream.Size];

  20:                 await reader.LoadAsync((uint)stream.Size);

  21:                 reader.ReadBytes(buffer);

  22:             }


  24:             StorageFile selectedPicture = null;

  25:             if (!string.IsNullOrEmpty(this.FolderName))

  26:             {

  27:                 StorageFolder PosPicFolder = await KnownFolders.PicturesLibrary.CreateFolderAsync(this.FolderName, CreationCollisionOption.OpenIfExists);

  28:                 selectedPicture = await PosPicFolder.CreateFileAsync(file.Name, CreationCollisionOption.OpenIfExists);

  29:             }

  30:             else

  31:                 selectedPicture = await KnownFolders.PicturesLibrary.CreateFileAsync(file.Name, CreationCollisionOption.OpenIfExists);


  33:             await FileIO.WriteBytesAsync(selectedPicture, buffer);

  34:             this.Source = selectedPicture.Path;

  35:         }

  36:     }

  37: }

Now comes the last part of our control! We have to be able to use a string pointing to a file inside the picture library as source…

This is actually very easy with the Source dependency property in place and the Picture Library capability. We only have to read out the file and set it as BitmapImage to the image ssource inside the control! The code for this is

   1: private static async void SourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)

   2: {

   3:     var control = (PictureLibImage)d;

   4:     var file = await StorageFile.GetFileFromPathAsync((string)e.NewValue);

   5:     if (file != null)

   6:     {

   7:         using (var stream = await file.OpenAsync(FileAccessMode.Read))

   8:         {

   9:             var bitmapImage = new BitmapImage();

  10:             await bitmapImage.SetSourceAsync(stream);


  12:             bitmapImage.ImageOpened += (sender, args) => control.LoadImage(bitmapImage);

  13:         }

  14:     }

  15: }

Only thing left to do is use it in your project… the source can be binded to some MVVM property of course.

   1: <controls:PictureLibImage x:Name="PictureLibImage"

   2:                             Placeholder="/Assets/placeholder_250.png"

   3:                             FolderName="PictureLibImageTest"

   4:                             Source="E:\ZuneMedia\Pictures\SistersOfMercyWhite500x500.jpg"

   5:                             Height="250"

   6:                             Width="250"

   7:                             />

Let me get one thing straight, this is not by any chance the most elegant solution… but for our needs it served the purpose very well.

Just go through the code, maybe there are other things you could reuse instead of the actual control itself :)

Everything is up on my Github here… so download, use and any suggestions or improvements are welcome!


Twitterate your Windows Phone app

Thanks Scott for helping with the Path control!

Well, the people of twitter have streamlined the look and feel of their mobile apps, so that it almost looks the same on each platform.
Currently on Windows Phone this means it’s like this:


So you will see a set of icons on top with a content body below and by selecting one of the images you will switch the content body.

But the special thing about this, is that the body content will ‘slide’ from one to another, meaning in Windows Phone development terms this is most likely a Pivot control. Weird thing though, the icons are not inline with the standard Pivot control header. If so they would not be all on the same page. 
How can we mimic this ourselves?

I have no doubt there would be several different possibilities, but let me give you mine…

Create a new windows phone app and in the MainPage.xaml add a ListBox called ImageBar.
This imagebar will host our 4 icons!
Each icon is represented with a custom Path control that contains XAML data ( more on this below ). To get nice looking icons, just download Metro Studio from Syncfusion! Open the program, look up a nice icon and copy paste the generated XAML data. The listbox XAML will be:

   1: <ListBox x:Name="ImageBar"

   2:             Grid.Row="0"

   3:             ScrollViewer.VerticalScrollBarVisibility="Disabled"

   4:             ItemContainerStyle="{StaticResource ListBoxItemStyle}"                     

   5:             SelectionChanged="ImageBar_SelectionChanged">

   6:     <ListBox.ItemsPanel>

   7:         <ItemsPanelTemplate>

   8:             <customcontrols:SplitPanel />

   9:         </ItemsPanelTemplate>

  10:     </ListBox.ItemsPanel>

  11:     <ListBoxItem>

  12:         <customcontrols:PathControl Content="M37.786324,45.467003C39.394405,45.467003 40.702843,46.774002 40.702843,48.383999 40.702843,49.994999 39.394405,51.299996 37.786324,51.299996 36.178245,51.299996 34.869808,49.994999 34.869808,48.383999 34.869808,46.774002 36.178245,45.467003 37.786324,45.467003z M26.671389,45.467003C28.282196,45.467003 29.582848,46.774002 29.582848,48.383999 29.582848,49.994999 28.282196,51.299996 26.671389,51.299996 25.060581,51.299996 23.749926,49.994999 23.749926,48.383999 23.749926,46.774002 25.060581,45.467003 26.671389,45.467003z M42.511345,36.764008C44.122189,36.764008 45.432873,38.069786 45.432873,39.680516 45.432873,41.291245 44.122189,42.597023 42.511345,42.597023 40.900505,42.597023 39.599827,41.291245 39.599827,39.680516 39.599827,38.069786 40.900505,36.764008 42.511345,36.764008z M31.961349,36.764008C33.572155,36.764008 34.872807,38.069786 34.872807,39.680516 34.872807,41.291245 33.572155,42.597023 31.961349,42.597023 30.350542,42.597023 29.039886,41.291245 29.039886,39.680516 29.039886,38.069786 30.350542,36.764008 31.961349,36.764008z M20.771337,36.764008C22.382177,36.764008 23.692862,38.069786 23.692862,39.680516 23.692862,41.291245 22.382177,42.597023 20.771337,42.597023 19.160496,42.597023 17.859817,41.291245 17.859817,39.680516 17.859817,38.069786 19.160496,36.764008 20.771337,36.764008z M26.491566,0C32.521801,8.3675695E-07 37.622181,4.5700085 39.312214,10.863009 40.262218,10.601992 41.252262,10.450991 42.272339,10.450991 48.382656,10.450991 53.333004,15.399997 53.333004,21.506993 53.333004,25.436009 51.282841,28.877995 48.192707,30.84 46.952648,31.847996 45.372604,32.457005 43.66243,32.468998 43.202442,32.524998 42.752346,32.563999 42.272339,32.563999 41.812351,32.563999 41.362377,32.528019 40.922287,32.472019L12.440745,32.472019C11.990769,32.528019 11.530662,32.563999 11.060665,32.563999 4.9503445,32.563999 0,27.804997 0,21.933995 0,16.063998 4.9503445,11.302004 11.060665,11.302004 11.900677,11.302004 12.72079,11.40201 13.510751,11.575014 15.000823,4.9209912 20.241222,8.3675695E-07 26.491566,0z" />

  13:     </ListBoxItem>

  14:     <ListBoxItem>

  15:         <customcontrols:PathControl Content="M44.032538,19.436991L46.035559,20.664995C46.766642,21.111948 46.996969,22.06896 46.546339,22.798928 46.188246,23.393661 45.486426,23.65635 44.843695,23.49366L44.820734,23.486614 44.760626,23.369398C44.336168,22.588939,43.820093,21.865227,43.226896,21.212781L43.137881,21.119438 43.321413,20.826843C43.589335,20.38273,43.827212,19.918489,44.032538,19.436991z M44.82997,13.720987L47.239088,13.785997C48.088778,13.810999 48.76853,14.523084 48.748537,15.381185 48.71855,16.238285 48.008806,16.914367 47.149122,16.890363L44.779986,16.825356C44.849962,16.3543 44.899943,15.875243 44.909941,15.387185 44.929934,14.822118 44.899943,14.267052 44.82997,13.720987z M21.977497,10.773006C26.976931,10.773006 31.206434,14.566032 32.616308,19.785997 33.39625,19.569017 34.216105,19.44298 35.065985,19.44298 40.135477,19.44298 44.245001,23.551007 44.245001,28.616981 44.245001,31.877963 42.535231,34.731965 39.975462,36.358001 38.945551,37.194974 37.645769,37.699002 36.215876,37.710965 35.835917,37.756986 35.455954,37.790006 35.065985,37.790006 34.686022,37.790006 34.316069,37.756986 33.936106,37.712003L10.328789,37.712003C9.948827,37.756986 9.568865,37.790006 9.1788942,37.790006 4.1095211,37.790006 1.9717481E-07,33.840974 0,28.970008 1.9717481E-07,24.098981 4.1095211,20.150987 9.1788942,20.150987 9.8788883,20.150987 10.558744,20.233018 11.208695,20.376023 12.448545,14.856986 16.788141,10.773006 21.977497,10.773006z M45.670187,7.1741316C46.22111,7.1813989 46.750784,7.4820194 47.025496,8.0038509 47.425074,8.7628791 47.135378,9.701914 46.376181,10.099929L44.228449,11.232971C43.858839,10.249934,43.369356,9.3319013,42.75001,8.4998705L44.92771,7.3528278C45.164961,7.2281356,45.419771,7.1708283,45.670187,7.1741316z M22.364184,6.4086061C22.625342,6.4160089,22.888245,6.4901156,23.127493,6.6371107L25.234978,7.9250669C24.984028,8.220556,24.749932,8.5298274,24.53269,8.851454L24.328104,9.1663187 23.911865,9.0885335C23.280842,8.9838928 22.635969,8.9300027 21.980942,8.9300021 21.811247,8.9300027 21.642187,8.933701 21.473829,8.9410375L21.137139,8.9630404 21.059021,8.8642364C20.707878,8.3721137 20.662932,7.6973248 21.000031,7.1490924 21.280943,6.6922345 21.756939,6.4314623 22.25244,6.4095001 22.289602,6.4078531 22.326876,6.4075489 22.364184,6.4086061z M33.789492,5.6309772C33.870619,5.6311283 33.951971,5.6323071 34.033533,5.6345253 39.253584,5.7765265 43.363627,10.121519 43.223623,15.341507 43.179873,16.972441 42.724985,18.495466 41.960894,19.814482L41.873621,19.957062 41.662577,19.791178C39.823569,18.415445 37.541613,17.599988 35.071509,17.599989 34.671488,17.599988 34.271471,17.623987 33.861456,17.668986 32.387953,13.866806 29.602228,11.015687 26.206078,9.7126912L26.036064,9.6513226 26.297627,9.2972018C28.035303,7.0573542,30.754883,5.6253028,33.789492,5.6309772z M40.950159,2.103569C41.247969,2.095583 41.552279,2.1736679 41.825686,2.3465204 42.54479,2.8074622 42.764517,3.7673416 42.295103,4.4892492L40.966757,6.5819864C40.177744,5.9140697,39.298838,5.3531408,38.340037,4.9201951L39.688353,2.8204608C39.975496,2.3692675,40.453802,2.1168785,40.950159,2.103569z M27.495932,1.5621281C28.047716,1.5674677,28.578221,1.8664742,28.853361,2.3876524L30.02396,4.5908751C29.033455,4.9449115,28.112984,5.4329605,27.272556,6.0350218L26.111962,3.8427997C25.711758,3.0837221 25.9919,2.1446285 26.752289,1.7435865 26.98991,1.6179495 27.245119,1.559701 27.495932,1.5621281z M34.100096,0.00037002563C34.126665,-0.00020599365 34.153399,-0.00011253357 34.180281,0.00066947937 35.040442,0.022665024 35.71057,0.73755455 35.690565,1.5934191L35.62055,4.0930305C35.120459,4.0080423 34.600359,3.953052 34.070257,3.940053 33.550162,3.9270558 33.030062,3.9520516 32.519968,4.0090423L32.589979,1.5114326C32.609358,0.68037415,33.276407,0.018217087,34.100096,0.00037002563z" />

  16:     </ListBoxItem>

  17:     <ListBoxItem>

  18:         <customcontrols:PathControl Content="M19.589941,21.452004L29.499502,21.452004 37.799032,35.562995 29.729471,35.562995 37.109123,49.672003 17.060033,29.747993 24.549665,29.747993z M26.491512,0C32.521856,0 37.622147,4.5720005 39.312244,10.865 40.262299,10.603 41.252354,10.451 42.272413,10.451 48.382761,10.451 53.333042,15.401 53.333042,21.507999 53.333042,25.437999 51.282928,28.878998 48.192751,30.840999 46.952681,31.848998 45.372589,32.455998 43.652491,32.468998 43.202465,32.525998 42.742439,32.564999 42.272413,32.564999 41.812386,32.564999 41.36236,32.528998 40.922335,32.472999L38.562201,32.472999 30.771756,19.228999 15.680895,19.228999 20.631178,27.525999 11.670666,27.525999 16.64095,32.472999 12.44071,32.472999C11.990685,32.528998 11.530658,32.564999 11.060631,32.564999 4.9502821,32.564999 0,27.805998 0,21.934999 0,16.062999 4.9502821,11.304 11.060631,11.304 11.900679,11.304 12.720726,11.403 13.510771,11.576 15.000856,4.9220009 20.241155,0 26.491512,0z" />

  19:     </ListBoxItem>

  20:     <ListBoxItem>

  21:         <customcontrols:PathControl Content="M31.348,48.494C32.685356,48.494 33.770001,49.577489 33.770001,50.913598 33.770001,52.249612 32.685356,53.333001 31.348,53.333001 30.013346,53.333001 28.930001,52.249612 28.930001,50.913598 28.930001,49.577489 30.013346,48.494 31.348,48.494z M22.1227,48.494C23.457356,48.494 24.542,49.577489 24.542,50.913598 24.542,52.249612 23.457356,53.333001 22.1227,53.333001 20.785345,53.333001 19.702,52.249612 19.702,50.913598 19.702,49.577489 20.785345,48.494 22.1227,48.494z M35.26405,41.272998C36.60138,41.272998 37.686001,42.357621 37.686001,43.69365 37.686001,45.029576 36.60138,46.113 35.26405,46.113 33.929424,46.113 32.846001,45.029576 32.846001,43.69365 32.846001,42.357621 33.929424,41.272998 35.26405,41.272998z M26.508051,41.272998C27.845278,41.272998 28.930001,42.357621 28.930001,43.69365 28.930001,45.029576 27.845278,46.113 26.508051,46.113 25.173323,46.113 24.09,45.029576 24.09,43.69365 24.09,42.357621 25.173323,41.272998 26.508051,41.272998z M17.2318,41.272998C18.567579,41.272998 19.652001,42.357621 19.652001,43.69365 19.652001,45.029576 18.567579,46.113 17.2318,46.113 15.896121,46.113 14.813,45.029576 14.813,43.69365 14.813,42.357621 15.896121,41.272998 17.2318,41.272998z M44.021054,19.436998L46.029053,20.664867C46.758328,21.111492 46.98745,22.068645 46.540806,22.799085 46.17791,23.393704 45.478031,23.656512 44.837174,23.493527L44.815964,23.486991 44.755482,23.36905C44.331001,22.588596,43.814919,21.86488,43.221752,21.212421L43.130867,21.117109 43.312847,20.826513C43.580288,20.382326,43.817284,19.918125,44.021054,19.436998z M44.828007,13.720999L47.233292,13.786101C48.090233,13.810902 48.76614,14.523125 48.741337,15.381254 48.719235,16.238081 48.005619,16.913905 47.148682,16.890402L44.771996,16.825301C44.846211,16.353886 44.893116,15.874769 44.908718,15.386453 44.92432,14.821335 44.895718,14.266617 44.828007,13.720999z M21.976753,10.773001C26.979286,10.773001 31.208513,14.566015 32.614826,19.786035 33.396027,19.568634 34.218937,19.442332 35.069241,19.442332 40.136975,19.442332 44.245003,23.550448 44.245003,28.616866 44.245003,31.877278 42.539391,34.731389 39.976872,36.357696 38.948166,37.194999 37.640854,37.698899 36.213749,37.710603 35.840042,37.756803 35.459843,37.790002 35.069241,37.790002 34.687737,37.790002 34.312737,37.756803 33.940331,37.7119L10.320378,37.7119C9.942775,37.756803 9.5625736,37.790002 9.1705706,37.790002 4.1054971,37.790002 2.7073101E-07,33.840787 0,28.969668 2.7073101E-07,24.09855 4.1054971,20.150635 9.1705706,20.150635 9.871175,20.150635 10.552179,20.232635 11.208384,20.375936 12.447993,14.856316 16.789219,10.773001 21.976753,10.773001z M45.672386,7.174134C46.223133,7.1814017 46.751801,7.4817498 47.025681,8.0036976 47.424053,8.762845 47.132473,9.7023534 46.374729,10.100129L44.221188,11.232998C43.857914,10.249938,43.361851,9.3319101,42.749996,8.4998286L44.929535,7.352598C45.167145,7.2280803,45.422043,7.1708302,45.672386,7.174134z M22.363896,6.4076095C22.626137,6.4149718,22.889933,6.4890428,23.129202,6.6360126L25.232002,7.9243071C24.983002,8.2199299,24.749006,8.5292008,24.531187,8.8507769L24.325581,9.1660111 23.90588,9.0875378C23.275191,8.9828922 22.630835,8.9289981 21.976551,8.9289984 21.806952,8.9289981 21.637982,8.9326974 21.469708,8.9400355L21.134642,8.961946 21.056421,8.86325C20.704596,8.3712656 20.660401,7.6966183 20.996401,7.148304 21.275526,6.6914134 21.754027,6.4305587 22.251677,6.4085212 22.289,6.4068675 22.326431,6.4065576 22.363896,6.4076095z M33.776642,5.6300144C33.857746,5.6301656 33.939079,5.6313434 34.020622,5.6335616 39.242119,5.7754722 43.354096,10.120625 43.213494,15.340688 43.169556,16.971583 42.715012,18.494635 41.951519,19.813686L41.865544,19.954264 41.657589,19.790779C39.818852,18.414998 37.537552,17.599476 35.068867,17.599476 34.66787,17.599476 34.261574,17.622975 33.857876,17.668475 32.385815,13.866086 29.595959,11.014804 26.199589,9.711732L26.027451,9.6495949 26.288347,9.2962282C28.025217,7.0564322,30.74283,5.6243949,33.776642,5.6300144z M40.950501,2.1025438C41.249367,2.0946379 41.553959,2.1728296 41.825966,2.345706 42.547425,2.8067083 42.760941,3.7664986 42.298603,4.4892459L40.963902,6.5819988C40.17614,5.913939,39.294472,5.3526425,38.339997,4.9202757L39.681301,2.8197699C39.9702,2.3680468,40.452393,2.1157179,40.950501,2.1025438z M27.487883,1.5610685C28.038714,1.5662165,28.569157,1.8650618,28.844816,2.3860846L30.014,4.5899091C29.027098,4.9434433,28.105391,5.4317431,27.266875,6.033999L26.101694,3.8418369C25.700634,3.0827036 25.989704,2.1438856 26.746128,1.7428083 26.982916,1.6170683 27.237505,1.5587282 27.487883,1.5610685z M34.094422,0.00037670135C34.120896,-0.00020503998 34.147526,-0.00011539459 34.174298,0.00065898895 35.032345,0.022751808 35.706803,0.73763561 35.683403,1.593688L35.615608,4.0929995C35.11044,4.0083418 34.593571,3.9536633 34.066303,3.9406538 33.541535,3.9269848 33.023365,3.952373 32.512997,4.0096216L32.580693,1.5110502C32.603359,0.67979717,33.273724,0.018374443,34.094422,0.00037670135z" />

  22:     </ListBoxItem>

  23: </ListBox>

After this, also add a Pivot to the page, but don’t add a HeaderTemplate ( we don’t need one ). The one in our solution looks like this:

   1: <controls:Pivot x:Name="ContentPivot"

   2:                 Grid.Row="1"

   3:                 Margin="0,-12,0,0"

   4:                 SelectionChanged="ContentPivot_SelectionChanged"                            

   5:                 >

   6:     <controls:PivotItem>

   7:         <StackPanel>

   8:             <TextBlock Text="RAIN" Style="{StaticResource PhoneTextTitle2Style}" FontWeight="Bold"/>

   9:             <RichTextBox Margin="0,12,0,0">

  10:                 <Paragraph>

  11:                     <Run>

  12:                         Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc sit amet interdum dolor. In ornare vulputate nulla, eu scelerisque quam luctus eget. Aenean pharetra quam id risus suscipit, et mattis massa tincidunt. Nunc euismod convallis sem sit amet tristique. Nam tempus venenatis dolor. Nam urna diam, rhoncus at imperdiet non, ultrices eget libero. Cras volutpat metus in purus fringilla, nec porttitor est volutpat. Vestibulum sollicitudin lorem nulla, at tempor elit pellentesque eu. Donec porta, eros eu hendrerit porttitor, ipsum nunc sodales lacus, et cursus diam arcu vel enim. Sed rutrum urna erat, a elementum arcu hendrerit vitae. Morbi ante turpis, vulputate sit amet nisi vel, pharetra pellentesque lorem. Morbi scelerisque luctus eleifend. Sed varius vel odio id tristique. Cras a sem nec magna placerat elementum sollicitudin eget mi.

  13:                     </Run>

  14:                 </Paragraph>

  15:             </RichTextBox>

  16:         </StackPanel>

  17:     </controls:PivotItem>

  18:     <controls:PivotItem>

  19:         <StackPanel>

  20:             <TextBlock Text="CLOUDY" Style="{StaticResource PhoneTextTitle2Style}" FontWeight="Bold"/>

  21:             <RichTextBox Margin="0,12,0,0">

  22:                 <Paragraph>

  23:                     <Run>

  24:                         Sed bibendum vehicula quam. Mauris id massa non erat vehicula feugiat id eget nibh. Donec quis neque in lorem pellentesque feugiat at ac tortor. Suspendisse pulvinar justo et fermentum hendrerit. Morbi sed dolor at mauris tempus blandit eget non neque. Maecenas lobortis varius diam, ut pharetra mi vestibulum eu. Vivamus ornare ut est nec dapibus. Duis ultrices in dui sit amet luctus. Pellentesque pretium, mauris vel porta vehicula, ipsum felis ullamcorper nibh, sed fermentum nibh libero a erat. In volutpat lorem cursus risus hendrerit, et imperdiet lorem consectetur. Mauris eu urna in leo tempus convallis et cursus neque. Donec at lacinia eros, ac gravida magna. Praesent enim mauris, tempor nec pharetra at, lobortis sed nulla.

  25:                     </Run>

  26:                 </Paragraph>

  27:             </RichTextBox>

  28:         </StackPanel>

  29:     </controls:PivotItem>

  30:     <controls:PivotItem>

  31:         <StackPanel>

  32:             <TextBlock Text="STORM" Style="{StaticResource PhoneTextTitle2Style}" FontWeight="Bold"/>

  33:             <RichTextBox Margin="0,12,0,0">

  34:                 <Paragraph>

  35:                     <Run>

  36:                         Integer eget molestie lacus. Nam facilisis purus sed vehicula rutrum. Integer auctor imperdiet lectus a ultricies. Vivamus erat magna, feugiat et cursus nec, feugiat id enim. Aliquam tempus, turpis sed gravida pretium, sapien arcu commodo massa, ut congue tellus urna eu magna. Sed ligula tellus, consectetur et ornare id, porttitor congue libero. Morbi justo neque, varius ac commodo a, fermentum a arcu. Integer lectus mi, ultrices nec dui cursus, molestie dictum quam. Pellentesque mollis, eros at blandit vulputate, arcu massa tincidunt risus, at tempor urna metus at nulla. Morbi ultricies sem metus, at aliquet purus faucibus sed.

  37:                     </Run>

  38:                 </Paragraph>

  39:             </RichTextBox>

  40:         </StackPanel>

  41:     </controls:PivotItem>

  42:     <controls:PivotItem>

  43:         <StackPanel>

  44:             <TextBlock Text="DRIZZLE" Style="{StaticResource PhoneTextTitle2Style}" FontWeight="Bold"/>

  45:             <RichTextBox Margin="0,12,0,0">

  46:                 <Paragraph>

  47:                     <Run>

  48:                         Phasellus molestie volutpat ante pulvinar convallis. Suspendisse tincidunt ante quis arcu dignissim, nec condimentum nisi ultrices. Nulla ac arcu ultrices ipsum euismod pretium. Cras euismod tristique leo volutpat consequat. Maecenas porta aliquet sodales. Nulla lacinia sed lectus id interdum. Quisque mauris sem, commodo eu ultricies eget, mollis id velit. Donec non leo in justo ultrices congue. Etiam mattis, justo sit amet feugiat eleifend, turpis libero tempus risus, sit amet pellentesque nisl diam sit amet elit. Nulla sit amet nunc eu turpis vehicula fermentum.

  49:                     </Run>

  50:                 </Paragraph>

  51:             </RichTextBox>

  52:         </StackPanel>

  53:     </controls:PivotItem>

  54: </controls:Pivot>

Now there are several things going on in here! You will see that both the listbox and the pivot have a SelectionChanged event, these are needed to keep the image listbox and the pivot in sync on what is selected. This is very easy to do, we just need to set the selectedindex across!

The code looks like this:

   1: private void ImageBar_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)

   2: {

   3:     this.ContentPivot.SelectedIndex = this.ImageBar.SelectedIndex;

   4: }


   6: private void ContentPivot_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)

   7: {

   8:     this.ImageBar.SelectedIndex = ((Pivot)sender).SelectedIndex;

   9: }

Now when you would run this, nothing will happen with your headers, because we need to give the selected icon a colour when it’s selected. This is done by giving the listbox a custom ItemContainerStyle in there we are going to use the visual states of the listbox to set the colour when an item is selected! In our example we the PhoneAccentColor for the selected state and PhoneInactiveColor for the unselected state…

For this trick to work we needed to create our custom Path Control! Because using a Style Template will try to set/change the ForeGround of a ContentControl, but a default Path control only has a Fill property! The custom control will use template binding to take the ForeGround value and use this as the Fill value. We do the same for the Content itself, this is used for the Data property of the Path control.

   1: <Style x:Key="ListBoxItemStyle" TargetType="ListBoxItem">

   2:     <Setter Property="Background" Value="Transparent"/>

   3:     <Setter Property="BorderThickness" Value="0"/>

   4:     <Setter Property="BorderBrush" Value="Transparent"/>

   5:     <Setter Property="Padding" Value="0"/>

   6:     <Setter Property="HorizontalContentAlignment" Value="Center"/>

   7:     <Setter Property="VerticalContentAlignment" Value="Top"/>

   8:     <Setter Property="Template">

   9:         <Setter.Value>

  10:             <ControlTemplate TargetType="ListBoxItem">

  11:                 <Border x:Name="LayoutRoot" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" HorizontalAlignment="{TemplateBinding HorizontalAlignment}" VerticalAlignment="{TemplateBinding VerticalAlignment}">

  12:                     <VisualStateManager.VisualStateGroups>

  13:                         <VisualStateGroup x:Name="CommonStates">

  14:                             <VisualState x:Name="Normal"/>

  15:                             <VisualState x:Name="MouseOver"/>

  16:                         </VisualStateGroup>

  17:                         <VisualStateGroup x:Name="SelectionStates">

  18:                             <VisualState x:Name="Unselected">

  19:                                 <Storyboard>

  20:                                     <ColorAnimation Duration="0" To="{StaticResource PhoneInactiveColor}" Storyboard.TargetProperty="(Control.Background).(SolidColorBrush.Color)" Storyboard.TargetName="ContentContainer" d:IsOptimized="True"/>

  21:                                     <ColorAnimation Duration="0" To="{StaticResource PhoneInactiveColor}" Storyboard.TargetProperty="(Control.Foreground).(SolidColorBrush.Color)" Storyboard.TargetName="ContentContainer" d:IsOptimized="True"/>

  22:                                 </Storyboard>

  23:                             </VisualState>

  24:                             <VisualState x:Name="Selected">

  25:                                 <Storyboard>

  26:                                     <ColorAnimation Duration="0" To="{StaticResource PhoneAccentColor}" Storyboard.TargetProperty="(Control.Foreground).(SolidColorBrush.Color)" Storyboard.TargetName="ContentContainer" d:IsOptimized="True"/>

  27:                                 </Storyboard>

  28:                             </VisualState>

  29:                             <VisualState x:Name="SelectedUnfocused">

  30:                                 <Storyboard>

  31:                                     <ColorAnimation Duration="0" To="{StaticResource PhoneAccentColor}" Storyboard.TargetProperty="(Control.Foreground).(SolidColorBrush.Color)" Storyboard.TargetName="ContentContainer" d:IsOptimized="True"/>

  32:                                 </Storyboard>

  33:                             </VisualState>

  34:                         </VisualStateGroup>

  35:                     </VisualStateManager.VisualStateGroups>

  36:                     <ContentControl x:Name="ContentContainer" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" Foreground="#FF1BA1E2" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" Background="Black" BorderBrush="Black"/>

  37:                 </Border>

  38:             </ControlTemplate>

  39:         </Setter.Value>

  40:     </Setter>

  41: </Style>    

Do note in this style, we also set the HorizontalContentAlignment to Center so each icon takes center place in the list item…

   1: <Style TargetType="controls:PathControl">

   2:     <Setter Property="Template">

   3:         <Setter.Value>

   4:             <ControlTemplate TargetType="controls:PathControl">

   5:                 <Grid>

   6:                     <Path Data="{TemplateBinding Content}"

   7:                             Fill="{TemplateBinding Foreground}"/>

   8:                 </Grid>

   9:             </ControlTemplate>

  10:         </Setter.Value>

  11:     </Setter>

  12: </Style>

But still, when you would run this, each icon will stick to the next! So how do we divide them evenly. You could calculate this and add margins or change something else… But in our example we again use a special control derived from Panel, called SplitPanel and use this as ItemsPanelTemplate on the listbox.

This control will calculate the total amount of space available and evenly change the width of each item to match this.

   1: public class SplitPanel : Panel

   2: {

   3:     protected override Size MeasureOverride(Size availableSize)

   4:     {

   5:         // the final measure size is the available size for the width, and the maximum

   6:         // desired size of our children for the height

   7:         Size finalSize = new Size { Width = availableSize.Width };


   9:         if (this.Children.Count != 0)

  10:             availableSize.Width /= (double)this.Children.Count;


  12:         foreach (var current in this.Children)

  13:         {

  14:             current.Measure(availableSize);


  16:             Size desiredSize = current.DesiredSize;

  17:             finalSize.Height = Math.Max(finalSize.Height, desiredSize.Height);

  18:         }


  20:         // make sure it will works in design time mode

  21:         if (double.IsPositiveInfinity(finalSize.Height) || double.IsPositiveInfinity(finalSize.Width))

  22:             return Size.Empty;


  24:         return finalSize;

  25:     }


  27:     protected override Size ArrangeOverride(Size arrangeSize)

  28:     {

  29:         Rect finalRect = new Rect(new Point(), arrangeSize);

  30:         double width = arrangeSize.Width / this.Children.Count;


  32:         foreach (var child in this.Children)

  33:         {

  34:             finalRect.Height = Math.Max(arrangeSize.Height, child.DesiredSize.Height);

  35:             finalRect.Width = width;


  37:             child.Arrange(finalRect);


  39:             // move each child by the width increment 

  40:             finalRect.X += width;

  41:         }


  43:         return arrangeSize;

  44:     }

  45: }

So when all is done, you should get following result: ( source code is up on my GitHub here… )



Fixing header when scrolling content on windows phone page

Thanks to Kévin Gosse @KooKiz for tweaking a part of the code to get it right :)

So, sometimes you want to try something different on Windows Phone and this time I wanted to tweak the scroll animation of a page.

The setup I was looking for visually: You have a content page ( for example in a news app ) that shows a nice article with an image and a title caption on top. But as you can see the text of the article is too large, so when the user scrolls down to view the text, we want to keep the header title in view! ( or something else depending on your taste )

TextPage TextPage2 TextPage3

You would think this would be easy, but it alas! Again we are faced with a stripped down XAML version in Windows Phone, so we don’t have the same ‘control’ over these events as in WPF.
Now with some small tricks, it’s possible to get a decent working version, so let’s get to it!

Just create a new Windows Phone app in Visual Studio, when doing so you’ll get a MainPage.xaml right away.

We are going to change the page, so delete everything inside the LayoutRoot grid and replace it with following xaml

   1: <Grid.RowDefinitions>

   2:     <RowDefinition Height="Auto"/>

   3:     <RowDefinition Height="*"/>

   4: </Grid.RowDefinitions>


   6: <!--TitlePanel contains the name of the application and page title-->

   7: <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,12">

   8:     <TextBlock Text="FIXED TITLE DEMO" Style="{StaticResource PhoneTextNormalStyle}" Margin="12,0"/>

   9:     <TextBlock Name="Values" />

  10: </StackPanel>


  12: <!--ContentPanel - place additional content here-->

  13: <Grid x:Name="ContentPanel" Grid.Row="1" Margin="0,0,0,0">

  14:     <ScrollViewer Name="ScrollViewer"

  15:                   Margin="0,0,0,12"

  16:                   VerticalAlignment="Top"

  17:                   VerticalScrollBarVisibility="Visible"

  18:                   ManipulationMode="Control"

  19:                   MouseMove="ScrollViewer_MouseMove"

  20:                   MouseLeftButtonUp="ScrollViewer_MouseLeftButtonUp">

  21:         <Grid>

  22:             <Grid.RowDefinitions>

  23:                 <RowDefinition Height="Auto" />

  24:                 <RowDefinition Height="*" />

  25:             </Grid.RowDefinitions>


  27:             <Image Name="Image"

  28:                    Source="/Assets/Images/bear.jpg"

  29:                    Grid.Row="0"

  30:                    />


  32:             <RichTextBox Name="Article"

  33:                          Grid.Row="1"

  34:                          Margin="12,12,12,0">

  35:                 <Paragraph>

  36:                     <Run FontWeight="Bold" FontSize="22">Never heard before, but a bear was found in the wild!</Run>

  37:                 </Paragraph>

  38:                 <Paragraph>

  39:                     <LineBreak />

  40:                     <Run>

  41:                         Bears are mammals of the family Ursidae. Bears are classified as caniforms, or doglike carnivorans, with the pinnipeds being their closest living relatives. Although only eight species of bears are extant, they are widespread, appearing in a wide variety of habitats throughout the Northern Hemisphere and partially in the Southern Hemisphere. Bears are found on the continents of North America, Central America, South America, Europe, and Asia.

  42:                         Common characteristics of modern bears include large bodies with stocky legs, long snouts, shaggy hair, plantigrade paws with five nonretractile claws, and short tails. While the polar bear is mostly carnivorous and the giant panda feeds almost entirely on bamboo, the remaining six species are omnivorous, with varied diets.

  43:                         With the exceptions of courting individuals and mothers with their young, bears are typically solitary animals. They are generally diurnal, but may be active during the night (nocturnal) or twilight (crepuscular), particularly around humans. Bears are aided by an excellent sense of smell, and despite their heavy build and awkward gait, they can run quickly and are adept climbers and swimmers. In autumn, some bear species forage large amounts of fermented fruits, which affects their behaviour.[1] Bears use shelters, such as caves and burrows, as their dens; most species occupy their dens during the winter for a long period (up to 100 days) of sleep similar to hibernation.[2]

  44:                         Bears have been hunted since prehistoric times for their meat and fur. With their tremendous physical presence and charisma, they play a prominent role in the arts, mythology, and other cultural aspects of various human societies. In modern times, the bears' existence has been pressured through the encroachment on their habitats and the illegal trade of bears and bear parts, including the Asian bile bear market. The IUCN lists six bear species as vulnerable or endangered, and even least concern species, such as the brown bear, are at risk of extirpation in certain countries. The poaching and international trade of these most threatened populations are prohibited, but still ongoing.

  45:                     </Run>

  46:                 </Paragraph>

  47:             </RichTextBox>

  48:         </Grid>

  49:     </ScrollViewer>


  51:     <Border Name="TitleBorder"

  52:             Background="#FF264778"

  53:             Margin="0,216,0,0"

  54:             Height="{Binding ElementName=TitleText, Path=Height}"

  55:             VerticalAlignment="Top">

  56:         <TextBlock Name="TitleText"

  57:                            Text="Bear found in the wild!" 

  58:                            Foreground="White"

  59:                            Margin="12"

  60:                            />

  61:     </Border>

  62: </Grid>

If you did this correctly ( also there should be a bear.jpg image inside the Assets/Images folder ;) ) you’ll get a nice page representing the article!


You’ll notice the page has a scrollviewer that contains everything except the header title! The trick we are going to use, is manipulating the location of the header title ourselves.

Do note that we need to se the ManipulationMode of the ScrollViewer to Control to get a smooth UI effect! ( see xaml above ) Otherwise the UI won’t be notified enough with the scrollviewer scroll values – is a performance optimization that we now need to bypass.

To manipulate the location of the header title, we need to keep track of the scroll offset of our scrollbar. But the scrollviewer itself doesn’t give use enough info, the way to get this info is by hooking into the ValueChanged event of the VerticalScrollBar that is inside the ScrollViewer !

So first we need to get hold of this VerticalScrollBar, this is done by using the VisualTreeHelper on our ScrollViewer and hook onto the ValueChanged event.

   1: _vBar = ((FrameworkElement)VisualTreeHelper.GetChild(ScrollViewer, 0)).FindName("VerticalScrollBar") as ScrollBar;

   2: _vBar.ValueChanged += _vBar_ValueChangedHandler;

Inside our ValueChangedHandler we will animate our header title to the corresponding vertical offset of our ScrollBar, but we will stop animating when the vertical offset is higher than the top position of our header title when the page was launched!! Because if our vertical offset has reached that value this means that the header title will be positioned at the top of our page!

The top value of the header title is called _borderTop and is calculated at page load.

   1: private void _vBar_ValueChangedHandler(object sender, RoutedPropertyChangedEventArgs<double> e)

   2: {

   3:     if (e.NewValue < _borderTop)

   4:     {

   5:         if (e.NewValue >= 0)

   6:             this.TitleBorder.SetVerticalOffset(0 - e.NewValue);

   7:     }

   8:     else

   9:         this.TitleBorder.SetVerticalOffset(0 - _borderTop);

  10: }

With that in place all is looking great, except for one thing: what to do when the user compresses the ScrollViewer by dragging the content down when the page is at it’s initial position. This behaviour is sometimes used in listboxes to get that ‘pull to refresh effect’. When someone does this on our page, the header title will stay at it’s position while the rest is dragged down… not a nice effect indeed. Let’s fix this.

This can be solved by hooking into 2 other events of the ScrollViewer: MouseMove and MouseLeftButtonUp. MouseMove to detect the compression and MouseLeftButtonUp to know when the user stops the compression.

When the compression occurs, you’ll notice a positive TranslateY value on the CompositeTransform of the ScrollViewer content, we’ll use this to also animate our header title. When the compression is stopper, we check if we had a positive TranslateY value and if so, we reset the position of the header title.

   1: private void ScrollViewer_MouseMove(object sender, MouseEventArgs e)

   2: {

   3:     UIElement scrollContent = (UIElement)this.ScrollViewer.Content;

   4:     CompositeTransform ct = scrollContent.RenderTransform as CompositeTransform;

   5:     if (ct != null && ct.TranslateY > 0)

   6:         this.TitleBorder.SetVerticalOffset(ct.TranslateY);

   7: }


   9: private void ScrollViewer_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)

  10: {

  11:     UIElement scrollContent = (UIElement)this.ScrollViewer.Content;

  12:     CompositeTransform ct = scrollContent.RenderTransform as CompositeTransform;

  13:     if (ct != null)

  14:     {

  15:         if(ct.TranslateY > 0)

  16:             this.TitleBorder.SetVerticalOffset(0);

  17:     }

  18: }

If all goes well, the end result should look like this


To not mis anything, the demo project is up on my github here https://github.com/Depechie/FixedTextScrollDemo


Facebook like settings pane with gestures – windows phone

In my last post I showed you how to mimic the settings pane used in the Facebook app on windows phone through use of Visual States ( read it here… ).
But that solution was not 100% the same as in the Facebook app, it missed drag gestures.

So, let’s add those :)

First you’ll need to add the Windows Phone Toolkit nuget ( http://phone.codeplex.com/ )! Because it has a GestureListener, that you can hook up in XAML to almost any control. It will give you events for all possible windows phone gestures.

   1: <Grid x:Name="Container" Background="Transparent">

   2:     <toolkit:GestureService.GestureListener>

   3:         <toolkit:GestureListener DragDelta="GestureListener_OnDragDelta" DragCompleted="GestureListener_OnDragCompleted" />

   4:     </toolkit:GestureService.GestureListener>

You’ll notice that with this, we are hooking into the DragDelta and DragCompleted events. We are going to use these events to animate our page.

In the OnDragDelta event we will check to see if there has been a Horizontal gesture detected and if so, depending on what pane is already open, show the animation on screen! Take a look at the code below and you’ll notice some tricks to get this done! Basically we calculate how far the user is dragging the content and if he get’s passed DragDistanceToOpen ( or ToClose ), we will start the rest of the animation.

If the user stops the drag gesture before reaching the given distance variable, we animate the screen to the given distance… ( through use of the SetHorizontalOffset method )

   1: private void GestureListener_OnDragDelta(object sender, DragDeltaGestureEventArgs e)

   2: {

   3:     if (e.Direction == System.Windows.Controls.Orientation.Horizontal && e.HorizontalChange > 0 && !_isSettingsOpen)

   4:     {

   5:         double offset = _feRoot.GetHorizontalOffset().Value + e.HorizontalChange;

   6:         if (offset > _dragDistanceToOpen)

   7:             this.OpenSettings();

   8:         else

   9:         {

  10:             _feRoot.SetHorizontalOffset(offset);

  11:             _feSettings.SetHorizontalOffset(offset);

  12:         }

  13:     }


  15:     if (e.Direction == System.Windows.Controls.Orientation.Horizontal && e.HorizontalChange < 0 && _isSettingsOpen)

  16:     {

  17:         double offsetRoot = _feRoot.GetHorizontalOffset().Value + e.HorizontalChange;

  18:         double offsetSettings = _feSettings.GetHorizontalOffset().Value + e.HorizontalChange;

  19:         if (offsetRoot < _dragDistanceToClose)

  20:             this.CloseSettings();

  21:         else

  22:         {

  23:             _feRoot.SetHorizontalOffset(offsetRoot);

  24:             _feSettings.SetHorizontalOffset(offsetSettings);

  25:         }

  26:     }

  27: }

All this does is just animating the screen ‘while’ dragging.

Now next, we use the OnDragCompleted to finalize our animation, it’s almost the same code as above. The only difference now is that if the user has stopped his gesture and he has not passed the given distance, we will now reset the content to it’s original position. Giving us a ‘snap back’ animation.

   1: private void GestureListener_OnDragCompleted(object sender, DragCompletedGestureEventArgs e)

   2: {

   3:     if (e.Direction == System.Windows.Controls.Orientation.Horizontal && e.HorizontalChange > 0 && !_isSettingsOpen)

   4:     {

   5:         if (e.HorizontalChange < _dragDistanceToOpen)

   6:             this.ResetLayoutRoot();

   7:         else

   8:             this.OpenSettings();

   9:     }


  11:     if (e.Direction == System.Windows.Controls.Orientation.Horizontal && e.HorizontalChange < 0 && _isSettingsOpen)

  12:     {

  13:         if (e.HorizontalChange > _dragDistanceNegative)

  14:             this.ResetLayoutRoot();

  15:         else

  16:             this.CloseSettings();

  17:     }

  18: }

To do the actual animation of the pages, we use the Animate method on the given grids ( RootGrid and SettingGrid ), here the OpenSettings example…

   1: private void OpenSettings()

   2: {

   3:     var trans = _feRoot.GetHorizontalOffset().Transform;

   4:     trans.Animate(trans.X, 380, TranslateTransform.XProperty, 300, 0, new CubicEase

   5:         {

   6:             EasingMode = EasingMode.EaseOut

   7:         });


   9:     trans = _feSettings.GetHorizontalOffset().Transform;

  10:     trans.Animate(trans.X, 480, TranslateTransform.XProperty, 300, 0, new CubicEase

  11:     {

  12:         EasingMode = EasingMode.EaseOut

  13:     });


  15:     _isSettingsOpen = true;

  16: }

Now… if you’ll try this in your code you will see that some methods are missing! This because we are using some extension methods. I’m not taking credit for this, they are created by Colin Eberhardt, you can follow him on twitter https://twitter.com/ColinEberhardt or take a look at his blog http://www.scottlogic.com/blog/ceberhardt/ !

The extension methods used are: SetHorizontalOffset, GetHorizontalOffset and Animate the actual code is in the example provided below.

So with all this in place you will get a nice settings pane example with drag gestures enabled, just like the facebook app… if they do it the same way? I doubt it ;)


Everything is up on my github, just take a look here https://github.com/Depechie/SettingsPageAnimation



Facebook like settings pane – windows phone

So, we all took a good look at the ‘new design’ we got with the new Facebook app for Windows Phone and what is one of the most remarkable changes? That the settingspane now slides in/out from the left… Take a look at it here:


So not a different page like we are used too in Windows Phone. So let’s get started at creating our own version!

Just fire up your Visual Studio and create a new Databound Windows Phone app, it will already contain a MainPage with some data on it.

2013-07-22 23_06_51-Start Page - Microsoft Visual Studio

Open up this MainPage.xaml and we are going to add a second Grid that is placed outside the viewingfield on the left side. So we will set it’s margin to –480. The complete xaml looks like this:

   1: <Grid x:Name="Container" Background="Transparent">

   2:     <Grid x:Name="SettingsPane"

   3:             Margin="-480,0,0,0"

   4:             Grid.Row="0">

   5:         <Grid.RowDefinitions>

   6:             <RowDefinition Height="Auto"/>

   7:             <RowDefinition Height="*"/>

   8:         </Grid.RowDefinitions>


  10:         <StackPanel Grid.Row="0" Margin="12,17,0,28">

  11:             <TextBlock Text="MY APPLICATION" Style="{StaticResource PhoneTextNormalStyle}"/>

  12:             <TextBlock Text="settings" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>

  13:         </StackPanel>

  14:     </Grid>


  16:     <Grid x:Name="LayoutRoot" Grid.Row="0">

  17:         <Grid.RowDefinitions>

  18:             <RowDefinition Height="Auto"/>

  19:             <RowDefinition Height="*"/>

  20:         </Grid.RowDefinitions>


  22:         <StackPanel Grid.Row="0" Margin="12,17,0,28">

  23:             <TextBlock Text="MY APPLICATION" Style="{StaticResource PhoneTextNormalStyle}"/>

  24:             <TextBlock Text="page name" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>

  25:         </StackPanel>


  27:         <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">

  28:             <phone:LongListSelector x:Name="MainLongListSelector" Margin="0,0,-12,0" ItemsSource="{Binding Items}" SelectionChanged="MainLongListSelector_SelectionChanged">

  29:                 <phone:LongListSelector.ItemTemplate>

  30:                     <DataTemplate>

  31:                         <StackPanel Margin="0,0,0,17">

  32:                             <TextBlock Text="{Binding LineOne}" TextWrapping="Wrap" Style="{StaticResource PhoneTextExtraLargeStyle}"/>

  33:                             <TextBlock Text="{Binding LineTwo}" TextWrapping="Wrap" Margin="12,-6,12,0" Style="{StaticResource PhoneTextSubtleStyle}"/>

  34:                         </StackPanel>

  35:                     </DataTemplate>

  36:                 </phone:LongListSelector.ItemTemplate>

  37:             </phone:LongListSelector>

  38:         </Grid>

  39:     </Grid>

  40: </Grid>

Next we just need to toggle between the 2 pages… to get this done, we’ll add an appbar with an appbar icon. Fastest way to do this is to open up the app in Blend! This can be don by right clicking the project and selecting Open in Blend…

2013-07-22 23_32_09-SettingsPageAnimation - Microsoft Visual Studio

In Blend be sure that the MainPage.xaml is open and on the Objects and Timeline pane, right click on PhoneApplicationPage and select Add ApplicationBar


After that, open the added ApplicationBar node, right click on [ApplicationBar] and select Add ApplicationBarIconButton


This will already add the icon, but to get it right select the correct Settings icon uri inside the Properties pane of the ApplicationBarIconButton


Now we need to add Visual Stats to get the facebook like behaviour! So click on the States tab on the top left side and press the Add state group button, this will add a VisualStateGroup with a Default transition inside it.


For good reference, rename the group to SettingsStateGroup ( by double clicking on the name ). Also press the Add State button twice and rename those states to SettingsOpenState and SettingsClosedState


Now select the SettingsOpenState as active item and when you do this, you will notice a ‘recording is on’ message inside the design pane! This is needed, because we are going to tweak the layout…


When the recording of SettingsOpenState is active, select the LayoutRoot grid and in the Properties tab, change the Projection – Global Offset – X value to 380


After that, select the SettingsPane grid and also change the Global Offset – X value but now to 480!

With that done there is still one thing to do in Blend and that is add an EasingFunction to the transition. This can be don by clicking the EasingFunction button of the Default Transition inside the SettingsStateGroup


In this example I select the Cubic in/out one, but you can select what you want as visual effect…


Also not forget the time frame, I’m going for 0.3sec, but also here, just toy with it…


Save the project and close Blend, now you should be back in Visual Studio, be sure to reload the project!

After this, add a Click event to the application bar icon and inside the event add following code.

   1: private bool _isSettingsOpen = false;

   2: private void ApplicationBarIconButton_OnClick(object sender, EventArgs e)

   3: {

   4:     if (_isSettingsOpen)

   5:     {

   6:         VisualStateManager.GoToState(this, "SettingsClosedState", true);

   7:         _isSettingsOpen = false;

   8:     }

   9:     else

  10:     {

  11:         VisualStateManager.GoToState(this, "SettingsOpenState", true);

  12:         _isSettingsOpen = true;

  13:     }

  14: }

That’s it!! When everything went to plan, you should now see a nice visual effect when pressing the appbar icon, opening and closing the settings pane!


Link to project: SettingsPageAnimation project