Skip to content

Commit f42c8ba

Browse files
niels9001CopilotCopilot
authored
Adding jumplist support (#2125)
WinUI Gallery now integrates with the Windows taskbar jump list, giving you quick access to common utilities like Iconography and Colors pages and your favorites directly from the taskbar. <img width="340" height="549" alt="image" src="https://github.com/user-attachments/assets/0889df18-a342-4a38-b52d-c261a5939301" /> **Taskbar jump list integration** - Right-click the WinUI Gallery icon to see all your favorited and recent items appear in a dedicated "Favorites" and "Recent" group in the jump list - The jump list updates automatically when you add or remove favorites - Clicking any jump list item launches the app and navigates directly to that page **New sample page: Jump list (Shell→ Jump list)** - Interactive demos for adding tasks and custom groups to a jump list - Inline C# code samples showing the JumpList and JumpListItem APIs - Packaged-mode (MSIX) requirement noted via InfoBar Also moved app notifications, badge notifications and jumplist to a new category called "Shell": <img width="328" height="163" alt="image" src="https://github.com/user-attachments/assets/4a965a76-1237-4cc8-b932-78bdea2384a3" /> --------- Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com> Co-authored-by: niels9001 <9866362+niels9001@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent c884002 commit f42c8ba

File tree

11 files changed

+338
-62
lines changed

11 files changed

+338
-62
lines changed

WinUIGallery/App.xaml.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,24 @@ private async void EnsureWindow()
140140
targetPageType = typeof(ItemPage);
141141
}
142142
}
143+
// Handle JumpList launch arguments (passed as command-line args)
144+
else if (eventArgs != null &&
145+
eventArgs.Kind == ExtendedActivationKind.Launch &&
146+
eventArgs.Data is ILaunchActivatedEventArgs launchArgs &&
147+
!string.IsNullOrEmpty(launchArgs.Arguments))
148+
{
149+
string arg = launchArgs.Arguments.Trim();
150+
targetPageArguments = arg;
151+
152+
if (ControlInfoDataSource.Instance.Groups.Any(g => g.UniqueId == arg))
153+
{
154+
targetPageType = typeof(SectionPage);
155+
}
156+
else if (ControlInfoDataSource.Instance.Groups.Any(g => g.Items.Any(i => i.UniqueId == arg)))
157+
{
158+
targetPageType = typeof(ItemPage);
159+
}
160+
}
143161

144162
MainWindow.Navigate(targetPageType, targetPageArguments);
145163

@@ -149,6 +167,9 @@ private async void EnsureWindow()
149167
navItem.IsSelected = true;
150168
}
151169

170+
// Initialize the taskbar JumpList with fixed tasks and favorites.
171+
await JumpListHelper.UpdateJumpListAsync();
172+
152173
// Activate the startup window.
153174
MainWindow.Activate();
154175
}
5.08 KB
Loading

WinUIGallery/Controls/PageHeader.xaml

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
88
xmlns:converters="using:CommunityToolkit.WinUI.Converters"
99
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
10-
xmlns:models="using:WinUIGallery.Models"
1110
xmlns:local="using:WinUIGallery.Controls"
1211
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
12+
xmlns:models="using:WinUIGallery.Models"
1313
Loaded="UserControl_Loaded"
1414
mc:Ignorable="d">
1515

@@ -34,11 +34,11 @@
3434
<StackPanel Orientation="Horizontal" Spacing="4">
3535
<TextBlock
3636
AutomationProperties.AutomationId="PageHeader"
37+
AutomationProperties.HeadingLevel="Level1"
3738
Style="{StaticResource TitleTextBlockStyle}"
3839
Text="{x:Bind Item.Title}"
3940
TextTrimming="CharacterEllipsis"
40-
TextWrapping="NoWrap"
41-
AutomationProperties.HeadingLevel="Level1" />
41+
TextWrapping="NoWrap" />
4242
<Button
4343
x:Name="APIDetailsBtn"
4444
Margin="0,0,0,3"
@@ -153,10 +153,10 @@
153153
Text="Control source code" />
154154
<Button
155155
Padding="6,5,6,6"
156-
Style="{ThemeResource SubtleButtonStyle}"
157-
ToolTipService.ToolTip="{x:Bind GetControlSourceInfoText(), Mode=OneWay}"
156+
AutomationProperties.HelpText="{x:Bind GetControlSourceInfoText(), Mode=OneWay}"
158157
AutomationProperties.Name="Info"
159-
AutomationProperties.HelpText="{x:Bind GetControlSourceInfoText(), Mode=OneWay}">
158+
Style="{ThemeResource SubtleButtonStyle}"
159+
ToolTipService.ToolTip="{x:Bind GetControlSourceInfoText(), Mode=OneWay}">
160160
<FontIcon
161161
VerticalAlignment="Center"
162162
FontSize="14"
@@ -174,7 +174,7 @@
174174
</HyperlinkButton>
175175
</StackPanel>
176176

