Programming the Task Bar in Windows 7 with WPF 4, Part Two – Jump Lists

By Alessandro Del Sole– Microsoft MVP

Download the code

Introduction
Understanding the Jump List
Interacting with the Jump List from managed code
Handling events
Interacting with the Jump List via XAML
Conclusion

Introduction

In the first article of this series about programming the Windows 7’s task bar with WPF 4, I discussed thumbnail buttons and showed how you can enrich your application’s Windows 7 task bar icon adding interactive features. In this second article of the series I will describe the Jump List, another new feature in the task bar, and show how you can programmatically interact with it by adding your own list of elements. If you are new to Windows 7, you are probably wondering what the Jump List is. When you right click an application’s icon in the task bar, you can now easily access a number of items such as recent files or frequent tasks. Figure 1 shows an example of a Jump List for Microsoft Word 2010 whereas Figure 2 shows an example for Microsoft Outlook 2010.

Figure 1 – Microsoft Word’s Jump List

Figure 2 – Microsoft Outlook’s Jump List

As you can see the Jump List provides shortcuts for accessing most common items or recent files with regard to the application. Moreover, such items can be organized into subcategories providing a better user experience. In this article I will show you how you can create these Jump Lists for your WPF applications. But before writing code, it is important to understand how the Jump List can be accessed from WPF and what kind of items it supports.

Understanding the Jump List

The Jump List supports two kinds of items: Tasks and Paths. Tasks are basically shortcuts to other applications or to specific functionalities of the running application whereas Paths are simply shortcuts to files or folder. Tasks and Paths can be organized into categories. By default both kinds of items are placed inside a category named Tasks (see Figure 2 for an example) but you can also take advantage of two built-in categories named Recent (which generally stores the list of most recently used files) and Frequent (which instead stores the list of the most frequently used features or files). Finally, you can create your custom categories if the built-in ones do not suite your needs. Talking in terms of .NET code, accessing the Jump List is possible by creating an instance of the System.Windows.Shell.JumpList class. A Task is represented by an instance of the System.Windows.Shell.JumpTask class whereas a Path is represented by an instance of the System.Windows.Shell.JumpPath class. Both expose properties that you can assign in order to add your items to the Jump List. In this article I will not cover paths in detail, but they are essentially just links to files; it is instead more interesting to understand how to work with tasks and how to customize them. Basically you have two ways for interacting with the Jump List in code. The first way is by writing XAML while the second one is by writing managed code, so that you can interact with the Jump List at runtime. This is a very common scenario since you might have the need to add items to the Jump List at runtime like the name of each file the user opened from your application. In this article I will describe both ways.

Interacting with the Jump List from managed code

The first step is to create a new WPF 4 project with Microsoft Visual Studio 2010. The goal of the sample project is to simply open text documents and add their names or references to the Jump List in different ways. Listing 1 shows the XAML code that defines a very simple user interface for the sample application.

<Window x:Class="MainWindow"
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525">
    
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition Height="40"/>
        </Grid.RowDefinitions>
        <TextBox Name="DocumentTextBox" Grid.Row="0" HorizontalScrollBarVisibility="Auto" 
                 VerticalScrollBarVisibility="Auto" />
        <Button Name="OpenButton" Width="100" Height="30" Content="Open file" Grid.Row="1"/>
    </Grid>

</Window>

Listing 1

The code declares a TextBox element that will show any document that the user will load by clicking the button. At this point go to the code-behind file. Listing 2 shows how to implement the Click event handler for the button and declares some useful variables. You can take a look at the comments that provide some further explanation.

