Repository navigation
Perf: Lazily construct ShortcutControl's edit dialog - #50920
Morten Nielsen (dotMorten) wants to merge 1 commit into
Conversation
…UI slowdowns ShortcutControl (used once per module's activation-shortcut row, appearing on nearly every Settings page) unconditionally built a full ShortcutDialogContentControl + wrapping ContentDialog at construction time, even though that UI is only needed when the user clicks "Edit" to open the shortcut-editing dialog. This eagerly parsed ShortcutDialogContentControl.xbf and allocated a full dialog object graph for every shortcut row on every page load. This change defers construction of the dialog content control and the ContentDialog to a new EnsureDialogCreated() method, called only from OpenDialogButton_Click. Loaded/Unloaded event wiring for the dialog is now null-guarded so it still works correctly across virtualized Loaded/Unloaded cycles, with or without the dialog created yet. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Azure Pipelines: There may be pipelines that require an authorized user to comment /azp run to run. |
🧭 PR intakeVisual evidence: Not needed — The changed files do not indicate a visible UI change. No visual evidence is expected. Recommendation
✅ Ready for reviewThis PR passed the automated intake checks and is ready for maintainer review. Automated PR intake; PowerToys maintainers make final decisions. |
|
Thanks for the detailed profiling and performance evidence here. This implements the same core optimization as the earlier #50532: defer construction of We’re keeping #50532 open as the canonical implementation because it also covers the surrounding lifecycle behavior that becomes important once construction is deferred, including:
Morten's ETW investigation and before/after measurements are valuable evidence for the optimization, and we should carry that context into #50532. Closing this one as a duplicate implementation in favor of #50532. |
Summary
The Settings UI has noticeable slowdowns when navigating between pages. Profiling with a WinUI 3 ETW-based perf tool (capturing full navigation through all Settings pages) identified
ShortcutControl— a control instantiated once per module's activation-shortcut row, and therefore present on nearly every Settings page — as the single largest source of parsing/allocation cost and GC-pause spikes.Root cause
ShortcutControlunconditionally built its entire "Edit shortcut" dialog UI (aShortcutDialogContentControlwrapped in aContentDialog) at construction time via an eager field initializer and constructor-time allocation:That dialog content is only ever read from
OpenDialogButton_Clickand the dialog's own lifecycle callbacks (Opened/PrimaryButtonClick/Closing) — i.e. it's only needed if/when the user actually clicks "Edit" on that specific shortcut row. Every other page load paid the full cost of parsingShortcutDialogContentControl.xbfand allocating the dialog graph for nothing.Because
ShortcutControlappears multiple times on several pages (e.g. CropAndLock, FancyZones), this eager work stacked up as a "death by a thousand cuts" — each individual instance wasn't dramatic, but combined across a page (or across a full navigation session) it showed up as large blocking GC suspension pauses (up to 355ms) and was, by far, the top XAML-parsing cost in the app.Fix
ShortcutDialogContentControl candContentDialog shortcutDialoglazily constructed via a newEnsureDialogCreated()method, called only fromOpenDialogButton_Clickright before the dialog is shown.ContentDialogconstruction/event wiring.c.Keys = ...assignment in theHotkeySettingssetter (the dialog already refreshesc.Keysfresh every time it's opened).isLoadedtracking field and null-guarded the dialog-specific event wiring inShortcutControl_Loaded/ShortcutControl_Unloaded, since virtualized list containers can cycleLoaded/Unloadedmultiple times while the underlyingShortcutControl(and its lazily-created dialog) persists — this ensures the dialog's events still get wired correctly whether it's created before or after the control is loaded.No behavioral changes to the dialog itself — same fields, same buttons, same Save/Cancel/Reset/Clear/key-capture behavior. Verified via UI automation + screenshot that the "Edit shortcut" dialog still opens and functions identically after the change.
Performance: before vs. after
Measured with an identical 24-page Settings navigation script, captured via ETW trace analysis before and after the fix:
ShortcutDialogContentControl.xbfparsing (top cost, 20 instances)ShortcutControl.xbfparsing (unavoidable row control)Caveat: both captures hit the trace tool's evidence-size cap (not fully exhaustive), and raw wall-clock/idle-time numbers between the two runs aren't directly comparable due to shared-machine variance during capture. However, the parsing-resource breakdown (tied to number/cost of distinct XAML parses, not wall time) and peak GC-suspension durations are reliable, reproducible indicators — and both show the same result: the single biggest parsing cost in the entire Settings app is fully eliminated, and worst-case UI-thread stalls caused by GC are cut by roughly 70%.
Testing
PowerToys.Settings.csproj(Debug/x64) — 0 errors.