Making WP8.1 JumpLists Better (Updated)

MakingJumpListBetter

Update: Want to get the JumpList experience the easy way? Check out my new controls and download them from nuget.

In a previous post, I outlined how to create a fully functioning JumpList in a WinRT WP app. I provided a helper class to do the heavy lifting of grouping and sorting as well as all of the styles and templates needed to get the correct UX so it can be consistent with the OS. While it works and looks great, it’s far from perfect. The JumpList doesn’t properly support globalization, it is unreadable when high contrast mode is enabled on the device, the performance is quite slow, and it doesn’t automatically support landscape orientation.

The good news is that I’ve fixed the high contrast theming and globalization issues. I’ve also managed improve the performance of the JumpList quite a bit. It’s not perfect and it still needs some work but I think it’s a good enough work around to release. The bad news is I still have no elegant solution for orientation changes without writing some code behind or an attached behavior. So let’s focus on the good news and get to fixing on what we can.

The Material

VS_IconSAMPLE SOLUTION – see it in action
Code_IconRESOURCES – styles, templates, and code to copy+paste
News_IconPART 1 – Jumplists in Windows Phone 8.1
News_IconPART 2 – Making WP8.1 JumpLists Better (You are here)

Globalization

This is a relatively straightforward issue that has a relatively straightforward solution. At the time of writing my JumpListHelper class, I attempted to write the ToAlphaGroups method to be as robust as it can be. This meant that it had to handle diacritics (accents above a letter), numbers as a single group, and other characters that can’t be classified (grouped under the globe icon). I wrote everything from scratch and due to time constraints, I only ended up supporting English.

Supporting only English works fine for any Latin script languages (with a-z alphabets). The English Alpha JumpList consist of a group for numbers and symbols under “#,” a group for each of the 26 English letters, and finally a group for everything else shown as a globe icon. Some non-English languages have additional groups specific for their languages and this is where my helper class fails. Even if the phone is set to Japanese, for example, it will throw all Japanese words into the “other” (globe) group. The expected behavior is to correctly group both Latin characters AND Japanese characters.

AlphaGroupingOpen AlphaGroupingOpenJP
English only JumpList grouping vs Japanese JumpList grouping on the same collection.

Thanks to Kinnara, a very helpful built in class was made aware to me that will handle this for me called CharacterGrouping. This class will provide the correct set of characters to group strings under for each of the supported languages. Not only that, it even contains a method that will classify the string into the proper group all while handling capitalization, diacritics, and any other string weirdness for me out of the box. So, I rewrote the ToAlphaGroups method using the CharacterGrouping class and now it supports proper dynamic grouping based on the phone’s display language, removed excess code handling diacritics, and works without needing any changes to the existing JumpList styles and templates. To take advantage of this, replace my previous JumpListHelper.cs file with the new one found in my updated sample project.

Supporting High Contrast

By default, all WinRT apps on WP is enabled to support High Contrast mode. This means that the app will switch out the styles to a more stark color palette for better readability. For the most part, the default style does pretty well at handling this automatically. Colors automatically change to white (in dark theme) or black (in light theme) so almost everything seems black and white. Unfortunately, with custom controls and custom styles, some things do not work out properly. You can see this for yourself by going to Settings>Ease of Access and turning on the high contrast toggle. This is what my original JumpList styles and templates look like when you enable high contrast.

AlphaGroupingContrastWrong AlphaGroupingOpenContrastWrong

Before we go any further, the first thing I did was move all of the JumpList specific styles and templates out into its own XAML file. The JumpList specific styles and templates are already getting pretty long and adding support for high contrast will only make it bigger and more unruly. To start, I created a new folder called “Styles” under “Assets” and then added a new XAML-only file (I added a new text file but renamed it with the extension .xaml).

Capture

The file should be blank so we need to add some content to it to get started. The root of the JumpList.xaml file should look like this:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

</ResourceDictionary/>

Previously, I had all of the styles and templates for my JumpLists in my App.xaml resources. I simply moved all of that into the new JumpList.xaml file then referenced it in my App.xaml global resources like so:

<Application x:Class="JumpListSample.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="using:JumpListSample">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="Assets/Styles/JumpList.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>
Note: Many style and elements were renamed, moved, and reworked. To ensure that my implementation will work for you, I suggest you remove my old styles, templates, and page xaml and re-implement where needed.