177-
<MenuFlyoutSeparator Margin="-12" />
177+
<MenuFlyoutSeparator x:Name="ControlSourceSeparator" Margin="-12" />
178178

179179
<StackPanel
180180
Margin="0,8,0,0"
@@ -187,10 +187,10 @@
187187
Text="Sample page source code" />
188188
<Button
189189
Padding="6,5,6,6"
190-
Style="{ThemeResource SubtleButtonStyle}"
191-
ToolTipService.ToolTip="{x:Bind GetSamplePageSourceInfoText(), Mode=OneWay}"
190+
AutomationProperties.HelpText="{x:Bind GetSamplePageSourceInfoText(), Mode=OneWay}"
192191
AutomationProperties.Name="Info"
193-
AutomationProperties.HelpText="{x:Bind GetSamplePageSourceInfoText(), Mode=OneWay}">
192+
Style="{ThemeResource SubtleButtonStyle}"
193+
ToolTipService.ToolTip="{x:Bind GetSamplePageSourceInfoText(), Mode=OneWay}">
194194
<FontIcon
195195
VerticalAlignment="Center"
196196
FontSize="14"
@@ -272,14 +272,14 @@
272272
</Button.Content>
273273
</Button>
274274
<AppBarSeparator />
275-
<ToggleButton x:Name="FavoriteButton"
276-
Height="32"
277-
Margin="4,0,0,0"
278-
Click="FavoriteButton_Click"
279-
AutomationProperties.Name="Favorite sample">
275+
<ToggleButton
276+
x:Name="FavoriteButton"
277+
Height="32"
278+
Margin="4,0,0,0"
279+
AutomationProperties.Name="Favorite sample"
280+
Click="FavoriteButton_Click">
280281
<ToggleButton.Content>
281-
<FontIcon FontSize="16"
282-
Glyph="{x:Bind GetFavoriteGlyph(FavoriteButton.IsChecked), Mode=OneWay}" />
282+
<FontIcon FontSize="16" Glyph="{x:Bind GetFavoriteGlyph(FavoriteButton.IsChecked), Mode=OneWay}" />
283283
</ToggleButton.Content>
284284
<ToolTipService.ToolTip>
285285
<ToolTip Content="{x:Bind GetFavoriteToolTip(FavoriteButton.IsChecked), Mode=OneWay}" />

WinUIGallery/Controls/PageHeader.xaml.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,13 @@ public void SetControlSourceLink(string BaseUri, string SourceLink)
5353
if (!string.IsNullOrEmpty(SourceLink))
5454
{
5555
ControlSourcePanel.Visibility = Visibility.Visible;
56+
ControlSourceSeparator.Visibility = Visibility.Visible;
5657
ControlSourceLink.NavigateUri = new Uri(BaseUri + SourceLink);
5758
}
5859
else
5960
{
6061
ControlSourcePanel.Visibility = Visibility.Collapsed;
62+
ControlSourceSeparator.Visibility = Visibility.Collapsed;
6163
}
6264

