microsoft / microsoft/PowerToys

[CmdPal] Context-item `RequestedShortcut` navigates twice, requiring two Esc / two Back presses to leave the page

Open
#48,725 0 comments 0 reactions 0 assignees View on GitHub
Issue-Bug Needs-Triage Product-Command Palette
Dominant language
C
Stars
139k
Forks
8.6k
PR merge metrics
PR metrics pending

Description

### Microsoft PowerToys version

0.100.0

### Installation method

GitHub

### Area(s) with issue?

Command Palette

### Steps to reproduce

This affects extension authors using `CommandContextItem.RequestedShortcut` on a top-level command's `MoreCommands`, where the bound command is a page (navigation).

1. Create an extension from the Command Palette extension template.
2. Replace `TemplateCmdPalExtensionCommandsProvider.cs` with the minimal repro below.
3. Run the extension and open Command Palette.
4. Select the "ShortcutRepro" item so it is highlighted.
5. Press **Ctrl+1** (the shortcut bound to "MoreCommand2") to navigate into `ReproListPage`.
6. Press **Esc** once.

Optional, to make the double-navigation visible directly: after step 5, click the back button (the `<` arrow) at the top-left instead of pressing Esc.

### Minimal repro (`TemplateCmdPalExtensionCommandsProvider.cs`)

```csharp
using Microsoft.CommandPalette.Extensions;
using Microsoft.CommandPalette.Extensions.Toolkit;
using Windows.System;

namespace TemplateCmdPalExtension;

public partial class TemplateCmdPalExtensionCommandsProvider : CommandProvider
{
private readonly ICommandItem[] _commands;

public TemplateCmdPalExtensionCommandsProvider()
{
DisplayName = "ReproExtension";

var page = new ReproListPage();

_commands = [
new CommandItem(page)
{
Title = "ShortcutRepro",
MoreCommands = [
new CommandContextItem(new ReproListPage())
{
Title = "MoreCommand1 (no shortcut)",
},
new CommandContextItem(new ReproListPage())
{
Title = "MoreCommand2 (Ctrl+1)",
RequestedShortcut = KeyChordHelpers.FromModifiers(
ctrl: true, vkey: VirtualKey.Number1),
},
],
},
];
}

public override ICommandItem[] TopLevelCommands() => _commands;
}

// Reproduces with both ListPage and DynamicListPage.
internal sealed partial class ReproListPage : ListPage
{
public ReproListPage()
{
Title = "ReproListPage";
Name = "Open";
}

public override IListItem[] GetItems() => [];
}
```

### ✔️ Expected Behavior

After navigating into the page via the context-item shortcut (Ctrl+1), pressing Esc once (with "Escape key behavior" set to "Always go back", and also with the default) returns to the previous page — the same as when navigating with Enter or Ctrl+Enter. A single back-button click also returns to the previous page.

### ❌ Actual Behavior

After navigating via the context-item shortcut, **two** Esc presses (or two back-button clicks) are required to leave the page. On the first press, nothing appears to happen except that the progress bar starts animating (this happens with a plain `ListPage` as well as a `DynamicListPage`). The second press returns to the top page.

Navigating into the same page with **Enter** or **Ctrl+Enter** behaves correctly: a single Esc / single back-button click returns.

The single back-button click reproducing the same problem indicates the navigation stack contains two entries, i.e. the page is navigated to twice.

### Upload Bug Report ZIP-file

_No response_

### Additional Information

OS version: Windows 11 x64
.NET version: .NET 10
System Language: Japanese
User or System Installation: Unknown
Running as admin: No

## Root cause (from source reading, v0.100.0)

The shortcut path sends a `TryCommandKeybindingMessage` from `ShellPage_OnPreviewKeyDown`. This message has **two** registered recipients:

- `CommandBar` (`Microsoft.CmdPal.UI/Controls/CommandBar.xaml.cs`)
- `ContextMenu` (`Microsoft.CmdPal.UI/Controls/ContextMenu.xaml.cs`)

In `CommandBar.xaml`, the `ContextMenu` control (`ContextControl`) uses the default `SubscribeToCommandBar = true`, so both controls subscribe to `TryCommandKeybindingMessage`.

`ContextMenuViewModel` keeps the selected item's `MoreCommands` in `ContextMenuStack` even while the context menu is closed: its `SelectedItem` setter calls `UpdateContextItems()`, which is driven by `UpdateCommandBarMessage` on every selection change.

As a result, pressing the context-item shortcut matches the keybinding in **both** recipients:

- `CommandBar.Receive(TryCommandKeybindingMessage)` → `CommandBarViewModel.CheckKeybinding` → `PerformCommand` → sends `PerformCommandMessage` (navigation 1).
- `ContextMenu.Receive(TryCommandKeybindingMessage)` → `ContextMenuViewModel.CheckKeybinding` → `InvokeCommand` → sends `PerformCommandMessage` (navigation 2).

Both `PerformCommandMessage`s navigate to the bound page, pushing two entries onto `RootFrame`'s back stack. `NavigateBackMessage` (used by both Esc and the back button) pops one entry per press, so two presses are needed.

Enter / Ctrl+Enter use different messages (`ActivateSelectedListItemMessage` / `ActivateSecondaryCommandMessage`, handled only by `ListItemsView`), so they navigate once and behave correctly.

Note: the official `SamplePagesExtension` contains list items annotated "Try pressing Ctrl+1 with me selected", so this is an expected, documented usage pattern rather than an edge case.

## Possible fix direction

Avoid handling `TryCommandKeybindingMessage` in both recipients. For example, `ContextMenu` could skip keybinding handling while its flyout is closed (or mark `msg.Handled` so only one recipient acts), leaving `CommandBar` as the single handler when no context menu is open.

Note that the same `SubscribeToCommandBar` mechanism already exists and is used to solve essentially the same double-handling problem for the Dock: PR #46420 ("CmdPal: Fix Dock context menu following active item in Command Bar") added the `SubscribeToCommandBar` dependency property and set `SubscribeToCommandBar="False"` on the Dock's `ContextMenu` so it no longer reacts to CommandBar selection/keybinding messages. The main palette's `ContextControl` in `CommandBar.xaml` still uses the default `SubscribeToCommandBar="True"`, so it is not covered by that fix and the double-navigation described here remains. A fix for the main palette could follow the same pattern (e.g. only let the `ContextMenu` handle the keybinding while its flyout is actually open).

### Other Software

_No response_

Contributor guide

Open the contributing guide

Research direction

Start with Microsoft.CmdPal.UI/Controls/CommandBar.xaml.cs and ContextMenu.xaml.cs, then inspect the ContextControl subscription in CommandBar.xaml and the existing SubscribeToCommandBar handling from PR #46420. Reproduce the Ctrl+1 case with the provided extension and ensure the keybinding produces one navigation, so one Esc or back-button click returns to the previous page.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp
Domain
desktop
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
74/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.