Now, let’s actually enable support for high contrast. Essentially, we need to define two ways the JumpList will look: the default look, and the high contrast look. In order to let the OS handle the switch between these two looks automatically, we use a ThemeDictionary. The first ThemeDictionary will be keyed as Default and the second will be keyed as HighContrast for obvious reasons. The OS specifically looks for these keys so don’t go changing it around. The top of our JumpList.xaml styles file will look like this now:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <ResourceDictionary.ThemeDictionaries>
        <ResourceDictionary x:Key="Default">
            <JumpListItemBackgroundConverter x:Key="JumpListItemBackgroundConverter" />
            <JumpListItemForegroundConverter x:Key="JumpListItemForegroundConverter" />
        </ResourceDictionary>
        <ResourceDictionary x:Key="HighContrast">
            <JumpListItemBackgroundConverter x:Key="JumpListItemBackgroundConverter" />
            <JumpListItemForegroundConverter x:Key="JumpListItemForegroundConverter" />
        </ResourceDictionary>
    </ResourceDictionary.ThemeDictionaries>
    <!-- styles and templates for JumpLists are below this point -->

Notice that I’ve moved the converters needed by the JumpList templates into each of the ThemeDictionaries. If you don’t do this, the app will immediately crash when trying to instantiate the converters. I’m not sure why that happens but it works this way so let’s roll with it.

Now, in each of the ThemeDictionaries, we simply define the values that are different between the two modes and then reference theme in our styles and templates. The OS will automatically apply the correct dictionary at runtime. The most obvious difference between the two are the colors but margins and borders are also different as well. I’ve ensured that it matches the OS’ native look in both mode so here are the values:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <ResourceDictionary.ThemeDictionaries>
        <ResourceDictionary x:Key="Default">
            <JumpListItemBackgroundConverter x:Key="JumpListItemBackgroundConverter" />
            <JumpListItemForegroundConverter x:Key="JumpListItemForegroundConverter" />
            <Thickness x:Key="JumpListItemBorderThickness">0</Thickness>
            <Thickness x:Key="JumpListItemTextMargin">9.5,0,0,9.5</Thickness>
            <Thickness x:Key="AlphaJumpListGroupTextMargin">5.5,0,0,9.5</Thickness>
            <SolidColorBrush x:Key="JumpListItemBackgroundBrush"
                             Color="{ThemeResource SystemColorControlAccentColor}" />
            <SolidColorBrush x:Key="JumpListItemTextForegroundBrush"
                             Color="White" />
        </ResourceDictionary>
        <ResourceDictionary x:Key="HighContrast">
            <JumpListItemBackgroundConverter x:Key="JumpListItemBackgroundConverter" />
            <JumpListItemForegroundConverter x:Key="JumpListItemForegroundConverter" />
            <Thickness x:Key="JumpListItemBorderThickness">2.5</Thickness>
            <Thickness x:Key="JumpListItemTextMargin">7,0,0,7</Thickness>
            <Thickness x:Key="AlphaJumpListGroupTextMargin">5.5,0,0,7</Thickness>
            <SolidColorBrush x:Key="JumpListItemBackgroundBrush"
                             Color="Transparent" />
            <SolidColorBrush x:Key="JumpListItemTextForegroundBrush"
                             Color="{ThemeResource SystemColorControlAccentColor}" />
        </ResourceDictionary>
    </ResourceDictionary.ThemeDictionaries>
    <!-- styles and templates for JumpLists are below this point -->

Now that we have defined the values that will change between the two modes, we need to update our templates to use these values. We need to do this for several of the templates to get the look right but here’s one example below. The highlighted lines shows the properties that have been changed to point to the resources we’ve created in the ThemeDictionaries above. To get the full updated styles and templates that support high contrast, download the updated sample project above and grab the JumpList.xaml file.

<DataTemplate x:Key="AlphaGroupHeaderTemplate">
    <Border Background="{ThemeResource JumpListItemBackgroundBrush}"
            BorderBrush="{ThemeResource PhoneAccentBrush}"
            BorderThickness="{ThemeResource JumpListItemBorderThickness}"
            Width="49.5"
            Height="49.5"
            HorizontalAlignment="Left"
            Margin="0,0,0,9.5">
        <TextBlock Text="{Binding Key}"
                   Foreground="{ThemeResource JumpListItemTextForegroundBrush}"
                   FontSize="39"
                   FontFamily="{StaticResource PhoneFontFamilySemiLight}"
                   TextLineBounds="Tight"
                   OpticalMarginAlignment="TrimSideBearings"
                   IsColorFontEnabled="False"
                   IsTextScaleFactorEnabled="False"
                   HorizontalAlignment="Left"
                   VerticalAlignment="Bottom"
                   Margin="{ThemeResource AlphaJumpListGroupTextMargin}" />
    </Border>
</DataTemplate>

Here’s how the JumpList looks after applying our changes in high contrast mode.
AlphaGroupingContrast AlphaGroupingOpenContrast

Note the app needs to be restarted for all changes to take effect. This is because the Alpha grid uses a converter to get its background and foreground colors and doesn’t refresh automatically.