6365
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
using System;
5+
using System.Linq;
6+
using System.Threading.Tasks;
7+
using Windows.UI.StartScreen;
8+
using WinUIGallery.Models;
9+
10+
namespace WinUIGallery.Helpers;
11+
12+
/// <summary>
13+
/// Manages the app's taskbar JumpList, providing quick access to
14+
/// recently visited items and user-favorited items.
15+
/// </summary>
16+
internal static class JumpListHelper
17+
{
18+
/// <summary>
19+
/// Rebuilds the JumpList with recently visited items and the current set of favorite items.
20+
/// Safe to call at any time; silently returns if JumpList is not supported.
21+
/// </summary>
22+
public static async Task UpdateJumpListAsync()
23+
{
24+
if (!NativeMethods.IsAppPackaged || !JumpList.IsSupported())
25+
{
26+
return;
27+
}
28+
29+
try
30+
{
31+
JumpList jumpList = await JumpList.LoadCurrentAsync();
32+
jumpList.Items.Clear();
33+
jumpList.SystemGroupKind = JumpListSystemGroupKind.None;
34+
35+
// Dynamic group: Recent
36+
var recentlyVisited = SettingsHelper.Current.RecentlyVisited;
37+
foreach (string uniqueId in recentlyVisited)
38+
{
39+
AddItemTask(jumpList, uniqueId, groupName: "Recent");
40+
}
41+
42+
// Dynamic group: Favorites
43+
var favorites = SettingsHelper.Current.Favorites;
44+
foreach (string uniqueId in favorites)
45+
{
46+
AddItemTask(jumpList, uniqueId, groupName: "Favorites");
47+
}
48+
49+
await jumpList.SaveAsync();
50+
}
51+
catch
52+
{
53+
// JumpList updates are best-effort; don't crash the app.
54+
}
55+
}
56+
57+
private static void AddItemTask(JumpList jumpList, string uniqueId, string groupName)
58+
{
59+
ControlInfoDataItem? item = ControlInfoDataSource.Instance.Groups
60+
.SelectMany(g => g.Items)
61+
.FirstOrDefault(i => i.UniqueId == uniqueId);
62+
63+
if (item == null)
64+
{
65+
return;
66+
}
67+
68+
JumpListItem task = JumpListItem.CreateWithArguments(uniqueId, item.Title);
69+
task.GroupName = groupName;
70+
task.Description = "Go to " + item.Title;
71+
task.Logo = new Uri(item.ImagePath);
72+
jumpList.Items.Add(task);
73+
}
74+
}