'The following is required for working with the Task Bar
Imports System.Windows.Shell
Imports Microsoft.Win32
Class MainWindow

    'Stores the currently opened file name
    Private currentFileName As String

    'Allows interaction with the Jump List
    Private WithEvents myJumpList As New JumpList
    Private Sub OpenButton_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles OpenButton.Click
        Dim openDialog As New OpenFileDialog
        With openDialog
            .Filter = "Text files|*.txt|All files|*.*"
            .Title = "Select a text document"
            If .ShowDialog = True Then
                Me.currentFileName = .FileName
                Try

                    'Shows the content of the selected document
                    Me.DocumentTextBox.Text = My.Computer.FileSystem.ReadAllText(Me.currentFileName

                    'Invokes a method that will add the item to the Jump List
                    createJumpListItems()

                    'You can handle specific exceptions here
                Catch ex As Exception
                    MessageBox.Show(ex.Message)

                End Try
            End If
        End With
    End Sub
End Class

Listing 2

Notice how an instance of the JumpList class is declared at class-level (myJumpList variable). This is important because the application will be associated to one Jump List. Also notice how the code invokes a method called createJumpListItems which is implemented in Listing 3 and whose job is to add the document information to the Jump List.

 

Private Sub createJumpListItems()

        'Creates a new task
        Dim myJumpTask As New JumpTask

        'Assigns the instance of the Jump List to the current application
        JumpList.SetJumpList(Application.Current, myJumpList)

        'Sets some properties
        With myJumpTask
            .Title =                                        System.IO.Path.GetFileNameWithoutExtension(Me.currentFileName)
            .Description = "A sample document containing custom information"
            .ApplicationPath = "Notepad.exe"
            .Arguments = Me.currentFileName
        End With

        'Adds the new task to the current Jump List
        myJumpList.JumpItems.Add(myJumpTask)

        'Applies edits
        myJumpList.Apply()

End Sub

Listing 3

The createJumpListItems method performs the following operations:

  1. Creates an instance of the JumpTask class, supposing we want to add complete information about the new item being added to the Jump List
  2. Associates the current application to the JumpList class instance. This is accomplished by invoking the shared SetJumpList method from the JumpList class
  3. Assigns some properties of the new task. Particularly the Title property contains the text that will be shown in the Jump List for the current item (in this case the file name without extension retrieved via the System.IO.Path.GetFileNameWithoutExtension method), the Description property contains text that will be shown if you pass the mouse pointer over the task, the ApplicationPath property requires the name of the application that will be launched when the user clicks the current item in the Jump List and the Arguments property stores the command line arguments for the application. In this particular case the code associates the Windows Notepad application to the current task but you can associate your items with other applications
  4. Adds the newly created task to the items collection of the Jump List (JumpList.JumpItems property)
  5. Finally, it sends changes to the Jump List invoking the Apply method.

It is worth mentioning that no category has been specified for the new task so it will be added to the default Tasks category. If you run the application and open a text file by right-clicking the application’s icon in the task bar, you will get a result similar to the one shown in Figure 3, where LoremIpsum is the name of a sample document available on my machine.

Figure 3 – The Jump List shows the current document

If you hover with the mouse over the item you will see a tooltip showing the description you supplied before. If you instead click such an item, the associated file will be opened in Windows Notepad. Notice that no category has been specified in code for the new task, so by default it has been added to the built-in Tasks category. In case you want to provide your own custom category, you can set the CustomCategory instance property of the JumpTask class. For example, add the following assignment to the code shown in Listing 3:

With myJumpTask
            .CustomCategory = "My documents"
End With

This will create a new custom category in the Jump List where you can place your items. Figure 4 shows the result of the above assignment.

Figure 4 – Creating custom categories in the Jump List

Notice that if you re-run the application you will still find the Jump List items available. This is because Windows keeps track of Jump List items in custom categories unless you remove them. You remove items by right-clicking the item you want remove and then clicking Remove from this list. Other interesting properties of the JumpList class are IconResourcePath and IconResourceIndex. They allow assigning an icon to a task, as demonstrated in the following example:

'Sets the resource file containing the icon
myJumpTask.IconResourcePath = "C:\Windows\System32\System32.dll"

'Specified the icon position
myJumpTask.IconResourceIndex = 10

The Jump List also offers two built-in categories, respectively named Recent and Frequent as I mentioned at the beginning of the article. In order to enable them, you set the JumpList.ShowRecentCategory and JumpList.ShowFrequentCategory properties as True. For example you might be interested in creating a list of most recently used files without creating a new custom category. The code in Listing 4 demonstrates how to accomplish this (see comments within code for explanations).

Private Sub createRecentListItems()

        'Enabling the Recent category
        myJumpList.ShowRecentCategory = True

        'Creating a new task
        Dim myJumpTask As New JumpTask

        'Associating the current application with the active instance

        'of the Jump List
        JumpList.SetJumpList(Application.Current, myJumpList)

        'Setting properties for the task
        With myJumpTask
            .Title = IO.Path.GetFileNameWithoutExtension(Me.currentFileName)
            .Description = "A sample document containing custom information"
            .ApplicationPath = "Notepad.exe"
            .Arguments = Me.currentFileName

        End With

        'Adding the task to the Recent category and saving changes
        JumpList.AddToRecentCategory(myJumpTask)
        myJumpList.Apply()

End Sub

Listing 4

It is worth mentioning that adding a new item to the Recent list is accomplished invoking the shared JumpList.AddToRecentCategory method that receives the instance of the new task as an argument. Also notice that assigning the CustomCategory property to an item being added to the recent category will produce no result. If you replace the call to the createJumpListItems with createRecentListItems in the Button.Click event handler, running the application and opening a text document will produce the result shown in Figure 5.

Figure 5 – Taking advantage of the built-in “Recent” category.

Enabling the Frequent category is a very simple task and requires just one line of code. With regard to our example, we can set the ShowFrequentCategory property in the main Window’s Loaded event as demonstrated in Listing 5.

Private Sub MainWindow_Loaded(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded
        Me.myJumpList.ShowFrequentCategory = True
End Sub

Listing 5

The good news is that you do not need to write any other code because Windows will take care of recognizing the most frequently used items and add them to the Jump List automatically for you. Figure 6 shows an example of the Frequent category.

 

Handling events

The JumpList class offers two events, JumpItemsRemovedByUser which is raised when the user removes one or more items from the Jump List and JumpItemsRejected which is raised when one or more items cannot be added to the Jump List for some reason. This is the reason why we marked as WithEvents the myJumpList instance declaration in Listing 2, so that we could set up a declarative event handler. The JumpItemsRemovedByUser event signature requires an argument of type JumpItemsRemovedEventArgs. Such a type stores information about the items removed, such as the RemovedItems property of type IList(Of JumpItem). Similarly the JumpItemsRejected event signature requires an argument of type JumpItemsRejectedEventArgs which stores information on rejected items and rejection reasons; this information can be investigated by taking advantage of the RejectedItems collection of type IList(Of JumpItem). Reasons for rejection are stored by the JumpItemRejectionReason enumeration and can be the following:

  1. None (the reason is not specified in details)
  2. InvalidItem (the JumpItem instance points to invalid pathnames or the operating system does not support Jump Lists)
  3. NoRegisteredHandler (the current application is not registered for handling the file extension for the new JumpItem)
  4. RemovedByUser (the JumpItem was previously available in the Jump List but actually it was removed by the user).

Listing 6 shows an example of handling the events.

Private Sub myJumpList_JumpItemsRejected(ByVal sender As Object,
                                             ByVal e As System.Windows.Shell.JumpItemsRejectedEventArgs) _
                                             Handles myJumpList.JumpItemsRejected
        For Each element In e.RejectedItems
            Debug.WriteLine(CType(element, JumpTask).Title)
        Next
End Sub

Private Sub myJumpList_JumpItemsRemovedByUser(ByVal sender As Object, ByVal e As  _                                                 System.Windows.Shell.JumpItemsRemovedEventArgs) _
                                                  Handles myJumpList.JumpItemsRemovedByUser
        For Each element In e.RemovedItems
            Debug.WriteLine(CType(element, JumpTask).Title)
        Next
        Debug.WriteLine(e.RemovedItems.Count)
End Sub

Listing 6

Notice how the code iterates the item collections in order to retrieve information on what happened. Each item in the collection is converted into an instance of the JumpTask class so that we can get complete information about the objects that were removed or rejected from the Jump List.

Interacting with the Jump List via XAML

You can also add items to the Jump List in a declarative fashion using XAML. According to the MSDN documentation, the appropriate place for declaring Jump List items is the Application.xaml file. The reason is that the Window definition cannot nest a Jump List definition; jump lists are associated with an entire application, not just a window. Now that you have knowledge of how the Jump List and items work, understanding how to implement this in XAML is an easy task. Listing 7 demonstrates how to add some items to the application’s Jump List in the Application.xaml file.

<Application x:Class="Application"
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
    StartupUri="MainWindow.xaml">

    <JumpList.JumpList>

        <!-- Declares an instance of the JumpList class
             setting some properties and event handlers -->

        <JumpList ShowRecentCategory="True" 
                  ShowFrequentCategory="True"
                  JumpItemsRejected="JumpList_JumpItemsRejected"
                  JumpItemsRemovedByUser="JumpList_JumpItemsRemovedByUser">

            <!-- Adding a path -->

            <JumpPath Path="C:\Users\Alessandro\My Documents\LoremIpsum.txt"/>

            <!-- Adding a Task to the default category -->

            <JumpTask ApplicationPath="C:\Windows\Notepad.exe"
                      Arguments="C:\Users\Alessandro\My Documents\LoremIpsum.txt"
                      Description="The most common sample document"
                      Title="Lorem Ipsum"/>

            <!-- Adding a Task to a cutom category -->

            <JumpTask ApplicationPath="C:\Windows\Notepad.exe"
                      CustomCategory="My documents"
                      Arguments="C:\Users\Alessandro\My Documents\LoremIpsum.txt"
                      Description="The most common sample document"
                      Title="Lorem Ipsum"/>
        </JumpList>
    </JumpList.JumpList>

</Application>

Listing 7

Defining Jump Items declaratively can be useful if you have an application that offers standard tasks, such as Microsoft Outlook, that you do not need to create at runtime and that you want to offer to your users each time they run your application. It is worth mentioning that adding a path requires the application to be registered for handling the file extension of the pathname, otherwise the item will not be added to the Jump List.

Conclusion

The Jump List in Windows 7 provides enhanced user experience offering easy access to common tasks, recent files and custom items related to your applications. WPF 4 and Visual Studio 2010 provide a very powerful environment for creating applications that are able to take advantage of the Jump List with both declarative and imperative code.

About the author

Alessandro Del Sole is a Microsoft Visual Basic MVP and Team Member in the Italian “Visual Basic Tips & Tricks” Community. He writes many Italian and English language community articles and books about .NET development and is the author of the book “Visual Basic 2010 Unleashed”. He also enjoys writing freeware and open-source developer tools. You can visit Alessandro’s Italian language blog or his English language blog.