Performance

While the new SemanticZoom control with the proper style and setup gives us a very good looking JumpList with proper animations and all. The problem is it’s quite a bit slower than the system’s JumpLists when displaying the grid of letters. The purpose of providing a JumpList is to enable the user to jump to a section of the list quickly to get to the items they need quickly. During my test of the Alpha JumpList on a Lumia 920 device, it takes a very noticeable pause the first time the grid of letters is presented and chugs when it’s closed or opened in subsequent times.

The main cause of the performance hit is that the GridView that hosts the letters have to immediately render all 28 squares and then play a fancy animation to present them. The Generic JumpList doesn’t have as much of a problem since it only has present around 12 items in a list so we’ll only be addressing the Alpha JumpList. To speed up the animation performance, what we can do is set the GridView that hosts the 28 letter groups to have a BitmapCache cache mode.

<SemanticZoom.ZoomedOutView>
    <GridView ItemsSource="{Binding Collection.View.CollectionGroups}"
              ItemTemplate="{StaticResource AlphaJumpListItemTemplate}"
              CacheMode="BitmapCache" />
</SemanticZoom.ZoomedOutView>

This forces the GridView to be cached to the GPU and doesn’t get rendered on the CPU every frame redraw. This is only recommended on UIElements that are expected to be static and not animating. This means that this will have a drawback when the Alpha grid can scroll (like in the Japanese JumpList above). Setting its CacheMode property to BitmapCache will allow the reader board animation to play much more smoothly when the Alpha grid but the scrolling of the list (if it’s long enough) will look choppy since it’s being rendered on the GPU. One way around this is to set the Alpha grid’s CacheMode to BitmapCache before it’s shown and set it back to null after it has finished showing. I haven’t come up with a consistent solution using that method yet, but be sure I’ll post it once I’ve perfected it. For now, if you’re willing to deal with the choppy scrolling in non-Latin languages, use BitmapCache.

Note: The sample project will not have the BitmapCache applied to the Alpha grid. You can apply it in your own projects but do so on a case by case basis.

BitmapCache doesn’t solve the initial slowness of showing the Alpha grid though since it still needs to lay out and render all 28+ items. I don’t have a solution for this problem yet either but I’ve managed to simulate a feel of performance increase by invisibly showing the Alpha grid (forcing it to render and layout the items) and then closing it as fast as possible. This is kinda hacky but if I can get it to work consistently, I’ll post the solution along with the dynamic BitmapCache solution in the future.

Update: In addition to using BitmapCache, I’ve updated the styles in the JumpList.xaml file to include a more simple ItemContainerStyle for the Alpha grid provided by Kinnara. It dramatically reduces the visual tree elements that need to be rendered for each Alpha grid item and it dramatically reduces the initial slowness when showing the grid. Implementing this change also changed how the ZoomedOutView property is defined in the SemanticZoom since I combined everything into a ListView style and a GridView style. In pages where you want a JumpList, it should look something like this with the new styles:

<SemanticZoom Style="{StaticResource AlphaJumpListStyle}"
              Margin="19,0,0,0">
    <SemanticZoom.ZoomedInView>
        <!--The usual ListView stuff with GroupStyles-->
    </SemanticZoom.ZoomedInView>
    <SemanticZoom.ZoomedOutView>
        <GridView ItemsSource="{Binding Collection.View.CollectionGroups}"
                  Style="{StaticResource AlphaJumpListPickerStyle}" />
    </SemanticZoom.ZoomedOutView>
</SemanticZoom>
<SemanticZoom Style="{StaticResource GenericJumpListStyle}"
              Margin="19,0,0,0">
    <SemanticZoom.ZoomedInView>
        <!--The usual ListView stuff with GroupStyles-->
    </SemanticZoom.ZoomedInView>
    <SemanticZoom.ZoomedOutView>
        <ListView ItemsSource="{Binding Collection.View.CollectionGroups}"
                  Style="{StaticResource GenericJumpListPickerStyle}" />
    </SemanticZoom.ZoomedOutView>
</SemanticZoom>
Advertisements