WinUIGallery/Helpers/SettingsHelper/SettingsHelper.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,13 @@ public void UpdateFavorites(Action<List<string>> updater)
5656
var list = Favorites;
5757
updater(list);
5858
Favorites = list;
59+
_ = JumpListHelper.UpdateJumpListAsync();
5960
}
6061
public void UpdateRecentlyVisited(Action<List<string>> updater)
6162
{
6263
var list = RecentlyVisited;
6364
updater(list);
6465
RecentlyVisited = list;
66+
_ = JumpListHelper.UpdateJumpListAsync();
6567
}
6668
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<Page
3+
x:Class="WinUIGallery.ControlPages.JumpListPage"
4+
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
5+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
6+
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
7+
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
8+
xmlns:controls="using:WinUIGallery.Controls"
9+
mc:Ignorable="d">
10+
11+
<StackPanel>
12+
<InfoBar Margin="0,10,0,0" IsOpen="True" IsClosable="False" Severity="Warning"
13+
Title="JumpList is not available in unpackaged mode."
14+
Message="This API requires the app to be running in packaged mode (MSIX)."/>
15+
16+
<TextBlock Margin="0,12,0,0" TextWrapping="Wrap" Style="{StaticResource BodyTextBlockStyle}"
17+
Text="WinUI Gallery populates its jump list with your recently visited and favorited items. Restarting the app will restore these entries after any changes made on this page." />
18+
19+
<controls:ControlExample HeaderText="Adding tasks to the jump list">
20+
<StackPanel Spacing="8">
21+
<TextBlock TextWrapping="Wrap"
22+
Text="Tasks are items with an empty GroupName. They appear in the built-in 'Tasks' section at the bottom of the jump list. Use tasks for common app-wide actions that are always relevant, such as composing a new message or opening settings. Each task launches the app with a specific argument string that your app can handle on startup." />
23+
<StackPanel Orientation="Horizontal" Spacing="8">
24+
<Button Content="Add sample tasks"
25+
Style="{StaticResource AccentButtonStyle}"
26+
Click="AddTasksButton_Click" />
27+
<Button Content="Clear all items"
28+
Click="ClearTasksButton_Click" />
29+
</StackPanel>
30+
</StackPanel>
31+
<controls:ControlExample.CSharp>
32+
<x:String xml:space="preserve">
33+
var jumpList = await JumpList.LoadCurrentAsync();
34+
35+
var task = JumpListItem.CreateWithArguments("/compose", "New Message");
36+
task.Description = "Compose a new message";
37+
task.Logo = new Uri("ms-appx:///Assets/Tiles/AppList.targetsize-48.png");
38+
39+
jumpList.Items.Add(task);
40+
await jumpList.SaveAsync();
41+
</x:String>
42+
</controls:ControlExample.CSharp>
43+
</controls:ControlExample>
44+
45+
<controls:ControlExample HeaderText="Adding items to a custom group">
46+
<StackPanel Spacing="8">
47+
<TextBlock TextWrapping="Wrap"
48+
Text="Custom groups let you organize jump list items into named sections. Set the GroupName property to a non-empty string and all items sharing the same GroupName will appear together under that heading. This is useful for grouping related items like recent projects, pinned documents, or user-defined categories." />
49+
<Button Content="Add custom group items"
50+
Click="AddCustomGroupButton_Click" />
51+
</StackPanel>
52+
<controls:ControlExample.CSharp>
53+
<x:String xml:space="preserve">
54+
var jumpList = await JumpList.LoadCurrentAsync();
55+
56+
var item = JumpListItem.CreateWithArguments("/project-alpha", "Project Alpha");
57+
item.GroupName = "Projects";
58+
item.Description = "Open Project Alpha";
59+
item.Logo = new Uri("ms-appx:///Assets/Tiles/AppList.targetsize-48.png");
60+
61+
jumpList.Items.Add(item);
62+
await jumpList.SaveAsync();
63+
</x:String>
64+
</controls:ControlExample.CSharp>
65+
</controls:ControlExample>
66+
</StackPanel>
67+
</Page>
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
using Microsoft.UI.Xaml;
5+
using Microsoft.UI.Xaml.Controls;
6+
using System;
7+
using Windows.UI.StartScreen;
8+
using WinUIGallery.Helpers;
9+
10+
namespace WinUIGallery.ControlPages;
11+
12+
public sealed partial class JumpListPage : Page
13+
{
14+
public JumpListPage()
15+
{
16+
this.InitializeComponent();
17+
}
18+
19+
private async void AddTasksButton_Click(object sender, RoutedEventArgs e)
20+
{
21+
if (!NativeMethods.IsAppPackaged)
22+
{
23+
return;
24+
}
25+
26+
JumpList jumpList = await JumpList.LoadCurrentAsync();
27+
28+
JumpListItem composeTask = JumpListItem.CreateWithArguments("/compose", "New Message");
29+
composeTask.Description = "Compose a new message";
30+
composeTask.Logo = new Uri("ms-appx:///Assets/Tiles/AppList.targetsize-48.png");
31+
32+
JumpListItem searchTask = JumpListItem.CreateWithArguments("/search", "Search");
33+
searchTask.Description = "Search for items";
34+
searchTask.Logo = new Uri("ms-appx:///Assets/Tiles/AppList.targetsize-48.png");
35+
36+
jumpList.Items.Add(composeTask);
37+
jumpList.Items.Add(searchTask);
38+
39+
await jumpList.SaveAsync();
40+
}
41+
42+
private async void ClearTasksButton_Click(object sender, RoutedEventArgs e)
43+
{
44+
if (!NativeMethods.IsAppPackaged)
45+
{
46+
return;
47+
}
48+
49+
JumpList jumpList = await JumpList.LoadCurrentAsync();
50+
jumpList.Items.Clear();
51+
await jumpList.SaveAsync();
52+
}
53+
54+
private async void AddCustomGroupButton_Click(object sender, RoutedEventArgs e)
55+
{
56+
if (!NativeMethods.IsAppPackaged)
57+
{
58+
return;
59+
}
60+
61+
JumpList jumpList = await JumpList.LoadCurrentAsync();
62+
63+
JumpListItem item1 = JumpListItem.CreateWithArguments("/project-alpha", "Project Alpha");
64+
item1.GroupName = "Projects";
65+
item1.Description = "Open Project Alpha";
66+
item1.Logo = new Uri("ms-appx:///Assets/Tiles/AppList.targetsize-48.png");
67+
68+
JumpListItem item2 = JumpListItem.CreateWithArguments("/project-beta", "Project Beta");
69+
item2.GroupName = "Projects";
70+
item2.Description = "Open Project Beta";
71+
item2.Logo = new Uri("ms-appx:///Assets/Tiles/AppList.targetsize-48.png");
72+
73+
jumpList.Items.Add(item1);
74+
jumpList.Items.Add(item2);
75+
76+
await jumpList.SaveAsync();
77+
}
78+
}

0 commit comments

Comments
 (0)