microsoft / microsoft/microsoft-ui-xaml

NavigationView pane and list states become inconsistent when maximizing with an overlay pane open

Open
#11,740 0 comments 0 reactions 0 assignees View on GitHub
area-NavigationView bug team-Controls
Dominant language
C++
Stars
8.4k
Forks
942
Avg merge
2d 7h
Merged PRs (30d)
105

Description

### Describe the bug

When a NavigationView is narrow enough to use Minimal display mode, opening its overlay pane and then maximizing the window can leave the pane, list-size visual state, and pane lifecycle events inconsistent.

Depending on the event ordering, I have observed the following outcomes:

1. The pane transitions normally.
2. The pane remains open after maximizing. Closing it with the toggle button leaves the navigation item labels visible beside the compact icons.
3. More rarely, maximizing immediately produces a compact-width pane whose item labels remain visible.

The issue does not require rapid clicking. Repeating the same resize sequence is sufficient to reproduce it.

### Why is this important?

NavigationView is commonly used as the main navigation surface of a desktop application. The bug leaves the control in a visibly invalid state and can also suppress a subsequent PaneClosing event.

I reproduced the behavior in WinUI Gallery and in a newly initialized, minimal WinUI 3 project. I have also observed the same visible behavior in the Windows Photos and Settings apps, although I cannot confirm that those applications reach exactly the same internal code path.

### Steps to reproduce the bug

1. Run the attached minimal reproduction project, or open WinUI Gallery.
2. Resize the window until NavigationView enters Minimal display mode.
3. Click the pane toggle button to open the overlay pane.
4. Maximize the window.
5. If the pane remains visually open, click the pane toggle button once to close it.
6. Observe that the pane can become compact while the navigation item label remain visible.
7. Repeat the sequence if necessary. The exact result is ordering-dependent, but no rapid interaction is required.

[demo.zip](https://github.com/user-attachments/files/31673454/demo.zip)

[log-annotated.log](https://github.com/user-attachments/files/31673457/log-annotated.log)

### Actual behavior

The logical pane state, SplitView visual state, and NavigationView list-size visual state can disagree.

In one ordering, the close completion is interpreted as an open completion, PaneClosed is not raised for the close, and a later PaneClosing event can be suppressed.

In another ordering, the pane ends in a compact closed state while NavigationView remains in ListSizeFull, leaving labels visible beside the compact icons.

### Expected behavior

After the adaptive layout change completes:

- an open pane must use the full list-size state;
- a closed pane in a compact SplitView mode must use ListSizeCompact;
- each committed close/open transition must produce the corresponding lifecycle completion without suppressing a later manual close.

### Screenshots

Demo:
https://github.com/user-attachments/assets/286c39f7-4a83-42a0-8aab-079c86385f46
https://github.com/user-attachments/assets/2d3c70d3-ef07-4a1a-ba1e-d9a939ede0ff

WinUI 3 Gallery:
https://github.com/user-attachments/assets/b0355129-67d7-4b7c-b21a-9e3b9cdbe548

Photos:

https://github.com/user-attachments/assets/efb58486-2487-4402-9c08-71f89fc2a264

### NuGet package version

Microsoft.WindowsAppSDK 2.4.0

### Windows version

Windows 11 (25H2): Build 26200, Windows Insider Build (xxxxx)

### Additional context

Similar historical symptoms were reported in #450 and #5474. I have not established that those reports have the same underlying cause.

Preliminary source analysis found two independent ordering problems:

1. `SplitView::OnDisplayModeStateChanged` handles an asynchronously delivered `CurrentStateChanged` event by reading the latest shared `m_isPaneOpeningOrClosing` and `IsPaneOpen` values. If a close is followed by an open before the queued close state notification is delivered, the close notification can be interpreted as an open completion. `PaneClosed` is then lost and `m_isPaneClosingByLightDismiss` can remain set. As a result, the next manual close can be mistaken for the completion of the earlier light-dismiss operation, suppressing its `PaneClosing` event.

2. `NavigationView::OnSplitViewPaneClosing` only enters `ListSizeCompact` when SplitView is already in a compact display mode. During the preparation phase of light-dismiss, `PaneClosing` can be delivered while the display mode is still `Overlay`, so we not change the list size. Adaptive layout can then change the mode to `CompactInline` while the pane remains open. During the deferred execution phase, the pane is finally closed, but `SplitView::OnPaneClosing` suppresses the second `PaneClosing` notification because the close is already marked as a light-dismiss operation. Consequently, neither phase changes the list from `ListSizeFull`, and no later callback reconciles it with the final compact closed state.

I have a proposed fix for both orderings and am willing to submit a PR.

Please assign the issue to me if appropriate.

Contributor guide

Open the contributing guide

Research direction

Start with SplitView::OnDisplayModeStateChanged, NavigationView::OnSplitViewPaneClosing, and SplitView::OnPaneClosing, then reproduce the sequence using the attached minimal project or WinUI Gallery. Trace the event ordering during maximize and overlay close/open transitions. Done means the final pane and list-size states agree, and each committed transition raises the expected lifecycle event without suppressing a later manual close.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
desktop
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.