19 thoughts on “Making WP8.1 JumpLists Better (Updated)

    1. Once again, you and your boundless wisdom in XAML has helped me solve another problem. I will incorporate this into the JumpList solution when I can. Out of curiosity, did you test this on a physical device? It’s pretty quick on a Lumia 920 but I’m wondering how it’ll fare on a low end device like a 520.

      1. Glad I could help! I tested on Lumia 1020 and 625 and they showed similar performance. Maybe on the 625 it took a little bit longer to open initially but the animations were pretty smooth (smoother than 1020, because of the lower resolution I think) even without bitmap caching.

      2. That makes sense. The delay on initial opening needs to do layout calculations on the CPU and the 1020 has a faster one. The animation is also not 100% smooth without bitmap caching on my 920 (same resolution as 1020 I think) but the initial delay is much better now, and often unnoticeable, with your fix.

  1. After implementing everything from this article the JumpList shows up. I can press a letter to open the ZoomedOutView. But whenever I tap on a letter to go to that letter, the ZoomedOutView just closes and doesn’t go to the selected letter. The ListView stays where it was…

    1. Hi, I can’t give you a definitive answer without seeing the code for myself. If you want and are able to, feel free to send me a sample solution that exhibits this issue to qdev@live.com so I can take a look. You can also try to copy and paste the xaml for the SemanticZoom control here for a quick look.

      That being said, a few things come to mind that might be causing this.

      Are you able to scroll your ZoomedInView ListView manually using touch? If not, there could be a layout issues.
      Is both your ZoomedInView and ZoomedOutView bound to the same instance of the CollectionViewSource object? For example, in my sample solution, the ItemsSource of both is bound to different properties of the same “Collection” object but “Collection” is the same instance used for both.

      1. Thanks for the reponse. I didn’t check if the collection was null in the Collectionviewsource, checking if it was null before creating the collectionviewsource solved it.

    2. A common issue is having ZoomedOutView not bound to the right list of groups.

      You could try binding ItemsSource of your “JumpList” in code behind like this:

      Private Sub MainPage_Loaded(sender As Object, e As RoutedEventArgs) Handles Me.Loaded
      TryCast(SemanticZoom1.ZoomedOutView, ListViewBase).ItemsSource = CollectionViewSource1.View.CollectionGroups
      End Sub

      1. Can you be more specific about the issue and what’s happening that seems wrong? I’m not experiencing any issues with binding through XAML. Binding through XAML is the usual go-to way of binding in MVVM.

      2. I took a look at your example and it doesn’t seem to use any form of MVVM pattern. Your example is more of a code-behind approach and your solution of assigning the ItemsSource is the preferred way of doing things in this pattern. MVVM is vastly different than this and offers many benefits including decoupling data from the view (XAML). If you look at my JumpList sample, I have pretty much no code behind other than navigation stuff shipped with the templates. Look into MVVM programming pattern if you want to use binding from XAML.

      3. @Q I have to disagree with you. My data is decoupled from the view. It is very much MVVM.

        I simply placed the viewmodel classes (GroupedListClass, GroupClass, ItemClass) in the same file (MainPage.xaml.vb); Maybe this is confusing you as normally one would have then on their own files; This does not make a difference to the compiler though. MainPage.GroupedList would be the instance of my viewmodel. And you can see how during runtime (in response to button clicks) the new groups and items are added to ObservableCollections on my viewmodel; Not straight to the ListView. The non MVVM way would be populating the ListView with New ListViewItems through code; Not with binding through ItemsSource.

        Maybe you could take another look at my code?

      4. I did jump the gun. I assumed your solution was supposed to be in a broken state so when I saw the ItemsSource assignment in code behind, I assumed that was how all of it was written. It was my mistake and I probably shouldn’t be looking at code when I’m really tired. After taking a closer look, the ZoomedOutView needs access to CollectionGroups property of the CollectionViewSource.View. Because I define the CollectionViewSource in the ViewModel, I can get to the property easily like ItemsSource=”{Binding collectionViewSource.View.CollectionGroups}”. Because you defined yours as a XAML object, you have to get to it like ItemsSource=”{Binding Source={StaticResource groupedItemsViewSource}, Path=CollectionGroups}.”

  2. Good stuff. Is this up in source control anywhere (eg, github)? Also, prior to checking out your solution I had rolled my own which is bound to ObservableCollection, with the obvious benefit of being able to add items later and have them pop up in the correct group. I’d like to use your jump list implementation with this behavior. Is this possible? Could it be added?

    1. Hacking on it a bit, I’ve got a provisional version based on JumpListHelper from your demo solution. It would be great if I could work off of a fork of your actual code though. Ideally on github.

    2. All of this post’s source code is included in the sample solution link under “The Material” section at the top. There is also similar, more advance code used in my toolkit available on NuGet that I will release the source code some time in the future. Regarding an ObservableCollection-like behavior with JumpLists, it’s technically possible. Right now, my JumpListGroup class derives from List but we can easily create a similar JumpListObservableGroup class that derives from ObservableCollection instead. You can then manually add items to the correct JumpListObservableGroup and it’ll pop up on the UI as you’d expect. Of course, we can take it further by creating a class that monitors a flat list and automatically handle the grouping in conjunction with the adding and removing of items. I will try this in my toolkit in the near future.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s