mirror of
https://github.com/microsoft/microsoft-ui-xaml.git
synced 2026-10-28 08:25:50 +08:00
main
3145
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
b9fa30e549 |
WinUI Profiler: record the kernel's GPU memory events (#12424)
## Description The WinUI Profiler (`tools/WinUIProfiler`) now also records the kernel's GPU memory events for the target process, live and from `.etl` files. Windows writes them in any WinUI build. ### Current Behavior The profiler reads the target's `Microsoft-Windows-XAML` and `Microsoft-Windows-XAML-Profiler` events. Its only GPU memory data was the profiler provider's `GpuMemorySnapshot` event, stored like any other event, and only from WinUI builds with `XamlProfilerEnabled`. #12416 removed that event. The native GPU sampler reads GPU engine utilization, not memory. ### New Behavior - **Events:** `Microsoft-Windows-DxgKrnl` keyword `0x4`: `VidMmProcessUsageChange` (367) and `VidMmProcessBudgetChange` (366), the process's usage and budget per adapter and memory segment group. They carry the values `IDXGIAdapter3::QueryVideoMemoryInfo` returns. The kernel writes them when the usage or budget changes, and writes the current values when the process opens a GPU adapter and when a session requests capture state. - **Matched by the event's `ProcessId` field**, not the event header: the System process writes some of a process's rows, such as those at exit that bring its usage down to 0. That field becomes the event's process ID; the thread ID stays the writing thread. - **Live:** each connection's session also enables DxgKrnl keyword `0x4`, with no process filter, and requests capture state when reading starts, so an attach gets the current usage and budget at once. Both are best effort: a failure is traced as a warning, and XAML events are read as before. - **`.etl` files:** the target's rows are read along with its XAML events. - **Session files** store them with provider `DxgKrnl`. The schema version is unchanged. - **App:** the connection panel's status line adds a "GPU memory" count, and its tooltip a line for it. - **CLI:** `processes` has a DxgKrnl column. For traces it still lists only processes with XAML or XAML-Profiler events, since every process that uses the GPU has GPU memory events; a session file always lists its process. The usage text and messages say "events" instead of "XAML events". - **README:** the provider and its events, how rows are matched, the live behavior, and a `logman` line for the provider. ## Impact and risk Developer tooling only, under `tools/WinUIProfiler`; no product code changes. Low risk. Session files that contain GPU memory rows can't be read by earlier builds of the tool, which don't know the `DxgKrnl` provider; other session files are unchanged. ## How Has This Been Tested? `tools/WinUIProfiler` has no test project yet. I validated with traces of a 1,000-item ListView test app, from a VM without a GPU (WARP) and from a Snapdragon X Elite laptop (integrated GPU), and live on the VM. | Test | Result | | --- | --- | | `dotnet build .\WinUIProfiler.Cli\WinUIProfiler.Cli.csproj -c Release` | Passed, 0 warnings | | `.\scripts\Build-App.ps1 -Configuration Release -Platform x64` | Passed | | CLI, VM trace | The app's 2 rows: usage 0 and budget 50,680,655,872 bytes, the values of the `GpuMemorySnapshot` events in the same trace | | CLI, laptop trace with the app running | 130 usage rows and 1 budget row; usage from 0 to about 22 MB | | CLI, laptop trace through app exit | 255 usage rows and 1 budget row; the last rows bring usage to 0 | | Which process wrote the laptop rows | The System process wrote 2 of the running app's rows and 127 of its exit rows; the app's own threads wrote the rest. Matching by `ProcessId` keeps all of them | | Live attach to a running app (Core live source, elevated) | The capture-state usage and budget rows arrive; 0 lost | | Live launch (same) | 6,230 XAML events and 2 GPU memory rows; 0 lost | | `logman` with the README's provider lines | Records the app's GPU memory rows | | `record`, then `summary` and `dump` of the `.sqlite` | DxgKrnl rows round-trip; a file with only GPU memory rows lists its process | | Status line strings | `EtwFormat` and `EtwDetailsFormat` format with the new arguments. The App UI itself wasn't driven | Not tested: discrete GPUs (non-local memory), machines with two GPUs, Windows 10. |
||
|
|
03aabf5360 |
Capture an initial WinUI Profiler snapshot automatically after connecting (#12425)
## Fixes <!-- Link either the public GitHub issue or the internal issue this PR addresses. Remove the unused line. --> <!-- PRs without a linked issue may be closed unless they are minor documentation/typo fixes. --> No linked issue was supplied. Implements the requested automatic initial tree snapshot for WinUI Profiler. ## PR Type <!-- Check the type of change. Limit each PR to one type when possible. --> - [ ] Bugfix - [x] Feature - [ ] Code style update (formatting, renaming) - [ ] Refactoring (no functional changes, no API changes) - [ ] Build related changes - [ ] Documentation content changes - [ ] Other (please describe): ## Description <!-- Describe your changes in detail. --> ### Current Behavior <!-- How does this work today? --> Connecting to a target does not capture a baseline. Users must take the first tree snapshot manually, potentially missing the state immediately after connection. ### New Behavior <!-- How will it work after this PR? --> - Automatically capture and persist an **Initial snapshot** when launch/attach completes successfully, after the authenticated TAP hello and the existing connection prerequisites. - Reuse the manual capture path and prevent manual snapshots or another connection from overtaking the initial capture. - Keep the initial snapshot in the existing newest-first history alongside later manual snapshots. Each reconnect captures its own baseline without clearing earlier history; periodic hello heartbeats do not trigger additional snapshots. - Preserve cancellation and reject results from a cancelled, closed, or replaced connection before publishing them to history. Report capture errors through the existing error UI. - Add the initial-snapshot title resource and document the behavior. No native capture, snapshot schema, or tree-display changes. ## Customer Impact <!-- How does this change affect end users? Is it user-facing or infra changes? --> Profiler users get a saved connection baseline without an extra click. Manual capture becomes available after the initial attempt finishes. History remains available for the current profiler session, and saved JSON files retain their existing on-disk lifetime. ## Regression Potential <!-- Could this change cause regressions? What areas might be affected? --> - [x] Low risk — isolated change, limited scope - [ ] Medium risk — touches shared components or public APIs - [ ] High risk — architectural or breaking API change Limited to the standalone profiler's connection/capture UI and resources. The additional capture runs once per successful connection and uses the existing capture timeout, persistence, error reporting, and cancellation path. ## How Has This Been Tested? <!-- Describe how you validated your changes. --> - [x] I have performed a self-review of my own code - [ ] I have added tests to cover my changes - [ ] Existing tests pass locally **Release x64 build: passed.** Ran from the repository root with the .NET SDK resolver set to `C:\Program Files\dotnet` and an isolated `BaseOutputPath`, preserving the already-running profiler: ```powershell .\initrun.ps1 -Flavor amd64fre .\tools\WinUIProfiler\scripts\Build-App.ps1 -Configuration Release -Platform x64 ``` **External UI/runtime validation: passed, 25 checks and zero failures.** Ran this temporary Windows UI Automation harness against the built app: ```powershell & "$env:SystemRoot\System32\WindowsPowerShell\v1.0\powershell.exe" -NoLogo -NoProfile -STA -File 'C:\Users\akanpatel\.copilot\session-state\18d84a18-e328-402b-b9ec-6a2269e43683\files\initial-snapshot-validation\Validate.ps1' -Profiler 'C:\Users\akanpatel\.copilot\session-state\18d84a18-e328-402b-b9ec-6a2269e43683\files\initial-snapshot-validation\app-build\x64\Release\net10.0-windows10.0.26100.0\win-x64\WinUIProfiler.App.exe' ``` Verified automatic capture after packaged launch and attach; persisted snapshots with real XAML/Composition nodes and correlations; the initial label and authenticated process identity; later manual capture retaining the baseline; no duplicate capture on heartbeat; history retention on disconnect; one new baseline on reconnect; no capture for cancelled/failed connections; recovery after cancellation/failure; and no XAML binding or unhandled runtime errors. The temporary harness was removed after validation. No test cases or test project are included in this PR; no repository automated test suite was run. **Whitespace validation: passed.** ```powershell git --no-pager diff --check ``` ## Screenshots (if appropriate) <!-- If you are making visual changes, include before/after screenshots. --> Not applicable; no layout or styling changes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 18d84a18-e328-402b-b9ec-6a2269e43683 |
||
|
|
d16403c09a |
WinUI Profiler: say GpuMemorySnapshot is from earlier builds
XAML no longer writes GpuMemorySnapshot, so the README describes it as something earlier builds reported. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: e91a0e91-385c-48fd-9b9c-daea01d24e65 |
||
|
|
496e182522 |
WinUI Profiler: record the kernel's GPU memory events
The Microsoft-Windows-XAML-Profiler GpuMemorySnapshot event measures the process's GPU memory with IDXGIAdapter3::QueryVideoMemoryInfo on the UI thread, under the D3D device lock, each time a composition surface is created, freed or resized. The kernel already writes the same counters: Microsoft-Windows-DxgKrnl VidMmProcessUsageChange (367) and VidMmProcessBudgetChange (366), keyword 0x4, on every change, plus the current values when a process opens a GPU adapter and on capture state. Record those events with the target's XAML events, so the profiler has the process's GPU memory usage and budget from the kernel, in any build, without depending on the snapshot event: - Live sessions enable DxgKrnl keyword 0x4 for every process, because the System process writes some of the target's events (such as those written when it exits), and request capture state when reading starts, so an attached process gets its current values. Best effort: if this fails, the session still reads the XAML events. - Live and .etl reading keep the 366/367 events whose ProcessId field is the target, as XamlProvider.DxgKrnl events. Session files store them like the XAML events; the schema is unchanged. - The connection panel counts GPU memory events. The CLI adds a DxgKrnl column, and for a trace lists only processes with XAML events. - README: the provider, what its events carry, the live behavior and the logman line. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: e91a0e91-385c-48fd-9b9c-daea01d24e65 |
||
|
|
1433a837e7 |
Remove GpuMemorySnapshot from the XAML profiler (#12416)
## Fixes <!-- TODO: link the tracking issue --> ## PR Type - [ ] Bugfix - [ ] Feature - [ ] Code style update (formatting, renaming) - [ ] Refactoring (no functional changes, no API changes) - [ ] Build related changes - [ ] Documentation content changes - [x] Other (please describe): removes a diagnostics event, and the GPU memory query that fed it, from the XAML profiler instrumentation. ## Description ### Current Behavior In builds with the XAML profiler compiled in (`XAMLPROFILER_ENABLED`, which `main` turns on for Debug builds only), while a trace session enables `Microsoft-Windows-XAML-Profiler`, `DCompSurface::TraceProfilerMemoryChange` writes `GpuMemorySnapshot` on every DComp surface allocation, free and resize. To fill the event, the UI thread: 1. takes the shared D3D device lock; 2. walks from the device to its DXGI adapter and `IDXGIAdapter3`; 3. calls `QueryVideoMemoryInfo` twice, for the local and non-local segment groups. The event is 34 bytes, but on a VM without a GPU the query takes about 21 µs, most of it in the kernel. A launch of a 1,000-item ListView test app has 52–76 surface changes, so there the query costs about 1.1–1.6 ms of UI-thread time per profiled launch. On a laptop GPU it cost 0.7 ms per launch (4 µs median per query, 132 µs at most). ### New Behavior XAML no longer queries GPU memory or writes `GpuMemorySnapshot`. The DirectX kernel already traces the same numbers. `Microsoft-Windows-DxgKrnl` writes `VidMmProcessUsageChange` (event 367) and `VidMmProcessBudgetChange` (event 366) for each process, adapter and memory segment group: - whenever the process's usage or budget changes; - as a rundown when the process first opens an adapter; - on capture state (keyword `0x4`). They carry the values `QueryVideoMemoryInfo` returns, so profiling tools read them instead. A trace needs DxgKrnl keyword `0x4` with capture state, in a session that accepts kernel-mode providers (in a WPR profile, `NonPagedMemory="true"`). The two events written at the same point are unchanged: `DCompSurfaceMemoryChanged` (the surface's id and size) and `XamlHeapSnapshot` (XAML's heap counters). ## Customer Impact None for apps: the removed code only ran in builds with the profiler compiled in, while a trace session enabled the provider. Profiling sessions lose a UI-thread stall on every surface change. Tools that read `GpuMemorySnapshot` need to read the DxgKrnl events instead. ## Regression Potential - [x] Low risk — isolated change, limited scope - [ ] Medium risk — touches shared components or public APIs - [ ] High risk — architectural or breaking API change Two files, and the change only deletes code (plus a comment saying where the numbers come from now). Everything removed is inside `#ifdef XAMLPROFILER_ENABLED` and only ran while a session enabled the provider. Nothing in this repo reads `GpuMemorySnapshot`. ## How Has This Been Tested? - **Build.** amd64chk, the configuration in which `main` compiles the profiler, builds clean. - **Same values.** On a VM without a GPU (WARP), XAML's own usage query returns 0. There, in traces of the ListView1000 test app that have both `GpuMemorySnapshot` and the DxgKrnl events, every snapshot equals the kernel's latest usage and budget for that process and adapter: 52 of 52 when the app is still running at the end of the trace, and 102 of 102 when the trace continues through app exit. A D3D12 test program that allocates and frees video memory checked non-zero usage the same way: usage matched in 19 of 19 samples and budget in 18 of 19 (the miss was a sample taken before the program's device existed). - **Real GPU.** On a Snapdragon X Elite laptop (integrated GPU, one adapter, Windows 11), with the x64 test app running emulated, every snapshot equals the kernel's usage and budget: 76 of 76 with the app still running at the end of the trace, and 130 of 130 through app exit, with usage between 2.5 and 21.3 MB. There the snapshots took 0.70 ms of UI-thread time per launch. Enabling DxgKrnl keyword `0x4` instead makes the app write 511 kernel events per launch, about 0.1 ms at about 0.2 µs each (estimated from back-to-back event timestamps). - **Cost.** Measured on a VM without a GPU (WARP), in an amd64fre build with the profiler compiled in, ListView1000, profiler session on, 12 interleaved rounds of before and after: | | Before | After | |---|---:|---:| | CPU per launch in `DCompSurface::TraceProfilerMemoryChange` | 1.23 ms | 0.11 ms | | Difference per round, mean (95% CI) | | −1.12 ms (−1.33 to −0.92); lower in 12 of 12 rounds | | `GpuMemorySnapshot` events per launch | 52.5 | 0 | | `DCompSurfaceMemoryChanged` events per launch | 52.5 | 52.8 | Where the query's time went (121 CPU samples): ntoskrnl 32%, dxgkrnl 17%, d3d11 17%, dxgmms2 13%, dxgi 5%, Microsoft.UI.Xaml 4%. **Still to do:** discrete GPUs (non-local memory) and machines with two GPUs still need a capture on real hardware. - [x] I have performed a self-review of my own code - [ ] I have added tests to cover my changes - [ ] Existing tests pass locally ## Screenshots (if appropriate) Not applicable. |
||
|
|
b4573c044e |
WinUI Profiler: save live XAML events to a SQLite session file (#12423)
## Description Third step of the WinUI Profiler's event pipeline (`tools/WinUIProfiler`): while connected, the app saves every XAML event it receives to a SQLite session file. Builds on #12419 (live events) and #12415 (reading `.etl` files). The object-lifetime and frame processors will read from these files. ### Current Behavior The app reads the target's XAML events live and only counts them. Nothing is kept after disconnect. ### New Behavior - **One file per connection** in `%LOCALAPPDATA%\WinUIProfiler\Sessions`, named `<date>-<time>-<process>-<pid>.sqlite`. The connection panel shows the file name, event count and size; the full path is in the tooltip. - **Tables:** - `SessionInfo`: one row with the process, coverage (from process start or from connection), time zero, QPC frequency, size limit, how the session ended, and events lost or dropped. - `EventTypes`: one row per event layout, with the field names and their .NET types (for example `Int64,Int32,String,Boolean`). - `Events`: `Seq` (arrival order 1, 2, 3...), `Qpc`, thread, `TypeId` (references `EventTypes`), and the field values as a JSON array. - **Writer:** a separate thread commits batches of up to 2,000 events or every 250 ms, so disk writes don't hold up event processing. It waits rather than drops, so the pipeline queue stays the only place events are dropped. - **Size limit:** 500 MB per file. Near the limit, the oldest 5% of events are removed and the file records from which event it is complete. - **Infinity and NaN** (which occur in real XAML events) are stored as strings and read back as numbers. - The file is readable while being written and after a crash. It is closed as a single file on disconnect, target exit or app close. - **CLI:** `processes`, `summary` and `dump` accept `.sqlite` files. The new `record <etl> --pid <pid> --out <file> [--limit-mb]` writes a session file from a trace. - The connection panel's status lines are shorter: one line each, with details in tooltips. - Adds `Microsoft.Data.Sqlite` 10.0.13. ## Impact and risk Developer tooling only, under `tools/WinUIProfiler`; no product code changes. Low risk. Session files are not deleted by the profiler (a retention policy is a follow-up). ## How Has This Been Tested? `tools/WinUIProfiler` has no test project yet. I validated against `.etl` traces and against `logman` recording the same providers in parallel, on a local Release build of `Microsoft.UI.Xaml.dll` with `XamlProfilerEnabled` (not part of this PR). | Test | Result | | --- | --- | | `dotnet build .\WinUIProfiler.Cli\WinUIProfiler.Cli.csproj -c Release` | Passed, 0 warnings | | `.\scripts\Build-App.ps1` | Passed | | `record` of a 73,860-event trace, then `dump` of the `.sqlite` vs `dump` of the `.etl` | All 73,860 events identical (incl. 1,582 Infinity/NaN values); only the time column differs, by a constant offset (time zero is the recording's first event) | | `record` with `--limit-mb` 1 to 5 | Files stay under the limit; the newest events are intact | | Live launch of a WinUI 3 app in the app, with interaction, `logman` in parallel | Per thread, the session file holds exactly logman's events in order; 69,696 events, 0 lost, 0 dropped; closed as "Disconnected" | | `Seq` and timestamps in the live files | `Seq` gapless; no timestamp went backwards | | CLI reading a file while the app is recording | Passed | | Profiler killed mid-recording | File readable; all committed batches present | | SQLite foreign-key check (`Events.TypeId` → `EventTypes`) | No violations | |
||
|
|
29200d1531 |
WinUI Profiler: save live XAML events to a SQLite session file
While connected, every event the pipeline receives is saved to a SQLite file, one per connection, in %LOCALAPPDATA%\WinUIProfiler\Sessions. A writer thread commits batches; field values are a JSON array per event, with field names and .NET types stored once per event type. Files stay within 500 MB by removing the oldest events. The CLI reads session files and records one from an .etl. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> |
||
|
|
ba09cff5b6 |
Compact WinUI Profiler snapshot tree display (#12421)
## Fixes <!-- Link either the public GitHub issue or the internal issue this PR addresses. Remove the unused line. --> <!-- PRs without a linked issue may be closed unless they are minor documentation/typo fixes. --> Follow-up to #12414; no linked issue. ## PR Type <!-- Check the type of change. Limit each PR to one type when possible. --> - [x] Bugfix - [ ] Feature - [ ] Code style update (formatting, renaming) - [ ] Refactoring (no functional changes, no API changes) - [ ] Build related changes - [ ] Documentation content changes - [ ] Other (please describe): ## Description <!-- Describe your changes in detail. --> ### Current Behavior <!-- How does this work today? --> The XAML and Composition snapshot trees add vertical padding inside the native TreeView rows, making the hierarchy unnecessarily spread out. Labels also lead with snapshot-local `#Id` counters, which can be mistaken for object identities. ### New Behavior <!-- How will it work after this PR? --> - Remove redundant vertical padding while retaining the native TreeView indentation, chevrons, selection, and correlation outline. - Show the short runtime type, optional element name, and actual captured address in parentheses, without a leading counter. - Show the captured XamlRoot address in each root heading. - Preserve internal IDs for topology and correlation, and expose the selected node's ID in Details. This changes only four snapshot-viewer display files. Native capture, saved snapshot schemas, and correlation behavior are unchanged. ## Customer Impact <!-- How does this change affect end users? Is it user-facing or infra changes? --> More snapshot nodes fit on screen, and labels distinguish actual object/visual addresses from snapshot bookkeeping IDs. Both trees use the same compact layout. ## Regression Potential <!-- Could this change cause regressions? What areas might be affected? --> - [x] Low risk — isolated change, limited scope - [ ] Medium risk — touches shared components or public APIs - [ ] High risk — architectural or breaking API change Scope is limited to row spacing, labels, root headings, and the selected-node Details display. Expansion, selection, highlighting, scrolling, and bidirectional mappings were exercised against a real snapshot. ## How Has This Been Tested? <!-- Describe how you validated your changes. --> - [x] I have performed a self-review of my own code - [ ] I have added tests to cover my changes - [ ] Existing tests pass locally Release x64 profiler build: **Passed** using: ```powershell $env:DOTNET_MSBUILD_SDK_RESOLVER_CLI_DIR = 'C:\Program Files\dotnet' .\initrun.ps1 -Flavor amd64fre .\tools\WinUIProfiler\scripts\Build-App.ps1 -Configuration Release -Platform x64 ``` Temporary, out-of-repository UI/runtime probe: **29 checks passed, 0 failures**. Captured a real FolderTreeAppCpp snapshot through TAP with 90 XAML nodes and 191 Composition nodes, then exercised the actual viewer: - Compact rows and native indentation in both trees; actual addresses, names, and XamlRoot headings. - Restoring the original padding reproduced oversized rows; removing it restored compact spacing. - Expand/collapse, mapping in both directions, correlation highlighting, scrolling to a match, and retained IDs in Details. - Light/dark themes and larger text without fixed-height clipping. `git diff --check`: **Passed**. No repository test files were added. The full repository test suite, High Contrast, and multiple monitor DPI settings were not tested. ## Screenshots (if appropriate) <!-- If you are making visual changes, include before/after screenshots. --> The actual viewer was visually verified locally. The screenshot includes local session paths and is not attached. | Display | Before | After | |---|---|---| | Row height on the validation machine | 47 DIP | 32 DIP | | Example node label | `#12 Grid (RootGrid) [0x...]` | `Grid (RootGrid) (0x...)` | | Root heading | `XamlRoot #1` | `XamlRoot (0x...)` | Addresses above are illustrative placeholders, not captured pointer values. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 18d84a18-e328-402b-b9ec-6a2269e43683 |
||
|
|
d87f0d9ce0 |
WinUI Profiler: live XAML ETW events, from process start for launches (#12419)
## Description Second step of the WinUI Profiler's event pipeline (`tools/WinUIProfiler`): the app now reads the target's XAML TraceLogging events live while connected. Builds on #12415, which reads them from `.etl` files. ### Current Behavior The app connects to a WinUI 3 app and shows CPU, GPU, memory and tree snapshots. XAML events can only be read from a recorded `.etl` file with the CLI. ### New Behavior - **Launch (unpackaged or packaged):** the ETW session starts before the process is created. The process has no ID yet, so ETW filters on the executable name: the selected `.exe`, or for a packaged app the `Executable` of the matching `<Application>` in the installed package's `AppxManifest.xml`. As soon as the launch returns the process ID, the same session switches to a process ID filter. ETW keeps the events in the session's buffers until then, so the launch's first event is included. The target is launched normally, not suspended, so its launch timing is not affected. - **Attach:** the session filters on the process ID and counts from connection. - Events go through a queue of 65,536 events to one processing thread that runs event processors (`IEventProcessor`). The first processor counts events. When the queue is full, new events are dropped and counted; the session keeps running. - The connection panel shows whether the counts start at process start or at connection, the events per provider, events lost by ETW and events dropped by the profiler. The last counts stay visible after disconnect. If ETW cannot start (permissions), the panel says why and the metrics keep working. A launch is shown as "from connection" when activation returns a packaged app that was already running. - An ETW session outlives a crashed profiler and keeps the providers enabled in the target. Sessions are named `WinUIProfiler-<profiler PID>-<id>`; starting a session stops those whose profiler process no longer exists. - Microsoft-Windows-XAML is enabled with keyword `0x100000`, which no manifest event uses, so only TraceLogging events are written. The README's `logman` recipe now uses the same keyword. ## Impact and risk Developer tooling only, under `tools/WinUIProfiler`; no product code changes. Low risk. Reading ETW needs an elevated profiler or membership in the local Performance Log Users group, as for `logman`. ## How Has This Been Tested? `tools/WinUIProfiler` has no test project yet, so this was validated against `logman` recording the same providers in parallel, on a local Release build of `Microsoft.UI.Xaml.dll` with `XamlProfilerEnabled` (not part of this PR). | Test | Result | | --- | --- | | `dotnet build .\WinUIProfiler.Cli\WinUIProfiler.Cli.csproj -c Release` | Passed, 0 warnings | | `.\scripts\Build-App.ps1 -Configuration Release -Platform x64` | Passed | | Unpackaged launch in the app | "from process start"; 42,483 + 13,983 events = logman's first 56,466 for that process, from its first event; 0 lost, 0 dropped | | Packaged launch in the app (Microsoft Store, which carries its own `Microsoft.UI.Xaml.dll`) | "from process start"; 82,689 events = logman's 82,689 | | Attach in the app | "from connection"; counts follow activity; session stopped on disconnect | | Exe filter, then switch to the process ID 1 ms, 0.5 s and 1 s after launch, with a second copy of the exe running | Target counts identical to logman; the other copy's events excluded after the switch | | Reading started 6 s after launch | Identical to logman (ETW buffered the events) | | AUMID-to-executable lookup for every app installed on the test machine | 300 of 300 match their manifests | | Session left by a dead profiler process | Stopped; a session of a running process is kept | | 24 short launch and attach runs | No failures, no sessions left | |
||
|
|
7f2f671327 |
WinUI Profiler: read XAML events live, from process start for launches
The app now reads the target's XAML TraceLogging events with a real-time ETW session while connected. For a launch the session starts before the process is created, filtered by executable name (for packaged apps, the Executable in the installed package's manifest), and switches to the process ID once the launch returns it, so the first events are included without suspending the target. Attach filters by process ID and counts from connection. Events go through a 65,536-event queue to one processing thread; the first processor counts them. The connection panel shows whether counts start at process start or at connection, events per provider, events lost by ETW and events dropped by the profiler. Sessions left by a crashed profiler are stopped. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> |
||
|
|
552b0dacb1 |
Remove GpuMemorySnapshot from the XAML profiler
While a profiler session was on, DCompSurface::TraceProfilerMemoryChange wrote GpuMemorySnapshot on every DComp surface allocation, free and resize. Each one took the shared D3D device lock, walked device -> DXGI adapter -> IDXGIAdapter3 and made two QueryVideoMemoryInfo kernel calls on the UI thread: about 21 us per surface change, most of it in the kernel, to write a 34-byte event. A launch of a 1,000-item ListView test app has 52-76 surface changes, so about 1.1-1.6 ms of UI-thread time. The DirectX kernel already traces the same numbers. Microsoft-Windows-DxgKrnl writes VidMmProcessUsageChange and VidMmProcessBudgetChange for a process on every change of its usage or budget, plus a rundown when the process first opens an adapter and on capture state. They carry the values QueryVideoMemoryInfo returns (all 52 snapshots in a ListView capture matched the kernel's values). Profiling tools can read those instead, so XAML needs no query and no event. DCompSurfaceMemoryChanged and XamlHeapSnapshot are unchanged. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: e91a0e91-385c-48fd-9b9c-daea01d24e65 |
||
|
|
347deb0843 |
Add XAML and Composition tree snapshots and viewer to WinUIProfiler (#12414)
## Fixes <!-- Link either the public GitHub issue or the internal issue this PR addresses. Remove the unused line. --> <!-- PRs without a linked issue may be closed unless they are minor documentation/typo fixes. --> Standalone profiler feature; no linked issue. Uses the tool-side snapshot approach from #12393, without its MUX instrumentation or other profiler features. ## PR Type <!-- Check the type of change. Limit each PR to one type when possible. --> - [ ] Bugfix - [x] Feature - [ ] Code style update (formatting, renaming) - [ ] Refactoring (no functional changes, no API changes) - [ ] Build related changes - [ ] Documentation content changes - [ ] Other (please describe): ## Description <!-- Describe your changes in detail. --> ### Current Behavior <!-- How does this work today? --> WinUIProfiler can connect to a target process and display live process metrics, but cannot capture, save, or inspect its XAML and Composition trees. ### New Behavior <!-- How will it work after this PR? --> This PR adds on-demand tree snapshots and an interactive viewer, entirely under `tools/WinUIProfiler`. - **Capture exactly two trees:** the realized XAML visual tree and publicly exposed Composition visual tree. The injected TAP discovers XamlRoots through public diagnostics callbacks and traverses each root on its owning UI thread using public XAML and Composition APIs. Open popup children are captured as separate visual subtrees. - **Record useful node data and direct correlations:** types, XAML names, hierarchy/order, selected SDK properties, COM object addresses, Composition `IVisual` addresses, optional diagnostics handles, root/thread metadata, and capture intervals. XAML-to-Composition links come from `ElementCompositionPreview.GetElementVisual` and canonical COM identity; they do not depend on internal comp-node pointers. - **Transport and save snapshots:** add snapshot request/chunk/result messages to the authenticated TAP connection. Validate request/capture/process identity, chunk ordering and sizes, schema, graph relationships, and correlation references before atomically saving JSON under `%LOCALAPPDATA%\WinUIProfiler\Snapshots`. Capture has explicit timeout, cancellation, size, and record limits, with visible failures rather than partial-success files. - **Select captures from snapshot history:** show successful snapshots newest first, with counts and saved paths. History survives disconnect/reconnect within the current profiler session. Selecting an entry opens its viewer; selecting it again reuses that window, while different captures can be opened in separate windows. - **Inspect both trees:** asynchronously load and validate saved snapshots, lazily populate tree branches, show selected-node properties, highlight direct counterparts in either direction, and provide a match picker when there are multiple direct matches. Unmapped nodes do not acquire guessed ancestor mappings. Missing or invalid files surface errors. - **Navigate larger snapshots:** display the XAML object's COM identity address or Composition visual's `IVisual` address beside each node, with a full-label tooltip. Each tree has independent **Expand all** and **Collapse all** controls; expansion includes lazy descendants, and collapse resets descendant expansion state. - **Reduce property noise:** hide known default-valued Composition properties in the viewer only. Non-default values, unknown properties, and getter errors remain visible. XAML properties and the complete saved JSON are unchanged. - **Integrate the standalone build and UI:** generate the TAP's required public C++/WinRT projections from the app's restored SDK metadata, retain x64/ARM64 payload staging, add localized/accessibility strings, and document capture, viewing, and limitations. **Scope and limitations:** no MUX/product changes, ETW listener, heap capture, recording database, internal `HWCompNode` tree, or private compositor-tree traversal. Composition names/comments are not added. The capture represents realized/publicly accessible objects, not a logical tree or globally atomic cross-thread snapshot. `GetElementVisual` may materialize handout visuals. Capture is bounded to 20 seconds, 64 MiB, and 131,072 records. JSON files persist after exit, but the history list is session-only and is not reloaded on startup. ## Customer Impact <!-- How does this change affect end users? Is it user-facing or infra changes? --> Profiler users can capture and inspect a connected application's XAML and public Composition trees, follow direct cross-tree mappings, and compare saved captures without rebuilding or modifying MUX. This is a standalone tooling feature; it does not change shipping WinUI control behavior or SDK APIs. ## Regression Potential <!-- Could this change cause regressions? What areas might be affected? --> - [x] Low risk — isolated change, limited scope - [ ] Medium risk — touches shared components or public APIs - [ ] High risk — architectural or breaking API change Changes are confined to the standalone profiler. The affected areas are TAP request/lifetime handling, SDK projection generation and payload staging, snapshot file validation, and viewer interactions. Capture is opt-in and runs work on target UI threads, so it can temporarily affect the profiled application's responsiveness; public visual acquisition can also materialize handout visuals. Existing authentication, heartbeat, metrics, and disconnect behavior remain in place. ## How Has This Been Tested? <!-- Describe how you validated your changes. --> - [x] I have performed a self-review of my own code - [ ] I have added tests to cover my changes - [ ] Existing tests pass locally Validation on Windows x64 with .NET SDK 10.0.403: - `.\scripts\Build-App.ps1 -Configuration Release -Platform x64` from `tools\WinUIProfiler` — **passed**, including native x64/ARM64 TAP and injector builds, managed app compilation, and payload staging. - `MSBuild.exe tools\WinUIProfiler\WinUIProfiler.App\WinUIProfiler.App.csproj /t:Build /p:Configuration=Release /p:Platform=x64 /nologo /v:quiet` — **passed** for the final viewer changes. - `dotnet format whitespace tools\WinUIProfiler\WinUIProfiler.App\WinUIProfiler.App.csproj --no-restore --verify-no-changes --include tools\WinUIProfiler\WinUIProfiler.App\ViewModels\SnapshotViewerViewModel.cs tools\WinUIProfiler\WinUIProfiler.App\Views\SnapshotViewerWindow.xaml.cs --verbosity quiet` — **passed**. - `git diff --check` — **passed**. - Session-only managed/native smoke harnesses — **passed** capture/transport validation, repeated captures, document/model validation, direct one-to-many and many-to-one mappings, cancellation/error cases, default-property filtering, raw-property preservation, and address labels. - Session-only Windows UI automation — **passed** capture/history across reconnect, lazy expansion, selected-node details, bidirectional mapping and match selection, viewer reuse/separate windows, disconnected history access, missing-file errors, and parent-window cleanup. A live capture contained 90 XAML nodes, 310 Composition nodes, and 90 correlations. - Final `verify-snapshot-tree-controls-ui.ps1 -ProcessId 22824 -Reuse` session-only UI check — **passed** every row's displayed address, full lazy-descendant expansion, independent collapse/reset/re-expansion of both trees, and mapping/details after bulk operations. The local smoke/UI harnesses are not included in this PR. No repository regression-test files were added. The full WinUI product test suite and native ARM64 execution were not run. ## Screenshots (if appropriate) <!-- If you are making visual changes, include before/after screenshots. --> Before this change there was no snapshot capture/history UI or tree viewer. The images below show the new snapshot history and the two-tree viewer with addresses, independent expansion controls, and selected-node details.   Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> |
||
|
|
c2f0d35060 |
WinUI Profiler: read XAML TraceLogging events from .etl files (#12415)
## Description First step of the WinUI Profiler's event pipeline (`tools/WinUIProfiler`): read XAML ETW events from a recorded `.etl` file, and a small CLI to look at them. ### Current Behavior The profiler app connects to a WinUI 3 app and shows CPU, GPU and memory. It does not read any XAML ETW events. ### New Behavior `WinUIProfiler.Core` can read the XAML events of one process from a `.etl` file: - `OfflineEtlSource.Read(path, processId, sink)` opens the file with TraceEvent and calls `sink` once per event. - `XamlEventSubscription` keeps only TraceLogging events (ETW channel 11) from Microsoft-Windows-XAML and Microsoft-Windows-XAML-Profiler, for the requested process. Manifest events are not read. Each event is copied into a `ProfilerEvent`, because TraceEvent reuses its event objects. - The same subscription will be reused for live sessions in the next PR. `WinUIProfiler.Cli` shows what a trace contains, without the UI: | Command | Shows | | --- | --- | | `processes <trace.etl>` | Event count per process, to find the target's PID | | `summary <trace.etl> --pid <pid>` | Event counts for that process, by event name | | `dump <trace.etl> --pid <pid> [--name <text>] [--max <n>]` | The events in order, with their fields | The README explains how to record a trace with `logman` and inspect it. Adds `Microsoft.Diagnostics.Tracing.TraceEvent` 3.2.8; the `packages.lock.json` changes come from restore. The app's behavior does not change in this PR. ## Impact and risk Developer tooling only, under `tools/WinUIProfiler`; no product code changes. Low risk. ## How Has This Been Tested? `tools/WinUIProfiler` has no test project yet, so this was validated with the CLI on a 60 s trace of a WinUI 3 sample app. Run from `tools/WinUIProfiler`: | Command | Result | | --- | --- | | `dotnet build .\WinUIProfiler.Cli\WinUIProfiler.Cli.csproj -c Release` | Passed, 0 warnings | | `.\scripts\Build-App.ps1 -Configuration Release -Platform x64` | Passed | | `WinUIProfiler.Cli processes <trace>` | Target: 73,860 events (57,695 Microsoft-Windows-XAML + 16,165 Microsoft-Windows-XAML-Profiler), 0 lost | | `WinUIProfiler.Cli summary <trace> --pid <target>` | No manifest events; `CoreServices_Frame`, `DependencyObject_Enter`, `ElementEnteredTree`, `ChildAdded`, `CompPeerLinked` present | | `WinUIProfiler.Cli dump <trace> --pid <target> --name CoreServices_Frame --max 2` | Prints `IsStart` and `FrameNumber` per frame | | README `logman` steps from a non-elevated prompt | Trace recorded and read by the CLI | The Microsoft-Windows-XAML-Profiler events need a `Microsoft.UI.Xaml.dll` built with `XamlProfilerEnabled=true`; the trace used a local Release build with that setting, which is not part of this PR. |
||
|
|
e73a0ae103 |
WinUI Profiler: read XAML TraceLogging events from .etl files
Add an event reader to WinUIProfiler.Core and a small developer CLI. - OfflineEtlSource reads a recorded .etl with TraceEvent. - XamlEventSubscription keeps TraceLogging events (ETW channel 11) from Microsoft-Windows-XAML and Microsoft-Windows-XAML-Profiler for one process and copies each into a ProfilerEvent. Manifest events are not read. - WinUIProfiler.Cli: processes, summary and dump commands. - README: how to record a trace with logman and inspect it. - Adds Microsoft.Diagnostics.Tracing.TraceEvent 3.2.8. The app's behavior is unchanged; it will use the reader for live ETW next. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> |
||
|
|
bd03b2c662 |
Fix WinUI Profiler process identity and reconnect controls (#12413)
Fixes # Internal Issue: https://task.ms/ <!-- Link either the public GitHub issue or the internal issue this PR addresses. Remove the unused line. --> <!-- PRs without a linked issue may be closed unless they are minor documentation/typo fixes. --> Addresses two review findings from #12406: - [Preserve the selected process identity](https://github.com/microsoft/microsoft-ui-xaml/pull/12406#discussion_r4385701293). - [Restore reconnect controls when a connection ends](https://github.com/microsoft/microsoft-ui-xaml/pull/12406#discussion_r4385930293). ## PR Type <!-- Check the type of change. Limit each PR to one type when possible. --> - [x] Bugfix - [ ] Feature - [ ] Code style update (formatting, renaming) - [ ] Refactoring (no functional changes, no API changes) - [ ] Build related changes - [ ] Documentation content changes - [ ] Other (please describe): ## Description <!-- Describe your changes in detail. --> ### Current Behavior <!-- How does this work today? --> - Selecting a process updates the PID textbox, but its queued text-change event clears the saved process creation time and bypasses the existing PID-reuse check. - When a target exits or its connection fails, the view model retains the finished connection. Connect and target options remain disabled until the user clicks Disconnect. ### New Behavior <!-- How will it work after this PR? --> - Keep the selected process and creation time together. Set the selection before updating the textbox, and invalidate it only when the target text differs. - Observe connection completion, dispose and unsubscribe the finished connection, then release ownership and refresh the UI. Retain its metric history, terminal status, and error details. Ignore an older completion if it no longer owns the current connection. ## Customer Impact <!-- How does this change affect end users? Is it user-facing or infra changes? --> Profiler users retain PID-reuse protection when selecting a running app and can connect again after exit or failure without a redundant Disconnect. Completed graphs and failure details remain available. ## Regression Potential <!-- Could this change cause regressions? What areas might be affected? --> - [x] Low risk — isolated change, limited scope - [ ] Medium risk — touches shared components or public APIs - [ ] High risk — architectural or breaking API change Only the profiler window and live-session view model change. No product runtime, TAP protocol, public API, or layout changes. ## How Has This Been Tested? <!-- Describe how you validated your changes. --> - [x] I have performed a self-review of my own code - [ ] I have added tests to cover my changes - [ ] Existing tests pass locally **Release x64 build: passed** ```powershell $env:DOTNET_MSBUILD_SDK_RESOLVER_CLI_DIR = 'C:\Program Files\dotnet' .\initrun.ps1 -Flavor amd64fre .\tools\WinUIProfiler\scripts\Build-App.ps1 -Configuration Release -Platform x64 ``` **Real-app regression checks: 22 passed, 0 failed** Ran a temporary external harness with `Run-Regression.ps1 -Label fixed-sampling` against the built profiler and FolderTreeAppCpp. Checks cover queued textbox events, edited-target invalidation, PID-reuse rejection before injection, live metrics, target exit, unexpected sampling completion, reconnect, event unsubscription, restored UI controls, retained history/errors, and manual disconnect without terminating the target. Both reported bugs were reproduced on the unchanged app first. The temporary harness was not added to the repository; no extra test files are included in this PR. No existing automated test suite was run. **Whitespace validation: passed** ```powershell git diff --check ``` ## Screenshots (if appropriate) <!-- If you are making visual changes, include before/after screenshots. --> Not applicable; no layout or visual changes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 18d84a18-e328-402b-b9ec-6a2269e43683 |
||
|
|
e172a68fee |
Update standalone WinUIDetails fallback to 2.6.4-dev.experimental1D (#12405)
## Fixes Internal Issue: https://task.ms/63217679 Related dependency fix: https://task.ms/64592557 ## PR Type - [x] Build related changes ## Description ### Current Behavior Internal standalone builds default to `Microsoft.Internal.WinUIDetails` `2.6.0-dev.experimental6` when no monobuild version is supplied. ### New Behavior Update only that fallback to `2.6.4-dev.experimental1D`. Preserve `WindowsAppSDKVersionPinned`, the later imported version-props mapping, the OSS pin and all six companion fallbacks. The selected package comes from a successful main-line producer on 24 October 2026; its normal-feed archive was verified against that producer's package. This is a dependency update, not a Charts or TableView source change. ## Customer Impact Internal standalone builds consume the newer component package. OSS and explicitly overridden monobuild selection are unchanged. ## Regression Potential - [x] Medium risk — the selected native dependency changes; package compatibility and runtime validation are still required. ## How Has This Been Tested? - [x] I have performed a self-review of my own code. - [ ] I have added tests to cover my changes. - [ ] Existing tests pass locally. - The ten-case MSBuild selection matrix passes before and after the edit, covering standalone, OSS, explicit pin, imported props, global-property precedence and malformed imports. - The proposed package's signature is verified, and its normal-feed archive matches the main-line producer package byte-for-byte. - An isolated, uncached restore of the actual internal component project selects the new package from the normal feed, with matching archive and NuGet content hashes. - Both unchanged real samples publish against the produced candidate component. Their native control peers and package-bound modules were observed at startup. - Full baseline and candidate x64 Release builds, component packaging and test-project compilation passed. Compilation coverage is separate from the selected executions below. - Selected headless tests passed in both lanes: 13 methods and 108 executions per lane, including 20 iterations each of three MockDComp keyframe and two transform tests. The loaded MockDComp modules match the restored packages. - Final component dependencies are unchanged; component, test-input and consumer-output hashes reconcile. - The public PR Build workflow disables its compile matrix. Its green checks are not architecture-build evidence; internal integration CI is still required. - Functional/regression, clean-shutdown, human accessibility and required CI acceptance remain pending; startup observations are not a substitute for those checks. - Local results cover the recorded source snapshot, not subsequent main changes or a new merge commit. ## Screenshots Not applicable to this dependency-only diff. |
||
|
|
8f7d838788 |
Placeholder WinUIProfiler app with tap connection (#12406)
## Fixes Fixes # Internal Issue: https://task.ms/<ado-bug-id> ## PR Type <!-- Check the type of change. Limit each PR to one type when possible. --> - [ ] Bugfix - [x] Feature - [ ] Code style update (formatting, renaming) - [ ] Refactoring (no functional changes, no API changes) - [ ] Build related changes - [ ] Documentation content changes - [ ] Other (please describe): ## Description WinUIProfiler tool placeholder ### Current Behavior N/A ### New Behavior Standalone tool, which launches and connects to packaged/unpackaged or already running app ## Customer Impact <!-- How does this change affect end users? Is it user-facing or infra changes? --> ## Regression Potential <!-- Could this change cause regressions? What areas might be affected? --> - [x] Low risk — isolated change, limited scope - [ ] Medium risk — touches shared components or public APIs - [ ] High risk — architectural or breaking API change ## How Has This Been Tested? <!-- Describe how you validated your changes. --> - [x] I have performed a self-review of my own code - [ ] I have added tests to cover my changes - [ ] Existing tests pass locally ## Screenshots (if appropriate) <!-- If you are making visual changes, include before/after screenshots. --> |
||
|
|
9449655062 |
ScrollViewer::OnPointerReleasedHandledEventsToo: clear stale press state (#12290)
Description: Keep ToggleSwitch focused after selecting a NavigationView item without suppressing application pointer handlers. Addresses #9331. Earlier review discussion: https://github.com/microsoft/microsoft-ui-xaml/pull/12255. Root cause: ScrollViewer sees the item's press but its normal release handler is skipped when the child handles the release. The stale pressed flag then lets a later toggle release move focus to ScrollViewer. Fix: Use handledEventsToo press/release listeners and a ScrollViewer-local counter to clear the flag when the observed presses have completed. Reset bookkeeping on unload. Remove the earlier core input and per-press event-state changes. Behavior: Keep the existing normal focus decisions and application event delivery. This change does not introduce a new focus policy for nested input. Testing: Native compilation/linking and targeted C#/XAML test |
||
|
|
d8ab570820 |
Localization Update 20261019 (#12403)
## Fixes Internal Issue: https://task.ms/64340236 ## PR Type - [x] Bugfix - [ ] Feature - [ ] Code style update (formatting, renaming) - [ ] Refactoring (no functional changes, no API changes) - [ ] Build related changes - [ ] Documentation content changes - [ ] Other (please describe): ## Description Brings the TableView translations from the 19 October localization update to main. The six TableView strings added in #11820 now have translations in all 85 supported languages. ### Current Behavior 12 of the 19 TableView strings are translated. The cell control type ("cell"), the cell and group-header name formats, the cell text separator and the Boolean values fall back to English in every language. Coverage is 1,020 of 1,615 string-language pairs (63.2%). ### New Behavior 18 of the 19 strings are translated: 1,528 of 1,615 pairs (94.6%). Two gaps remain: - `TableViewCellBlankName` (added in #12285) has no translations yet. - The pt-PT and ug-CN translations of `TableViewCellLocalizedControlType` are left out because both mean "mobile phone". Those two languages keep the English "cell" until the translations are corrected. The returned `TableViewBooleanTrue` and `TableViewBooleanFalse` values are identical to English in 13 and 14 languages, including zh-CN, zh-TW and ko-KR. Not included from the update: - The `Microsoft.Internal.WinUILocalizationResources` bump to 3.0.0-experimental.261019.1, which is not on the OSS dependency feed. - MapControl placeholder files, which have no translations yet. ## Customer Impact User-facing. Screen readers announce TableView cells, cell and group-header names and Boolean values from each language's resources instead of falling back to English, apart from the gaps above. ## Regression Potential - [x] Low risk — isolated change, limited scope - [ ] Medium risk — touches shared components or public APIs - [ ] High risk — architectural or breaking API change Resource-only: 85 non-English `Resources.resw` files gain entries. No code, English strings or existing translations change. ## How Has This Been Tested? - [x] I have performed a self-review of my own code - [ ] I have added tests to cover my changes - [ ] Existing tests pass locally - 83 files are byte-identical to the generated update; pt-PT and ug-CN differ only by the one removed entry. - Against main: 2,032 lines added, none removed. No existing value, file header, encoding or line ending changes, and `en-us` is unchanged. - All 85 files parse as XML, and the format placeholders (`%1!s!`, `{0}`) match English in all 1,528 translated values. ## Screenshots (if appropriate) Not applicable. |
||
|
|
163f3a56ae |
Apply multiline coding style to native profiler metrics
Expand braced control flow, separate declarations and statements, and replace local lambdas with named file-scope helpers without changing metric behavior. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 0da43f78-cdc6-4dd3-b731-29dd185daded |
||
|
|
ff50256c5d |
Refine profiler charts, XAML UI and typed metric interop
Use quarter-second sampling with left-to-right two-minute charts and utilization scaling. Move dialogs and chart visuals into XAML, adopt the App SDK file picker, and replace byte-offset decoding with typed native contracts. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 0da43f78-cdc6-4dd3-b731-29dd185daded |
||
|
|
c8bfb0c3ef |
ResourceResolver::GetParentForResourceLookup: preserve flyout scope (#12344)
Description: NavigationView child selection indicators ignore Grid-scoped NavigationViewSelectionIndicatorForeground overrides when shown in a flyout. Ordinary flyouts also miss owner-scoped resources in default templates and code-set theme bindings. Fixes #8283. Root cause: ReparentRepeater moves the children under an internal Popup with no parent. The resource walk stops there instead of continuing through the associated flyout's declaring scope. Fix: Share GetParentForResourceLookup between live ThemeResource refresh and SetThemeResourceBinding. Use the existing GetAssociatedFlyoutNoRef and GetMentor APIs for template and ordinary flyouts, retaining the visited-popup cycle guard. Fix test-host initialization using App.TestContentRoot and wait for flyout closure before resource-test teardown. Behavior: Nearer overrides and application/default fallbacks remain unchanged; owner-scoped flyout colors follow theme changes. Local validation passes 190 AppPopup assertions and 14 targeted TAEF tests, including ordinary/shared/nested flyouts and cyclic ownership. Remote CI validation is still required. |
||
|
|
79fe886bc8 |
Fix Window.DispatcherQueue use-after-free after XAML shutdown (#12346)
## Fixes Internal Issue: https://task.ms/55026189 ## PR Type - [x] Bugfix - [ ] Feature - [ ] Code style update (formatting, renaming) - [ ] Refactoring (no functional changes, no API changes) - [ ] Build related changes - [ ] Documentation content changes - [ ] Other (please describe): ## Description ### Current Behavior `Window.DispatcherQueue` is meant to be read from any thread; its generated getter has no thread check. But `Window::get_DispatcherQueueImpl` walked `DesktopWindowImpl::m_dxamlCoreNoRef`, a raw `DXamlCore*` cached in the constructor and never cleared. A desktop app shuts XAML down on a thread when its last window closes, which deletes that thread's core. If the app still holds the `Window`, and a background thread then reads `Window.DispatcherQueue` (for example, to marshal work back to the UI thread during shutdown), the getter walks freed memory, and the app can crash with an access violation. Crash dumps show exactly this: a closed window kept alive by a managed reference, read from a thread-pool worker during process exit. ### New Behavior - `WindowImpl` gets `get_DispatcherQueueImpl`, and `Window` delegates to it. - `DesktopWindowImpl` captures a weak reference to its thread's `DispatcherQueue` once, in its constructor, and resolves it in the getter. The getter never touches the core. - While the window's XAML is running, the core's dispatcher holds the queue, so the getter returns the same object as before. - After XAML shuts down, it returns the shut-down queue while anything still holds it, and `TryEnqueue` returns false. Once the queue is destroyed it returns null, which the getter's `_Outptr_result_maybenull_` contract already allows. - The reference is weak because `DispatcherQueue` keeps its `ShutdownStarting`/`ShutdownCompleted` handlers until it's destroyed. A strong reference would leak any window captured by such a handler. - `UWPWindowImpl` keeps its existing lookup. The new regression test `XamlIslandTests::GetDispatcherQueueFromThreadpoolAfterCoreShutdown`: 1. closes a `Window` that it keeps alive; 2. shuts that thread's XAML down and verifies the core is gone; 3. reads `Window.DispatcherQueue` from an MTA thread-pool worker. It checks that the same queue is returned before `Close`, after `Close` and after shutdown, and that `TryEnqueue` then returns false. ## Customer Impact Apps that read `Window.DispatcherQueue` from a background thread around shutdown no longer crash in the getter. Once the window's queue has been destroyed, the getter returns null, so callers on other threads should null-check it. Behavior while a window's XAML is running is unchanged. ## Regression Potential - [x] Low risk — isolated change, limited scope - [ ] Medium risk — touches shared components or public APIs - [ ] High risk — architectural or breaking API change While XAML is running, the getter returns the same object as before. Behavior changes only where the old code read freed memory, or during core teardown, where the old lookup could return null. The weak reference adds no lifetime extension or reference cycle. The member is immutable after construction, so reading it from other threads is safe. ## How Has This Been Tested? - [x] I have performed a self-review of my own code - [x] I have added tests to cover my changes - [ ] Existing tests pass locally Validated on amd64chk on an internal branch that predates #12268 and #11982, with and without the fix: | | With fix | Without fix | |---|---|---| | New regression test | Passes | Access violation in the getter's core walk | | XamlIslandTests, Helix query, Win32Explicit (46 tests on that branch) | 40 pass | 39 pass; the extra failure is the new test | | WindowIntegrationTests, Helix query, WPF (42 tests on that branch) | 42 pass | 42 pass | Six other XamlIslandTests fail on that machine both with and without the fix; they are unrelated to this change. Among the files this fix touches or depends on, that branch differs from `main` only in these, none of which affect the fix: - Its `DesktopWindowImpl.cpp`/`.h` lack #12268, which changes only the Width/Height setters after `Close`. - Its `WindowsXamlManager_Partial.cpp`/`FrameworkApplication_Partial.cpp` lack #11982's shutdown changes. On `main` the core is still deleted at shutdown, and the test keeps the queue alive. - Its `XamlIslandTests.cpp`/`.h` lack #11982's new tests, hence the different test count. - `Window_Partial.cpp`, `UWPWindowImpl.*`, `WindowImpl.h`, `DXamlCore.cpp` and `xcpwindow.cpp` are byte-identical. Not built on `main` locally; relying on PR CI. ## Screenshots (if appropriate) Not applicable; no visual change. --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 84bf8dd5-9217-47bc-a3f9-88bffebaff37 |
||
|
|
b22743a863 |
TableView: reorganize and order IDL files (#12382)
Reorganizes the IDL files added for the TableView effort so they match
the layout and ordering of established controls (ItemsView, ScrollView,
SelectorBar, InfoBar, TeachingTip, Repeater,
`controls/idl/Microsoft.UI.Xaml.Controls.idl`). It also moves the IDL
comment notes into the TableView dev spec and rewrites that spec to be
accurate and concise. No API is added, removed or renamed.
### Files
- `controls/dev/TableView/TableView.idl`
- `controls/dev/TableView/TableViewSource.idl`
- `controls/dev/dll-tabular/Microsoft.UI.Xaml.Controls.Tabular.idl`
- `controls/dev/ResizeGripper/ResizeGripper.idl`
- `controls/dev/ResizeGripper/ResizeGripperAutomationPeer.idl`
- `controls/dev/TabularShaping/SortDirection.idl`
- `docs/design-notes/TabularControls/TableView-dev-spec.md`
### IDL changes
- **Type order:** enums, interfaces, column classes, event args (in the
same order as the events), helper classes, the control, then automation
peers (in the same order as the helpers).
- **Member order:** constructor, properties, methods, events, then
statics. Dependency-property statics follow the order of their
properties.
- **Formatting:** matches the reference IDLs.
- **Comments:** documentation and rationale comments moved into the dev
spec. A coverage check confirms all 111 comment blocks are captured.
Only one-line "do not ..." guard comments stay in the IDLs
(SortDirection namespace, Tabular IDL ordering and namespace,
ResizeGripper private namespace, frozen overload metadata on
`TableViewSource`), and each points to the dev spec.
### Dev spec changes
- **Size and structure:** rewritten to be accurate, concise and
consistent with the other dev specs in the repo (TitleBar, SelectorBar).
It went from about 14,200 to about 4,900 words and follows the repo
template order: Description, Design considerations, Architectural
overview, Performance, Debugging, Implementation details, Appendix.
- **Content:** IDL notes are folded into the feature sections.
Duplicated and stale text is removed.
- **Accuracy:** every claim was cross-checked against the code, and
about 30 incorrect or outdated statements were fixed. Examples:
- the "Key public components" list now matches the IDLs (removed
`TableViewEditingUnit`, `TableViewEditAction.Discard` and the public
editing hooks)
- no edit deferral in this release
- row template parts and states
- the body repeater's `ItemTemplate` is `TableViewRowTemplateSelector`
- header scroller mode and row cache length
- the cell wrapper is a `Grid`
- cell UIA control type, and `ITransformProvider` on the column-header
peer
- keyboard map and density resource keys
- why `TableView` is unsealed
- **Cited wording kept:** headings and phrases cited by code comments
("Keyboard", "Re-shape while focused", "Sticky headers", etc.) are
unchanged.
### ABI note
Member reordering changes the vtable order and IIDs of `ITableView`,
`ITableViewColumn`, `ITableViewColumnStatics`, `ITableViewSource` and
the private `IResizeGripper`.
- All TableView types are `[MUX_PREVIEW]` (Feature_Experimental), and
ResizeGripper is `[MUX_INTERNAL]` in the private namespace.
- Preview consumers, including C# `TableViewColumn` subclasses, need to
rebuild.
- `ITableViewColumnOverrides`, `ITableViewStatics` and
`ITableViewSourceStatics` are unchanged.
### Follow-ups (not in this PR)
- The API spec (`docs/api-specs/TableView/TableView-spec.md`) still
calls column resize "planned" and says duplicate row identity always
fails fast. Update it, and move the dev spec's "API notes not yet in the
API spec" appendix into it.
- About 100 code and test comments cite `TableView.idl:<line>` or
`dev-spec:NNN` line numbers. Rewrite them to section names.
- A few code comments in `TableView.h` and `TableView_Editing.cpp` still
mention a "deferral" that doesn't exist.
### Validation
- **IDL equivalence:** a script that strips comments and whitespace and
compares types, attributes and members confirms the six IDLs differ only
in order and comments.
- **Build:** the Tabular idl and dll projects build with 0 warnings and
0 errors, and checked-in generated files are unchanged.
- **WinMD comparison** (Tabular, Private.Controls, XamlTypeInfo) against
main: identical types, members, signatures and attributes. The only
differences are the 5 IIDs above.
- **Tests:** the TableView suite (882 tests) ran on a test VM. 876
passed and 3 are `[Ignore]`. The remaining 3 failed or hung in the full
run but passed when rerun alone on this branch and on the baseline. The
grouped automation-peer chunk hangs intermittently on both this branch
and baseline. Later commits change only type order, comments and docs.
- **Dev spec:** independent fact-check and readability reviews were run,
and all findings are addressed. A final review of the last fixes found
two issues, both corrected.
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 7e074e0a-bef2-4f87-ae83-4ebdf5da11b2
|
||
|
|
56d45d2665 |
Replace profiler UI polling with change notifications
Publish connection/heartbeat and committed-sample changes through the session view model. Coalesce notifications on the UI dispatcher, skip unchanged chart histories, and detach publishers during disconnect and window shutdown. Validate Debug/x64 production build, session-only notification RED/GREEN checks, and actual target UI state, heartbeat, metrics, reconnect, exit and shutdown without restoring test files to the branch. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 0da43f78-cdc6-4dd3-b731-29dd185daded |
||
|
|
357f33ea05 |
Remove tests from profiler executable placeholder
Remove managed/native test projects, UI/build test scripts and generated test artifacts. Drop test-only package versions, friend-assembly access and stale test documentation while preserving production components. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 0da43f78-cdc6-4dd3-b731-29dd185daded |
||
|
|
e43de2ad2c |
TableView: TiP attribution, per-operation TiP tests and TableView_Instance telemetry (#12301)
Builds on #12120. TableView's TiP blames the app only when its own code decided the outcome, each key operation has its own TiP test and measure, and telemetry records one event per table load. ## TiP tests | Test | Measure | Fails with | |---|---|---| | Initialize | 64477764 | `template_setup_failed`, `source_attach_failed` | | Sort | 64745033 | `sort_failed` | | Edit begin | 64745034 | `edit_begin_failed` | | Edit commit | 64745035 | `edit_commit_failed` | | Filter | 64745036 | `filter_failed` | | Expand or collapse one group | 64745037 | `group_expansion_failed` | | Expand or collapse one tree node | 64745038 | `hierarchy_expansion_failed` | - A test fails, with the HRESULT, only when TableView's own code throws. App code's exceptions are tagged where TableView calls it, so the test closes Ignored (`application_fault`) only when one of them decided the outcome. - Column code: the editing hooks are TableView's for every column, except where they realize or focus a `CellEditingTemplate`'s content. That content, and the overrides of a column class the app derived, are the app's. - Initialize runs from the template applied with a source attached until the first row or group header is laid out. Unloaded before that with rows and a body viewport, it closes Ignored as `unloaded_before_render`; otherwise as `unloaded`. - The metrics bucket is the flow direction and whether the source has at least 1,000 rows before the filter. The metrics carry both row counts. - No test runs for selection, a source swap after Initialize, a cancelled edit, group configuration, a subtree expansion or an expansion request that changes nothing. A failure in any of them still writes `TableView_Error`. - A process exit while a test runs is recorded by TiP as that test's failure, and so is a crash or hang on Windows builds whose Error Reporting recovers TiP results. - 64477774, the old combined operation test, is retired after this merges. ## Telemetry - `TableView_Instance` (one per load) replaces `TableView_SessionSummary`: enabled and used features, source-row, data-row and realized-row buckets, column and source kinds, RTL, and an incomplete template. - An app filter predicate, sort or group key selector or custom comparer that throws is still contained, and now writes one recoverable `TableView_Error` per operation with the first HRESULT and a `FaultCount`, also when the operation then fails. A verb an app delegate starts inside another counts only its own. - Scrolling is not measured in the field; scroll performance is left to a lab benchmark. ## Validation - 28 operation, 15 Initialize and 20 `TableView_Instance` cases, checked from captured ETW events of the final build. The 7 operation and Initialize cases whose behavior changes also ran on `f6c060a` and differ there; the other 22 operation cases match it. - 175 earlier cases rerun against `42b575b`: 174 identical; the other now also reports a group key that threw inside a filter that then threw an app data fault, as intended. - A local fault-injection build forces 19 failure paths, including built-in and app-derived column code, TableView's own code around a `CellEditingTemplate` editor, failures after contained app delegates, an unload before the first row's layout pass, and a swallowed app exception. TableView's own failures fail their test with its reason and the HRESULT; an app-tagged one closes Ignored. - UI Automation walkthrough of the final build (sort, edit, filter, group, tree node, selection): 13 successes, each under its own measure, none for selection, and no `TableView_Error`. - MSBuild: an internal pipeline build fails when the TiP package or its config is missing; the real package, the opt-out, local builds and external pipelines pass. A build with the public stub compiles. ## Notes - Versions start at 1 (`SchemaVersion`, `InstanceVersion`, both TiP data versions), since nothing has shipped. - Internal pipeline builds (`IsInternalWinUIBuild` and `TF_BUILD`) fail when Tabular falls back to the public TiP stub; `UseTestInProductionStub=true` chooses it on purpose. - The dev spec's "Telemetry and TiP" section documents every event, test, field and known gap. - Left out of the telemetry plan: a resource-load outcome (a missing `TabularControlsResources` fails at XAML parse) and separate "reached past the box" bits (`ColumnKinds` covers them). - Commits: `TableView_Instance` (`1737209`), Initialize (`6687f88`), per-operation tests (`1f82669`), review fixes (`927f474`), merge with main after #12307 (`42b575b`, conflicts in `BeginEdit` and the sort apply path), Copilot review fixes (`1f66475`). Review with whitespace hidden. |
||
|
|
53d167f57b |
build: refresh standalone WinUIDetails fallback
Update only the internal WinUIDetails fallback; preserve the OSS pin, monobuild overrides and companion versions. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 9d4612db-43aa-4271-86fa-b1b43d45d9c3 |
||
|
|
92621d39e3 |
Add standalone WinUI profiler executable baseline
Support packaged/unpackaged launch and running-process attach through an authenticated hello-only diagnostics TAP. Start independent CPU/GPU and process-memory combined graphs after the real TAP connection; preserve the target on cancellation, disconnect and shutdown. Include self-contained x64/ARM64 builds, architecture-matched payload deployment, bounded metric histories and managed/native/UI/build-entry validation. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 0da43f78-cdc6-4dd3-b731-29dd185daded |
||
|
|
5aab9d7579 |
NavigationView: don't clip PaneFooter/PaneCustomContent item icons in closed compact pane (#12319)
## Fixes Fixes #10415 Fixes #11108 ## PR Type - [x] Bugfix ## Description A `NavigationViewItem` placed directly in `NavigationView.PaneFooter` (or `PaneCustomContent`) has the right ~2px of its icon clipped when the pane is closed in compact mode (`LeftCompact`, or `Left`/`Auto` with the pane closed). `FooterMenuItems` and `MenuItems` are not affected. **Root cause:** the left-pane item presenter template gives `ContentGrid` a `Margin="0,0,14,0"` so content lines up with the expand/collapse chevron. The `ClosedCompactAndTopLevelItem` presenter state removes that margin, but `NavigationViewItem::UpdateVisualStateForClosedCompact` only selects that state when `IsTopLevelItem()` is true. `IsTopLevelItem` is set only in `NavigationView::OnRepeaterElementPrepared`, so items that live in `PaneFooter`/`PaneCustomContent` (not created by a NavigationView-owned repeater) never get it. In a 48px compact pane the item's `PresenterContentRootGrid` is 40px. The 14px margin leaves a 26px slot for `ContentGrid`, which needs 40px for the icon column, so layout clips it at x=30. The 16px icon spans x=16..32, so its right edge gets cut. PR #5039 (#4482) already fixed the icon *width* binding for PaneFooter items. This PR fixes the remaining *margin* problem. ### Current Behavior In a closed compact pane, PaneFooter/PaneCustomContent items keep `ContentGrid.Margin = 0,0,14,0` and the right ~2px of the icon is clipped. A Message glyph measures x=16..29 instead of 16..31. In the #11108 screenshot, the power icon is 2px narrower than the menu icons. ### New Behavior `NavigationViewItem::UpdateVisualStateForClosedCompact` also treats an item as root-level when it is hosted in the left pane's `PaneFooter` or `PaneCustomContent`. A new helper, `NavigationView::IsInLeftPaneFooterOrCustomContent`, walks up the visual tree and returns true only if it reaches `FooterContentBorder`/`PaneCustomContentBorder` before another `NavigationViewItemBase` or the NavigationView itself. These items now get exactly the same layout as top-level menu items: no clipping in closed compact, and unchanged when the pane is open. Everything else keeps the old `IsTopLevelItem()` value: NVIs in `NavigationView.Content`, NVIs nested in another item, and recycled/unparented items. The walk only runs while the pane is closed compact, for items that aren't in a NavigationView-owned repeater. `NavigationViewItem` also re-prepares on every `Loaded` (hooked once in the constructor; SplitView subscriptions are re-pointed, not duplicated) and then calls `ReparentRepeater()`. `NavigationViewItemBase::OnLoaded` re-resolves the ancestor NavigationView for items outside an NV-owned repeater. So an item reused across hosts gets the right closed compact state in each host: MenuItems → PaneFooter → Content, Content → MenuItems, or moved to another NavigationView. For hierarchical items moved to another NavigationView, the child repeater stays wired to the original NavigationView; that is pre-existing and out of scope. ## Customer Impact User-facing visual fix. Footer buttons like "Exit"/"Log out" in a compact NavigationView render their icon fully, the same as menu items. It also covers NVIs in `PaneCustomContent` (reported in the #10415 comments). Apps no longer need the `CompactPaneLength=52` workaround. ## Regression Potential - [x] Low risk — isolated change, limited scope Only NVIs hosted in the left pane's PaneFooter/PaneCustomContent are affected, and only while the pane is closed compact. Items in `MenuItems`/`FooterMenuItems`, hierarchical children and flyout children all go through NV-owned repeaters and are unchanged. NVIs in `NavigationView.Content` are unchanged (covered by a negative test). In Left (open), LeftMinimal and Top modes nothing changes. Every path that changes the inputs (IsPaneOpen, PaneDisplayMode, Compact/ExpandedModeThresholdWidth, resize in Auto, IsPaneVisible, CompactPaneLength, footer content set or moved at runtime, template re-apply) re-evaluates the state through the item's existing SplitView listeners or `Loaded`. The runtime ones are covered by a test. ## How Has This Been Tested? - [x] I have performed a self-review of my own code - [x] I have added tests to cover my changes - [x] Existing tests pass locally (see notes) New API tests in `NavigationViewTests`: - `VerifyPaneFooterItemIsNotClippedInClosedCompactPane`: `LeftCompact` with a closed pane, a menu item, a PaneFooter NVI and a PaneCustomContent NVI. Checks the presenter state, checks that the icon column fits in ContentGrid's layout slot, and does an open → close round trip. - `VerifyContentAreaItemKeepsLayoutInClosedCompactPane` (negative): an NVI in `NavigationView.Content` keeps its normal layout. - `VerifyHierarchicalPaneFooterItemIsNotClippedInClosedCompactPane`: a PaneFooter NVI with children isn't clipped. - `VerifyItemReusedFromMenuItemsRefreshesClosedCompactStateOnEveryHost`: an item realized in `MenuItems`, moved to `PaneFooter`, pane opened/closed, then moved into `Content`. Checks the state and InfoBadge placement in each host. - `VerifyItemMovedFromContentToMenuItemsGetsClosedCompactState` and `VerifyItemMovedToAnotherNavigationViewFollowsItsPane` (NavView A PaneFooter → B PaneFooter → B MenuItems; the item follows B's pane state). - `VerifyPaneFooterItemClosedCompactStateFollowsRuntimeChanges`: the footer is set after load, then runtime `ExpandedModeThresholdWidth`/`CompactModeThresholdWidth`/`PaneDisplayMode` changes, and the item is moved PaneFooter → PaneCustomContent → Content. Results (full builds on the test VM): - Test-only commit |
||
|
|
a0a9858a91 |
Localization Update 20261019
Bring the TableView translations from the 19 October localization update to main. The six TableView strings added in #11820 are now translated in all 85 supported locales. The pt-PT and ug-CN translations of the cell control type ("cell") are left out because both mean "mobile phone". Those two locales keep the English text until the translations are corrected. Not included from the update: the localization resource package bump, which is not on the OSS dependency feed, and the MapControl placeholder files, which have no translations yet. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 92bba586-bebe-4dd6-8f1d-28a868960e3b |
||
|
|
4ce52241eb |
InkToolbar: make the toolbar's keyed resources visible to app XAML (#12296)
**What:** InkToolbar's keyed brushes, styles, and sizes can be used from
app XAML (for example `{StaticResource InkToolbarCommonButtonStyle}`).
Split out of #12163. XAML move only; no values change.
**Why:** On `main` every such reference fails with `0x802B000A`
(resource not found). The resources are in `InkToolbar.xaml`, which ends
up in the controls `generic.xaml`. XAML only uses that for default
styles; app `{StaticResource}` / `{ThemeResource}` lookups never search
it. Snipping Tool copied the resources into its own dictionary as a
workaround.
**UWP:** the resources were in the system `generic.xaml`, which app
lookups do search. The Windows SDK copy
(`UAP\10.0.26100.0\Generic\generic.xaml`) has 41 `InkToolbar*` brushes
and sizes in each theme plus 10 top-level `InkToolbar*` styles.
**Fix:** move them to `InkToolbar_themeresources.xaml`, which is merged
into `XamlControlsResources` like every other control's theme resources.
All 59 theme keys are defined in each of `Default`, `Light`, and
`HighContrast`.
**Tested:** a test app loads markup that references each key.
| Key | 2.5.4-experimental | `main` | This PR |
|---|---|---|---|
| `InkToolbarGlyphFontStyle`, `InkToolbarCommonButtonStyle`,
`InkToolbarFlyoutStyle` | fail | fail | **resolve** |
| `InkToolbarAccentColorForegroundThemeBrush`,
`InkToolbarButtonBackground`, `InkToolbarButtonWidth` | fail | fail |
**resolve** |
| `InkToolbarAccentColorThemeBrush` (UWP-only name) | fail | fail | fail
(never defined in WinUI) |
A default InkToolbar screenshot (light theme, 700×120 px) on `main` vs
this PR: 0 differing pixels. New API test:
`InkToolbarTests.InkToolbarKeyedResourcesAreAppVisibleTest`.
|
||
|
|
ff21107d94 |
Emit owning-element identity on remaining XAML framework ETW events (#12179)
## Fixes <!-- Link either the public GitHub issue or the internal issue this PR addresses. Remove the unused line. --> <!-- PRs without a linked issue may be closed unless they are minor documentation/typo fixes. --> Combines #11959 and #12116 into one PR targeting `main`. No separate issue is linked. ## PR Type <!-- Check the type of change. Limit each PR to one type when possible. --> - [ ] Bugfix - [ ] Feature - [ ] Code style update (formatting, renaming) - [ ] Refactoring (no functional changes, no API changes) - [ ] Build related changes - [ ] Documentation content changes - [x] Other (please describe): Profiler-only ETW instrumentation and core-object identity correlation. ## Description <!-- Describe your changes in detail. --> ### Current Behavior <!-- How does this work today? --> Several XAML operation events lack a usable core-object identity, particularly on both edges of an activity. This makes it difficult to correlate layout, focus, selection, virtualization, hit-testing, and related operations with visual-tree records. A generic dependency-object pointer also cannot always be treated as a UI-element identity: a Hyperlink, for example, is not a `CUIElement`. Some existing events already identify their UI object using `ElementId` or `ObjectPointer`. This PR does not rename those existing schemas. ### New Behavior <!-- How will it work after this PR? --> Adds a shared `XamlElementTracing` emitter and runtime identity resolver, with **43 operation names (26 activities and 17 point events) across 71 emission sites in 21 source files**. #### Provider and build behavior - The operation events are self-describing TraceLogging events on **`Microsoft-Windows-XAML`**, GUID `{531A35AB-63CE-4BCF-AA98-F88C7A89E455}`, using the existing `XamlTelemetryLogging` registration. - The existing four-tree events remain on **`Microsoft-Windows-XAML-Profiler`**. Their schemas are unchanged. Enable both providers when collecting operation-to-tree correlation data. - New instrumentation is guarded by `XAMLPROFILER_ENABLED`. Retail builds retain their existing manifest events and payloads. - Profiler-enabled builds generally replace the corresponding manifest emission with the identity-bearing TraceLogging emission. `RealizeTransition` is an additional per-element point inside the existing pass-level activity. `GenerateMCContainer` uses a point at container realization instead of the manifest Begin/End pair in profiler builds. - Identity resolution runs only when the operation provider is enabled at Verbose level. - Guarded `XamlProfilerTracing.h` includes move into the relevant `precomp.h` files, replacing repeated includes in implementation files. #### Identity payloads | Subject | Fields emitted | Meaning | |---|---|---| | UI element, including a control | `ElementId` | The subject's core `CUIElement*` address, serialized as `uint64_t`. | | Non-UI dependency object with a known UI host | `objectpointer`, `ElementId` | The subject's core `CDependencyObject*` address and its associated UI host's `CUIElement*` address. | | Non-UI dependency object without a known UI host | `objectpointer` | The subject's core dependency-object address; no UI identity is invented. | | Null subject | `objectpointer = 0` | No `ElementId`, even if an owner argument was supplied. | Controls use the same `ElementId` field as other UI elements; no redundant control-pointer field is emitted. These are core addresses, not assigned IDs or DirectUI/WinRT wrapper addresses. For non-UI subjects, known-owner correlation covers: - Text elements such as Hyperlink: the existing containing TextBlock/RichTextBlock lookup. - `PrepareContainer`: the preparing ItemsControl. - `SetupCCC`: the ListViewBase owner. - `GenerateContainer` Stop: the generator host, when it supports `IUIElement`. A UI subject always keeps its own identity rather than being replaced by its owner's. Arbitrary dependency-object parent walking is not added. A containing text host is not necessarily the RichTextBlockOverflow rendering a particular run. `ElementId` can be joined with `ElementEnteredTree.ElementId` and `CompPeerLinked.UIElementId` within the same process and object lifetime. This does not guarantee that every operation has an entry record in a particular capture, or that every entered element generates an operation. Layout-pass events identify the layout root, not every child measured or arranged. #### Activity events Both Start and Stop carry the subject's identity using the rules above. `GenerateContainer` Start is the exception: it has no identity because the container has not yet been resolved. Other operation-specific fields are retained on the applicable edge. | Event | Subject | Additional payload / edge behavior | |---|---|---| | `FireLayoutUpdated` | Layout root | Identity on Start and Stop. | | `FireSizeChanged` | Layout root | Identity on Start and Stop. | | `IndividualSizeChanged` | Element receiving the size-change notification | Identity on Start and Stop. | | `PutRootVisual` | Public root visual | Identity on Start and Stop. | | `Layout` | Layout root | Identity on Start and Stop. | | `Measure` | Layout root | Whole measure pass, distinct from existing per-element `MeasureElement` events. | | `Arrange` | Layout root | Whole arrange pass, distinct from existing per-element `ArrangeElement` events. | | `CancelTransitions` | Transition target | Identity on Start and Stop. | | `ProcessLayoutForTransition` | Transition target | Identity on Start and Stop. | | `UpdateFocus` | Requested target on Start; `pNewFocus` on Stop | Identities can differ after focus redirection; Stop is also emitted on cancellation/error paths. | | `XYFocusEntered` | Current focused element or hosted component used for navigation | Start includes `Direction`. | | `ChangeSelection` | Owning text control | Identity on Start and Stop. | | `ExtendSelectionRange` | Owning text control | Identity on Start and Stop. | | `GenerateContainer` | Generated container on Stop | Start has no identity; Stop resolves the container and optional UI host. | | `GenerateItems` | Generating panel | Identity on Start and Stop. | | `PlaceElement` | Realized container | Start includes `DataIndex`. | | `PrepareContainer` | Container being prepared | Uses the preparing ItemsControl as host only for a non-UI container. | | `MeasureChild` | Child being measured | Identity on Start and Stop. | | `VirtualizationMeasure` | WrapGrid | Identity on Start and Stop. | | `VirtualizationAdd` | Container being added | Identity on Start and Stop. | | `VirtualizationCleanup` | Virtualizing panel | Identity on Start and Stop. | | `UpdateLogicalScrollData` | Virtualizing panel | Identity on Start and Stop. | | `GetElementCount` | Core object whose descendants are counted | Identity on Start and Stop. | | `GetBuiltInStyle` | Control, not the returned style | Stop includes `StyleName`. | | `HitTest` | Subtree being hit-tested | Identity on Start and Stop. | | `SetupCCC` | Container being set up | Start includes `ItemIndex`; a non-UI container can be linked to the ListViewBase owner. | #### Point events Each point carries the subject's identity plus the listed operation-specific payload. | Event | Subject | Additional payload | |---|---|---| | `RealizeTransition` | Transition element | None. | | `TouchSelectionGripperShowBegin` | Gripper element | `IsStartGripper`, `X`, `Y` | | `TouchSelectionGripperShowEnd` | Gripper element | `IsStartGripper`, `X`, `Y` | | `TouchSelectionGripperHideBegin` | Gripper element | `IsStartGripper`, `X`, `Y` | | `TouchSelectionGripperHideEnd` | Gripper element | `IsStartGripper`, `X`, `Y` | | `TouchSelectionGripperReposition` | Gripper element | `IsStartGripper`, `X`, `Y` | | `GenerateMCContainer` | Realized container | `Index` | | `VirtualizationIsEnabledByLayout` | ListViewBase | `IsVirtualizationActive`, `ElementName`, `ClassName`, `ParentClassName` | | `OuterBoundsStats` | Hit-tested subtree | `RecalculatedCount`, `ReusedCount` | | `InnerBoundsStats` | Hit-tested subtree | `RecalculatedCount`, `ReusedCount` | | `ContentBoundsStats` | Hit-tested subtree | `RecalculatedCount`, `ReusedCount` | | `ChildBoundsStats` | Hit-tested subtree | `RecalculatedCount`, `ReusedCount` | | `HubSectionCount` | Hub | `SectionCount` | | `FrameNavigated` | Frame, not its content | `Descriptor`, `NavigationMode` | | `FrameNavigating` | Frame, not its content | `Descriptor`, `NavigationMode` | | `SendInputScopeToRichEdit` | PasswordBox | `InputScope` | | `SetIhmKeyboardSkin` | PasswordBox | `KeyboardSkin` | #### Scope boundaries - Existing `FrameworkElement_ApplyTemplate` and `Control_RefreshTemplateBindings` events retain their `ObjectPointer` fields. No duplicate `ElementId` field is added to them. - Existing four-tree schemas and profiler-viewer ingestion/display are unchanged. - The added profiler identity test and performance inventory document have been removed; neither remains in the final PR diff. ## Customer Impact <!-- How does this change affect end users? Is it user-facing or infra changes? --> Diagnostic improvement for profiler-enabled builds: consumers can attribute operations to core UI elements and identify supported non-UI-to-UI relationships without relying only on activity nesting. No public API or user-facing behavior change is intended; retail event schemas remain unchanged. ## Regression Potential <!-- Could this change cause regressions? What areas might be affected? --> - [x] Low risk — isolated change, limited scope - [ ] Medium risk — touches shared components or public APIs - [ ] High risk — architectural or breaking API change Primary risks are profiler event attribution/pairing, conditional payload interpretation, and PCH include coverage. The instrumentation is compile-time gated; existing retail emissions are preserved. ## How Has This Been Tested? <!-- Describe how you validated your changes. --> - [x] I have performed a self-review of my own code - [ ] I have added tests to cover my changes - [ ] Existing tests pass locally Validation for head `9ccaabc00dd653e675647afe21ec29f7bd1fbce7`: | Command | Result | |---|---| | `.\initrun.ps1 msb /q "dxaml\xcp\dxaml\dllsrv\winrt\native\Microsoft.ui.xaml.vcxproj"` | Passed: current amd64chk/Debug runtime build, 0 warnings and 0 errors. | | `git --no-pager diff --check origin/main...HEAD` | Passed. | | `gh pr checks 12179 --repo microsoft/microsoft-ui-xaml` | Passed: all reported GitHub checks were green when this description was updated. This is not a claim that the full local test suite ran. | Earlier development validation used FolderTreeAppCpp and an ETW capture to confirm that container-operation identities matched visual-tree entries, Hyperlink focus records linked to the containing TextBlock, and UI-to-composition-to-WUC identities could be joined. That capture predates the final field-name/control-field cleanup and is correlation evidence, not a capture of the current payload schema. Not run for this update: the full TAEF/API/interaction suites, a fresh ETW capture of the final `ElementId` schema, or a new local Release build. No claim is made that every listed event was exercised. ## Screenshots (if appropriate) <!-- If you are making visual changes, include before/after screenshots. --> Not applicable; this PR changes diagnostic instrumentation, not UI appearance. |
||
|
|
1f66475147 |
TableView TiP: Copilot review fixes (fault attribution, contained faults)
- PrepareCellForEdit tags only the calls that run a CellEditingTemplate's content (the focus calls and the UpdateLayout that realizes it), and BeginEdit reads the tag, so TableView's own failure in the hook fails the test. IsAppEditor is removed. - Each shaping verb takes its own contained-fault tally on every exit, and a verb a delegate starts inside another hands the outer tally back. - A custom-comparer sort reports its contained comparer faults from the adapter it ranked with, however the sort ends. - An unload while the first-row layout callback is still armed closes Initialize as unloaded_before_render. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 53d996ee-1cb6-468a-bfc9-e11af03b1f80 |
||
|
|
f980ab26e3 |
Improve diagnostics for invalid ItemsSource values (#12335)
## Improves error reporting in #11248 ## PR Type - [ ] Bugfix - [ ] Feature - [ ] Code style update (formatting, renaming) - [ ] Refactoring (no functional changes, no API changes) - [ ] Build related changes - [ ] Documentation content changes - [x] Other (please describe): **Improved error messaging** ## Description Replace the bare `E_INVALIDARG` in `ItemsControl::OnItemsSourceChanged` with a descriptive, parameterized error identifying the property and required collection interfaces. ### Current Behavior When an ItemsSource value cannot be converted to a supported collection interface, ItemsControl returns a bare E_INVALIDARG. This can surface as “Value does not fall within the expected range,” without identifying the property or explaining its requirements. This is particularly difficult to diagnose when a .NET Native AOT application lacks the generated collection interop support described in #11248. ### New Behavior Originate a descriptive error after all existing collection-conversion attempts have failed: > _The value of 'ItemsControl.ItemsSource' must be null or implement IBindableIterable or IIterable<IInspectable>._ The message uses a reusable, parameterized error resource. The HRESULT remains E_INVALIDARG, and collection conversion—including the CLR tracker-wrapper fallback—remains unchanged. The diagnostic describes the actual interface requirement without assuming that every failure is caused by Native AOT configuration. This change improves error reporting; it does not generate missing interop support. ## Customer Impact Developers receive an actionable diagnostic identifying the failing property and required interfaces. Valid ItemsSource assignments and end-user UI behavior are unchanged. ## Regression Potential <!-- Could this change cause regressions? What areas might be affected? --> - [x] Low risk — isolated change, limited scope - [ ] Medium risk — touches shared components or public APIs - [ ] High risk — architectural or breaking API change ## How Has This Been Tested? <!-- Describe how you validated your changes. --> - [x] I have performed a self-review of my own code - [x] I have added tests to cover my changes - [ ] Existing tests pass locally Added regression coverage across ItemsControl, ComboBox, and ListView for: - Invalid sources: exception type, E_INVALIDARG, and the explanatory message. - Valid sources: empty collections, collection updates, and clearing ItemsSource with null. Validation commands and results: - `.\init.ps1 amd64chk` — passed. - `.\initrun.ps1 msb /q "dxaml\xcp\dxaml\dllsrv\winrt\native\Microsoft.ui.xaml.vcxproj"` — passed with zero warnings and errors. - `.\initrun.ps1 msb /q "dxaml\test\native\external\controls\Microsoft.UI.Xaml.Tests.External.Controls.vcxproj" /p:BuildProjectReferences=false` — passed with zero warnings and errors. Runtime test results are pending; no passing runtime-test result is claimed. ## Screenshots (if appropriate) Not applicable; no visual changes. --------- Co-authored-by: Mike Crider <45052236+codendone@users.noreply.github.com> |
||
|
|
ebf444c106 |
Convert even more tests from UAP hosting mode to WPF hosting. (#12229)
Reduce the number of tests running in HostingMode UAP from 275 to 129. ## Fixes This change is progress on #11907. ## PR Type - [x] Other (please describe): * Switch more tests to run in the supported islands mode. ## Description This change focuses on converting as many tests as possible which failed mostly or entirely due to just needing updated visual masters for WPF mode. Additional details: - Updates test setup, sizing, input handling, cleanup, and visual masters to account for XAML Islands hosting differences and improve reliability. - Several tests were changed for more deterministic teardown to prevent retained objects, final-release queue failures, and state leaking between tests. - The converted managed tests weren't actually running in UAP mode, since the test infra doesn't support that. Updated test masters for these tests tend to have many updated lines due to the masters being very stale. ## How Has This Been Tested? - [x] I have performed a self-review of my own code - [x] Existing tests pass locally |
||
|
|
0df0a8869e |
Fix WebView2 callback ownership during dispatcher shutdown (#12361)
## Issue Destructor of WebView2 is not getting called in WinUI3 ## Fixes Internal Issue: https://task.ms/60990130 ## PR Type <!-- Check the type of change. Limit each PR to one type when possible. --> - [x] Bugfix - [ ] Feature - [ ] Code style update (formatting, renaming) - [ ] Refactoring (no functional changes, no API changes) - [ ] Build related changes - [ ] Documentation content changes - [ ] Other (please describe): ## Description Few operations like Visibility update (UpdateCoreWebViewVisibility) and text scale update captured a strong WebView2 reference in a delayed action retained by a thread-pool timer. If that timer outlived UI dispatcher shutdown, releasing the capture could trigger the control’s final release on a worker thread. The framework then attempted to enqueue native deletion onto the stopped dispatcher; the rejected enqueue left the native control undestroyed. <!-- Could this change cause regressions? What areas might be affected? --> - [x] Low risk — isolated change, limited scope - [ ] Medium risk — touches shared components or public APIs - [ ] High risk — architectural or breaking API change ## How Has This Been Tested? Two Windows App SDK self-contained deployments used the same C++ sample executable and identical runtime payloads except for the baseline/fixed controls DLL. Native debugging held the actual visibility-timer callbacks until the UI loop returned, deterministically exercising the shutdown race without changing product code. - [x] I have performed a self-review of my own code - [ ] I have added tests to cover my changes - [ ] Existing tests pass locally ## Screenshots (if appropriate) <!-- If you are making visual changes, include before/after screenshots. --> |
||
|
|
c8872f79fd |
ResourceResolver::GetParentForResourceLookup: include ordinary flyouts
Remove the template-only gate while preserving mentor lookup and cycle detection. Use the existing app test root for cyclic-owner coverage and add ordinary-flyout resource and cycle regressions with event-based close synchronization. Local validation: 190 AppPopup assertions and 14 targeted TAEF tests pass. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> |
||
|
|
42b575bc75 |
Merge main into user/sahjaiswal/tableview-tip-followup
Brings in #12307 (TableView: templated column headers, UIA resize, sort fixes, recycle perf, theming) and 11 other commits from main. Conflicts, resolved by keeping both sides: - TableView_Editing.cpp (BeginEdit): the TiP try/catch now wraps #12307's unwind-on-throw guard and ResetPendingBeginEdit. - TableView_Sort.cpp: the typed Sorted rethrow and failure capture keep #12307's explicitClear, keptSourceSort and reconcile changes, and the comparer tally reset follows its early return for a source another table owns. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 53d996ee-1cb6-468a-bfc9-e11af03b1f80 |
||
|
|
d5132fd2e4 |
TableView: templated column headers, UIA resize, sort fixes, recycle perf, theming (#12307)
## Fixes | Internal issue | Type | Change | |---|---|---| | https://task.ms/64475625 | Bug | Column headers are a templated, restylable `TableViewColumnHeader` with visual states | | https://task.ms/64475623 | Bug | Header typography comes from the header Style, not C++ local values | | https://task.ms/64143451 | Bug | Header column separators are always drawn (no new property); hide them by overriding `TabularSurfaceHeaderSeparatorBrush` | | https://task.ms/63996584 | Task | Column headers expose the UIA Transform pattern so UIA clients can resize columns | | https://task.ms/64475611 | Bug | Outer border uses new `TabularSurfaceOuterBorderBrush` (`CardStrokeColorDefault`); grid-line values were in #12349 | | https://task.ms/64475629 | Bug | Default group header uses theme tokens (SemiBold key style, 8px spacing); the item count inherits the header `Foreground` | | https://task.ms/64652961 | Task | Row recycling no longer regenerates cells or refreshes row chrome twice (scroll performance) | | https://task.ms/63790855 | Task | Sort: `CanSort` is enforced and `SortByColumn(column, None)` always clears; a sort through a source another table owns returns false; no spurious `Sorted`; an app-owned sort survives a `CanSort`/`CanUserSortColumns` opt-out | | https://task.ms/63787021 | Task | Cell automation-peer constructor validates its arguments; `BeginEdit` rolls back when it throws | Not changed: https://task.ms/64512732 (by design: Auto columns follow content width), https://task.ms/64475627 (design decision: keep chevron sort glyphs), https://task.ms/64513223 (investigation only; fix deferred). ## PR Type - [x] Bugfix - [x] Feature - [ ] Code style update (formatting, renaming) - [ ] Refactoring (no functional changes, no API changes) - [ ] Build related changes - [x] Documentation content changes - [ ] Other (please describe): ## Description Follow-up to #12349 (merged) and #12285. Column headers move from code-built Grids to a new templated `TableViewColumnHeader`, restylable through `TableView.ColumnHeaderStyle` or `TableViewColumn.HeaderStyle`. Header peers gain the UIA Transform pattern for column resize. Sort behavior is tightened around `CanSort`, opt-outs and shared `TableViewSource` ownership. Row recycling reuses cells. Outer border, header lines and the default group header follow Fluent tokens. Review-driven fixes included here: - The header Style's `TargetType` `TypeName` is kept alive while it is compared; a Style with only `BasedOn` typed is applied, and its setters are read before applying it seals the Style. Incompatible, untyped or cyclic `BasedOn` Styles are ignored with a debug diagnostic. - A header disabled by its Style reports `IsEnabled=false` and is not invokable through UIA. - UIA `Transform.Resize` converts physical pixels through the table's full visual scale (not only `RasterizationScale`), and a width change below the drag jitter deadband is written once, exactly. - Disabling a header in its High Contrast hover/pressed state keeps the disabled foreground (state order fixed); the disabled sort glyph follows the text brush across theme changes. - A `CanSort` opt-out made inside `BeginningEdit` is replayed after the edit it let open closes, and rebuilds headers even when a `Sorted` handler throws. - Header rebuilds caused by `Content`/`Header` or `Columns` changes keep focus in the header band, respect an app's own focus move, and release `UIElement` headers back to the app. - A disabled group header does not dim its item count twice. The count opacity follows `ContentTemplate` changes while the header is disabled. <details><summary>Product changes by file</summary> | File | +/- | What changed | Items | |---|---|---|---| | `TableView/TableView.cpp` (+ .h) | +684 / −382 | `RebuildHeaders` creates `TableViewColumnHeader` controls; applies `ColumnHeaderStyle`/`HeaderStyle` with TargetType/BasedOn checks; density MinHeight/Padding only when the Style doesn't set them<br>`ReleaseHeaderCells` and header-focus handling for removed/replaced columns, re-entrant rebuilds and app focus moves<br>Header bottom rule thickness set on `PART_HeaderRow`; header lines no longer follow `GridLinesVisibility`<br>Row prepare/clear refresh chrome once; `TryResizeColumnFromAutomation` writes small widths exactly | 64475625, 64475623, 64143451, 64652961, 63996584, 63790855 | | `TableView/TableViewColumnHeader.cpp` (+ .h; replaces `TableViewHeaderCell.h`) | +435 / −210 | New sealed templated `ContentControl` with read-only `Column`<br>Resolves `PART_ContentPresenter`, `PART_GridLine`, `PART_SortIndicatorHost`, `PART_ResizeGripperHost`; hosts the sort glyph and gripper<br>Drives `CommonStates`/`HighContrastStates`/`SortStates`; disabled sort glyph bound to the text brush | 64475625, 64475623 | | `TableView/TableViewRow.cpp` (+ .h) | +191 / −35 | Recycled rows keep their cells unless cell automation peers exist; chrome refreshed once; tooltips re-applied on recycle<br>`BeginCellEdit` restores display element, edit state and focus on throw | 64652961, 63787021 | | `TableView/TableView.xaml` | +128 / −18 | Default `TableViewColumnHeader` Style/template: typography setters, visual states, parts<br>Outer border uses `TabularSurfaceOuterBorderBrush`; header lines use `TabularSurfaceHeaderSeparatorBrush`<br>Group header: `TableViewGroupExpanderSpacing` (8), padding 8,8,12,8, key/count text styles, count inherits `Foreground` | 64475625, 64475623, 64475611, 64475629 | | `TableView/TableViewColumnHeaderAutomationPeer.cpp` (+ .h) | +127 / −9 | `ITransformProvider`: `Resize` converts physical px through the visual scale; `CanResize` tracks gripper availability (pinned → false)<br>`IsEnabled` follows the header; name read from the content presenter | 63996584, 64475625 | | `TableView/TableView_Sort.cpp` | +113 / −39 | `SortByColumn(None)` bypasses the `CanSort` gate; other sorts refused<br>Sort through a source owned by another table returns false; implicit clears keep the app's sort and raise no `Sorted`; app-lit columns left alone on opt-out | 63790855 | | `TableView/TableView_Layout.cpp` | +74 / −14 | Realized-row walk only when a visible Auto column exists<br>Transform `CanResize` changes raised for existing header peers | 64652961, 63996584 | | `TableView/TableView_Editing.cpp` | +63 / −15 | `BeginEdit` unwinds on throw; `CanSort` opt-out during `BeginningEdit` queued until the edit closes | 63787021, 63790855 | | `TableView/TableViewCellAutomationPeer.cpp` (+ .h, `TableViewCell.h`) | +48 / −0 | Public constructor throws `E_INVALIDARG` for null args, negative/out-of-range index or a foreign column; internal path uses `TrustedOwnerTag` | 63787021 | | `TableView/TableViewGroupHeader.cpp` (+ .h) | +39 / −0 | Default count's opacity is not applied twice when disabled | 64475629 | | `TableView/TableView.idl` | +31 / −13 | `TableViewColumnHeader`, `TableView.ColumnHeaderStyle`, `TableViewColumn.HeaderStyle`<br>`CanSort`/`SortByColumn`/`ToggleSortDirection`/`GridLinesVisibility` docs; cell-peer `E_INVALIDARG`; group-header parts | 64475625, 63790855, 64143451, 63787021 | | `TableView/TableView_Columns.cpp` | +19 / −0 | Releases removed columns' header cells; old `UIElement` `Header` returned to the app on change | 64475625 | | `TableView/TableViewColumn.cpp` | +7 / −0 | `HeaderStyle` change queues a header rebuild | 64475625 | | `TableView/TableViewSource.h` | +6 / −0 | `IsOwnedByOther` helper for sort sync | 63790855 | | `CommonStyles/TabularSurfaces_themeresources.xaml` | +5 / −2 | New `TabularSurfaceOuterBorderBrush` (Dark/Light/HC); group count opacity documented as secondary-over-primary text (0.77 Dark) | 64475611, 64475629 | | `TableView/TableView.vcxitems` | +2 / −1 | Swap `TableViewHeaderCell.h` for `TableViewColumnHeader.*` | 64475625 | | Generated / metadata (`Generated/TableView.properties.*`, `Generated/TableViewColumn.properties.*`, `dll-tabular/XamlMetadataProviderGenerated.h`) | +123 / −0 | Codegen for the new properties and type | 64475625 | **Total: 17 rows, 29 product files, +2117 / −738.** Also: 19 test files (+3718 / −32), 3 docs (+161 / −35), 6 sample files (+2 / −9). </details> ### New preview API (needs API review) - **`TableViewColumnHeader`**: sealed, templated `ContentControl`, created only by `TableView`. - Read-only `Column` property. - Template parts (optional): `PART_ContentPresenter`, `PART_GridLine`, `PART_SortIndicatorHost`, `PART_ResizeGripperHost`. - Visual state groups: `CommonStates`, `HighContrastStates`, `SortStates`. - **`TableView.ColumnHeaderStyle`**: Style applied to every column header. - **`TableViewColumn.HeaderStyle`**: per-column Style; wins over `TableView.ColumnHeaderStyle`. There is no `HeaderGridLinesVisibility` property. `GridLinesVisibility` affects body lines only; header lines are always drawn. To hide them, override `TabularSurfaceHeaderSeparatorBrush` with `Transparent`. ### Current Behavior - Headers are built in C++ with no template, no visual states and forced local typography, so apps can't restyle them. Header separators don't show. - Header peers report `HeaderItem` without Transform, so UIA clients can't resize columns. - `SortByColumn` ignores `CanSort`; `CanSort=false` on a sorted column leaves the sort in place; sorting through a source another table owns reports success; some implicit clears raise `Sorted(None)` while the app's sort remains. - The cell peer constructor accepts invalid arguments; a throw in `BeginEdit` leaves edit state half-open. - Recycling a row rebuilt every cell, refreshed chrome up to twice and invalidated the table's measure. - The outer border has no dedicated brush; the default group header uses raw weights and off-grid spacing. ### New Behavior - Headers use the templated `TableViewColumnHeader` (see API above); typography, padding and brushes come from Style setters. - Header peers implement `ITransformProvider` through the existing resize path (Min/Max clamping, scale conversion; pinned columns report `CanResize=false`). - `CanSort=false` blocks header clicks, `SortByColumn` (except `None`) and `ToggleSortDirection`, and clears the column's own active sort; sorts through a source owned by another table return false; `Sorted` is raised only when the table's sort state changes. - Cell peer constructor throws `E_INVALIDARG` on bad input; `BeginEdit` rolls back on failure. - Recycled rows reuse their cells unless cell automation peers exist; chrome refreshes once. > **Visible changes** > - Outer border uses `CardStrokeColorDefault`. > - Header lines are always shown, even when `GridLinesVisibility` is `None` or `Horizontal`. > - Group header restyled: 8px spacing; the count follows the header `Foreground`. > - Disabled header text and sort glyph dim. > - TableView samples no longer opt out of High Contrast backplates app-wide. ## Customer Impact Restylable column headers, visible header separators, Fluent-aligned outer border and group headers, UIA column resize for assistive technology, smoother scrolling and predictable sort events. ## Regression Potential - [ ] Low risk — isolated change, limited scope - [x] Medium risk — touches shared components or public APIs - [ ] High risk — architectural or breaking API change Header construction (focus restore, frozen columns, UIA peer, tooltips, resize, sort glyph) moved to a templated control. Sort event semantics are tighter: `CanSort` is enforced on programmatic sorts and fewer `Sorted` events fire. Outer border color and always-on header lines change visibly. Covered by new and existing tests. ## How Has This Been Tested? - [x] I have performed a self-review of my own code - [x] I have added tests to cover my changes - [x] Existing tests pass locally Full TableView API and interaction suites on test VMs: 877/882 pass at 224d10d5c; 3 Localization tests are [Ignore] and 2 intermittent failures passed 3/3 on rerun. Product, test apps and TableView sample apps build (x64 chk). Also covered: `VerifyDisabledGroupHeaderCountFollowsContentTemplateChanges`. Key new tests: - `VerifyColumnHeaderTemplateParts`, `VerifyColumnHeaderSortStates`, `VerifyColumnHeaderTypographyHasNoLocalValues` - `VerifyColumnHeaderStyleApplies`, `VerifyBasedOnOnlyColumnHeaderStyleApplies`, `VerifyIncompatibleColumnHeaderStyleIsIgnored` - `VerifyHeaderGridLinesShowWhateverTheBodyGridLines`, `VerifyTransparentHeaderSeparatorBrushHidesHeaderLines`, `VerifyGridLineAndOuterBorderBrushesMatchFluentStrokeTokens` - `VerifyDisablingHighContrastHoveredHeaderKeepsDisabledForeground`, `VerifyDefaultGroupHeaderCountInheritsForegroundThatFollowsStates` - `VerifyTransformResizeAccountsForAncestorScale`, `VerifyTransformResizeSmallWidthChangeWritesOnce`, `VerifyHeaderDisabledByStyleIsNotEnabledOrInvokable` - `VerifySortByColumnRespectsCanSort`, `VerifyImplicitClearKeepsSourceSort`, `VerifyUngatingKeepsAppLitColumnWithoutEvents`, `VerifySortOptOutDuringBeginningEditKeepsTheEditOpen` - `VerifyRecycledRowWithoutCellPeersReusesItsCells`, `VerifyHeaderFocusStaysInHeaderBandWhenColumnsReplaced`, `VerifyBeginEditThatThrowsRollsBackAndAllowsALaterEdit`, `VerifyCellPeerConstructorValidatesArguments` --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 94961bb8-d187-483c-bece-19e203752c67 |
||
|
|
42d204698a |
Add code-coverage-calculation skill: component code coverage from a local VM test run (#12254)
## Fixes No linked issue. This adds agent tooling only and doesn't change product code or test code. ## PR Type - [ ] Bugfix - [x] Feature - [ ] Code style update (formatting, renaming) - [ ] Refactoring (no functional changes, no API changes) - [ ] Build related changes - [ ] Documentation content changes - [x] Other (please describe): new experimental agent skill under `.github/skills` ## Description Adds the experimental `code-coverage-calculation` skill to `.github/skills/`, where Copilot CLI and agency discover skills automatically (the same place as `build` and `test-on-vm`). It measures code coverage of one WinUI control or component using only that component's tests, run on a local Hyper-V VM against the local build. The report numbers match the ADO Code Coverage tab. ### Current Behavior Getting coverage for a single control needs a coverage pipeline run with all tests, or manual instrumentation and collector setup. Agents can't do it from a prompt. ### New Behavior In this repo, the prompt `calculate coverage of control <name>` makes the agent: 1. **Check prerequisites:** a local build, VS Enterprise code coverage, `dotnet-coverage`, and Hyper-V rights. 2. **Gate on VM details (strict):** before every run, the agent asks the user to confirm a cached VM and username or enter new details. It never picks one silently. The password is typed in a separate window, never in chat, and stored in the same encrypted cache `tools\run-tests-on-vm.ps1` uses. The shared `tools\Set-VmCredential.ps1` then checks the logon and that the user has an active desktop session, and only then replaces the cached credential. 3. **Find the component and its tests:** - `controls\dev\<Control>` (MUXC): it finds the folder by name or by its `.idl` class, the `APITests` and `InteractionTests` classes, the test filter(s), and the source-path regex. - Not under `controls\dev`: it investigates `dxaml` (MUX) sources and tests and **needs the user's approval** before running. - Found in neither place: it asks the user. 4. **Run:** it relinks MUXC with `/DEBUGTYPE:CV,FIXUP` outside `BuildOutput`, instruments it (cached), runs the collector and tests on the VM, then always restores the payload and the VM copies with hash checks. 5. **Report:** per-file and per-function coverage, deduplicated by (file, line) to match ADO, plus an HTML tree, written under `BuildOutput\Coverage\<flavor>\<Component>` by default (git-ignored). The skill is self-contained in this repo: everything it needs is in `.github/skills/code-coverage-calculation/` plus the shared `tools\Set-VmCredential.ps1`, and it doesn't refer to other repos, PRs, builds or skills. Supported modules: `Microsoft.UI.Xaml.Controls.dll`, `Microsoft.UI.Xaml.Controls.Tabular.dll` (TableView and the tabular helpers) and `Microsoft.ui.xaml.dll`. Before every run the agent tells the user what it changes on the VM, including the coverage pipe it opens to all local users. `-TestFilter` accepts several filters, each run with its own collector and merged in the report. ## Customer Impact None for end users. This is developer and agent tooling only. ## Regression Potential - [x] Low risk — isolated change, limited scope The PR only adds new files under `.github/skills/code-coverage-calculation/` and `tools\Set-VmCredential.ps1`. During a run the skill changes only the generated `TestPayload\<flavor>` folder and the VM's deployed payload, and restores both with hash checks. The restore state is written before anything is overwritten, so an interrupted run is restored at the start of the next one, and a failed restore fails the command. ## How Has This Been Tested? - End-to-end run on TitleBar before this PR (`*.TitleBarTests.*`, 24/24 passed, TitleBar sources at 81 % line coverage); payload, VM and `BuildOutput` were checked back at their original hashes afterwards. - All scripts parse without errors. Review fixes only changed failure and cleanup paths: Step 1 and all `lib` scripts are code-identical to the version used in that run (comments only). - Step 5 old vs new on a real 88 MB `.coverage` file: identical per-file, per-module and per-function CSVs and summary. Path keys now also join for checkouts under folders like `C:\src`, and the function list now covers components whose sources sit in subfolders (PullToRefresh: 0 to 120 functions). - Failure paths tested with mocked VM calls and a fake coverage tool. A Step 3 failure keeps its error, downloads the collector log and closes the session. A Step 4 VM mismatch fails and keeps the state. Step 2 restores a rebuilt payload to the new build, not the old one, and re-instruments after a tool version change. The orchestrator stops the transcript when Step 3 fails. - The orchestrator was tested with stubbed steps: one filter gives the same call sequence and run label as before, and two filters run Step 3 twice and pass both `.coverage` files to Step 5. - `Set-VmCredential.ps1` rejects `VM\user` names and unknown VMs. Against a real VM with a cached credential, it confirmed the logon and an active console session, without prompting and without changing the cache. - A full coverage run has not been repeated from the repo copy. - End-to-end run of the skill from this branch on TitleBar (`*.TitleBarTests.*`, fresh `amd64chk` build of this branch, Hyper-V VM): 32/32 tests passed, TitleBar 835/954 lines (87.53 %), `TitleBar.cpp` 99.03 %. Payload and VM were restored to the original DLLs afterwards. That run found the stale incremental-deploy bug fixed in the last commit: reruns tested the original DLL or deployed the instrumented DLL without `static_covrun64.dll`. The fix removes exactly those 16 manifest entries. Rejecting a stale build was tested against an older build (TitleBar: 0 matches, stops before instrumenting; TeachingTip: 84 matches, runs). - Second review round (`c0ef2a400`), checked with stub and mock tests before and after the change: - Step 2 fails on the 3rd of 4 copies: before the change, 2 copies stayed instrumented with no state file; now Step 4 restores all 4. - A failed `-RefreshPayload` or incomplete payload now stops the run; a `te.exe` error is reported as a listing failure, not as 0 matches; IXMP appx tests are now accepted by the check. - A restore failure now fails the command after the report is written; when Step 3 also failed, Step 3's error is still the one reported. A leftover state file is restored before the next run. - A baseline taken from `D:\src\baseline-repo` matched 0 of 15 TitleBar files before the change and 15 of 15 after; an unrelated baseline now shows a warning. Results without a baseline are unchanged on real TitleBar coverage. - `tools\Set-VmCredential.ps1` keeps the old cache when the desktop isn't active. - End-to-end VM run of the Tabular module from this branch: `-Module Microsoft.UI.Xaml.Controls.Tabular.dll -TestFilter '*.TableViewSortingTests.*'`, 35/35 passed in 3 min 41 s, TableView 3557/11779 lines (30.2 %) from that one test class. Payload and VM were restored afterwards, and the output stays git-ignored. - [x] I have performed a self-review of my own code - [ ] I have added tests to cover my changes - [ ] Existing tests pass locally |
||
|
|
1c9adcadd9 |
NavigationView: respect IsPaneOpen=false set before PaneDisplayMode=Left on launch (#7609) (#12325)
## Fixes
Fixes #7609
## PR Type
- [x] Bugfix
## Description
Setting `IsPaneOpen="False"` **before** `PaneDisplayMode="Left"` (XAML
attribute order, or code before the control is loaded) launched the
NavigationView with the pane open and `IsPaneOpen == true`. The reverse
order worked, so the result depended on property order.
Root cause: the `PaneDisplayMode` branch of
`NavigationView::OnPropertyChanged` unconditionally reset
`m_wasForceClosed`. Before the template is applied, that flag can only
be `true` because the app explicitly set `IsPaneOpen=false` (the
pre-template path added in #3197 for #2391). Clearing it made the first
`UpdateAdaptiveLayout` (Expanded) call `OpenPane()`.
Fix: only reset `m_wasForceClosed` on a `PaneDisplayMode` change once
the template has been applied at least once. A new never-reset
`m_templateAppliedOnce` is used rather than `m_appliedTemplate`, because
`m_appliedTemplate` is also false while a template is re-applied, and an
app's custom `GoToStateCore` can run in that window. That keeps
re-templating identical to main.
### Current Behavior
`<NavigationView IsPaneOpen="False" PaneDisplayMode="Left"/>` launches
with the pane open and `IsPaneOpen` becomes `true`.
### New Behavior
The pane launches closed, the same as `PaneDisplayMode="Left"
IsPaneOpen="False"` already did (matching the launch table in #3197).
Runtime `PaneDisplayMode` switches on a loaded control are unchanged:
for example, LeftMinimal -> Left still opens the pane, as designed
(#1702/#1787).
## Customer Impact
Apps that declare a collapsed Left pane in XAML get the state they asked
for without an `OnLoaded` workaround.
## Regression Potential
- [x] Low risk: isolated change, limited scope
The change only takes effect for a `PaneDisplayMode` change that happens
before the first `OnApplyTemplate` while the app has already set
`IsPaneOpen=false`. Post-template behavior is unchanged. This doesn't
conflict with #12315, which touches
`UpdateAdaptiveLayout`/`UpdateIsClosedCompact`.
## How Has This Been Tested?
- [x] I have performed a self-review of my own code
- [x] I have added tests to cover my changes
- [x] Existing tests pass (no failures beyond the known failures on
`origin/main`
|
||
|
|
927f474d48 |
TableView TiP: blame by exception type and count contained app faults
Review round 3. Attribution matched the last app HRESULT against a control-wide counter, so a nested callback could overwrite it and an unrelated exception with the same HRESULT could match. App code's exceptions are now tagged where TableView calls it (CallerError, and CallerOutOfMemory for an allocation failure, which stays a std::bad_alloc), and the catch that decides an outcome reads the type. SelectionModel calls TableView back across the ABI, which drops the C++ type, so the call into it tags the exception again by its error object. Column code is TableView's unless app code ran it: the editing hooks are internal, so only a CellEditingTemplate's content (while the editor is prepared) is the app's there, and only an app-derived column class owns its display factory and sort member path. Expansion is always tracked and its catch reads the tag; only a request that changes one group or node runs a test. A filter over a large source no longer drops into the small bucket: the large flag counts the source before the filter, and the metrics and TableView_Instance carry both counts. Initialize closes as unloaded_before_render when the table is unloaded with rows and a body viewport before any row was laid out. An app filter predicate, sort or group key selector or custom comparer that throws is still contained, and now writes one recoverable TableView_Error per operation with FaultCount. Internal pipeline builds fail when Tabular falls back to the TiP stub. All versions start at 1, since none has shipped. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 53d996ee-1cb6-468a-bfc9-e11af03b1f80 |
||
|
|
a7483e3aed |
Merge main into user/sahjaiswal/tableview-tip-followup
Brings in #12349 (TableView low-risk fixes) and 35 other commits from main. No conflicts. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 53d996ee-1cb6-468a-bfc9-e11af03b1f80 |
||
|
|
51dbbd540b |
Synchronize staggered entrance transition visibility (#12196)
## Fixes <!-- Link either the public GitHub issue or the internal issue this PR addresses. Remove the unused line. --> <!-- PRs without a linked issue may be closed unless they are minor documentation/typo fixes. --> Fixes [#8879](https://github.com/microsoft/microsoft-ui-xaml/issues/8879) ## PR Type <!-- Check the type of change. Limit each PR to one type when possible. --> - [x] Bugfix - [ ] Feature - [ ] Code style update (formatting, renaming) - [ ] Refactoring (no functional changes, no API changes) - [ ] Build related changes - [ ] Documentation content changes - [ ] Other (please describe): ## Description <!-- Describe your changes in detail. --> ### Current Behavior <!-- How does this work today? --> For staggered load transitions, `CTransition::SetupTransition` initially hides the layout transition element and injects a discrete, time-zero opacity keyframe to reveal it after the stagger delay. This animation has zero natural duration, which prevents its conversion to a compositor animation. The reveal therefore depends on a UI-thread tick, while the entrance fade and translation run independently on the compositor. These timelines can diverge: content can become visible partway through its motion, or be revealed before the compositor motion is attached. This is a candidate explanation for the intermittent entrance artifacts reported in #8879, not yet proof that it accounts for every stutter in the issue. ### New Behavior <!-- How will it work after this PR? --> Expand dynamic storyboard children before resolving duration and give the injected reveal the transition storyboard's natural duration. This allows the reveal to run on the compositor alongside the motion, retaining the existing discrete time-zero keyframe and stagger delay without intentionally changing the overall transition duration. Add `ValidateStaggeredEntranceVisibilityTiming` to check the reveal's explicit positive duration, discrete time-zero keyframe, and agreement with the motion duration, across repeated loads with staggering enabled and disabled. The test also verifies MockDComp's compositor tree after a staggered reload, including the three attached local-opacity keyframe animations and their stagger delays. Changes are split into the runtime fix (`591dbdae9`) and regression coverage (`5b30aa458`). ## Customer Impact <!-- How does this change affect end users? Is it user-facing or infra changes? --> User-facing: staggered entrance reveals should remain synchronized with compositor motion when the UI thread is delayed. No public API changes. An out-of-tree reproduction restores the issue's Windows App SDK 1.4.230822000 baseline, original row layout, and two-page navigation flow. A default-off timing experiment deliberately pauses the UI thread for 250 ms after rendering callbacks and a compositor commit. In captured runs, the historical runtime abruptly revealed delayed rows together, while the patched branch continued revealing them progressively. The historical and branch apps have different SDK/.NET dependencies, so this comparison supports the timing mechanism but is not an otherwise-identical pre/post-patch experiment. Reliable reproduction of the exact unstressed issue video remains unconfirmed. Very late compositor attachment is also a remaining limitation. ## Regression Potential <!-- Could this change cause regressions? What areas might be affected? --> - [ ] Low risk — isolated change, limited scope - [x] Medium risk — touches shared components or public APIs - [ ] High risk — architectural or breaking API change The duration change is limited to the injected visibility animation for delayed load transitions, but dynamic timeline expansion moves earlier in shared transition setup. Other theme transitions and staggered/non-staggered loads warrant coverage. Existing keyframes, delays, and intended completion timing are preserved. ## How Has This Been Tested? <!-- Describe how you validated your changes. --> - [x] I have performed a self-review of my own code - [x] I have added tests to cover my changes - [x] Existing tests pass locally Local native tests ran in the WPF host using Debug x64 binaries, including the test-fixture correction in `cf54f1b7d` and the compositor coverage in `e5696b8e5`. The fixture retains the Rectangle managed peers through deferred storyboard creation and checks the captured storyboards after the synchronous ticks, so verification failures report cleanly rather than unwinding through the runtime callback. The original frozen clock, two ticks, and staggering/reload coverage are preserved. Commands below were run from the assembled `TestPayload\x64chk` directory in an elevated shell: **Regression repetition: passed 20/20 runs.** Each run covers two fresh loads with staggering disabled and two with staggering enabled. This was rerun successfully with the compositor snapshot check enabled. ```powershell .\TE.exe Test\Microsoft.UI.Xaml.Tests.External.Foundation.dll /select:"@Name='*ThemeTransitionTests::ValidateStaggeredEntranceVisibilityTiming'" /p:HostingMode=WPF /p:SkipConsoleWindowMinimize /testtimeout:0:02 /unicodeOutput:false /reportLoadingIssue /testmode:loop /looptest:20 ``` **Related transition tests: passed 24/24 runs** (eight tests, each repeated three times), including `ValidateStaggeringWorks`, the new regression, the ItemsControl timing helper, and the five ItemsControl storyboard tests. This was also rerun successfully after adding the compositor coverage. ```powershell .\TE.exe Test\Microsoft.UI.Xaml.Tests.External.Foundation.dll /select:"@Name='*ThemeTransitionTests::ValidateStagger*' OR @Name='*ThemeTransitionTests::ValidateItemsControl*'" /p:HostingMode=WPF /p:SkipConsoleWindowMinimize /testtimeout:0:02 /unicodeOutput:false /reportLoadingIssue /testmode:loop /looptest:3 ``` **Negative controls: failed as expected.** When only the explicit reveal-duration assignment was temporarily removed, both non-staggered loads passed and both staggered loads failed at the intended duration assertion (`TimeSpan` expected, `Automatic` actual), with the test host remaining alive. An additional negative control temporarily forced the injected LTE opacity animations to use UI-thread ticking while preserving their positive duration and keyframes. All existing duration/keyframe assertions passed, but the new compositor snapshot check failed: the three local-opacity compositor animations were absent while the entrance fade and translation animations remained attached. Both negative controls used the single-run command below. ```powershell .\TE.exe Test\Microsoft.UI.Xaml.Tests.External.Foundation.dll /select:"@Name='*ThemeTransitionTests::ValidateStaggeredEntranceVisibilityTiming'" /p:HostingMode=WPF /p:SkipConsoleWindowMinimize /testtimeout:0:02 /unicodeOutput:false /reportLoadingIssue ``` The patched runtime was restored before the successful repetition runs above. All temporary diagnostics were removed. The full test suite and other architectures/hosting modes were not run locally. ## Videos https://github.com/user-attachments/assets/68fffca2-d637-49cb-8d8a-45331d9574b6 --------- Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> |
||
|
|
5e5dc36a16 |
Publish localization resources to the OSS dependency feed (#12364)
## Fixes No linked issue. ## PR Type - [ ] Bugfix - [ ] Feature - [ ] Code style update (formatting, renaming) - [ ] Refactoring (no functional changes, no API changes) - [x] Build related changes - [ ] Documentation content changes - [ ] Other (please describe): ## Description Make newly generated localization resource packages available to OSS builds by publishing them to the OSS dependency feed before proposing localization updates. ### Current Behavior The localization pipeline does not publish its generated resource package to the OSS dependency feed. A localization update can therefore reference a package version that OSS builds cannot restore. ### New Behavior Publish the same generated package and version to the shine-oss `WinUI-Dependencies` feed, using the existing cross-organization authentication pattern. Preserve existing publication behavior and stop before proposing localization updates if OSS publication fails. Update the localization documentation to describe dual-feed publishing and its access requirements. ## Customer Impact Enables OSS builds to restore newly published localization resources. This is a build-infrastructure change with no runtime or public API changes. ## Regression Potential - [x] Low risk — isolated change, limited scope - [ ] Medium risk — touches shared components or public APIs - [ ] High risk — architectural or breaking API change The publishing identity must have permission to publish to the OSS dependency feed. Authentication or publication failures will block localization updates rather than propose an unavailable package version. ## How Has This Been Tested? - [x] I have performed a self-review of my own code - [ ] I have added tests to cover my changes - [ ] Existing tests pass locally Local YAML and configuration checks confirm that both publication steps target the same package/version, existing steps remain unchanged, and OSS publication precedes localization updates. Live pipeline publication and OSS restore validation are pending. ## Screenshots (if appropriate) Not applicable; no visual changes. Co-authored-by: Test User <test@example.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 83aa42a4-855c-4597-bf2e-e6e9239adbc3 |
||
|
|
4bcbff6d6b |
NavigationView: fix pane toggle button clipped on first render (#12321)
## Fixes Fixes #5854 Fixes #9034 ## PR Type - [x] Bugfix - [ ] Feature - [ ] Code style update (formatting, renaming) - [ ] Refactoring (no functional changes, no API changes) - [ ] Build related changes - [ ] Documentation content changes - [ ] Other (please describe): ## Description `NavigationView::UpdatePaneToggleSize()` copies `TemplateSettings.PaneToggleButtonWidth` into `TogglePaneButton.Width`. On first load it runs from `OnApplyTemplate` (`UpdatePaneDisplayMode` → `SetDisplayMode`) **before** `OnLoaded` → `UpdatePaneButtonsWidths()` initializes that template setting, which is still at its default of 0. As a result the button gets `Width = 0` and falls back to `MinWidth` (`SmallerPaneToggleButtonWidth` = 40). Its template needs 48px (a 40px icon column plus a 4px `LayoutRoot` margin on each side), so the hover background and the hamburger glyph are clipped on the right. The width is only corrected the next time something calls `UpdatePaneToggleSize()`, for example when the pane is collapsed. Fix: `UpdatePaneButtonsWidths()` now calls `UpdatePaneToggleSize()` when `PaneToggleButtonWidth` actually changes. Two follow-ups from review: - **Effective open pane length.** With a `PaneTitle`, the toggle button is stretched to the effective open pane length. `UpdateOpenPaneLength()` (called from `SizeChanged` and from `OpenPaneLength` changes) now re-applies the width when that length changes. Before, a runtime `OpenPaneLength` change, or a NavigationView narrower than `OpenPaneLength`, left the button at its old width. - **Negative width.** In an open overlay pane with the close button shown, the button width is `OpenPaneLength - 40`, which is negative when `OpenPaneLength` < 40, and `Width(-x)` throws. `UpdatePaneToggleSize()` now clamps it to 0. ### Current Behavior A default `<NavigationView/>` whose pane starts open (`PaneDisplayMode` left at `Auto`, width ≥ `ExpandedModeThresholdWidth`) renders `TogglePaneButton` 40px wide (`Width=0`, `MinWidth=40`) instead of 48. The hover background and the glyph are cut off until the pane is toggled once. Also, changing `CompactPaneLength` at runtime doesn't resize the toggle button until the next pane toggle. ### New Behavior `TogglePaneButton` is `CompactPaneLength` (48) wide from the first frame, matching what you get after a toggle and the visual-tree masters, which already expect `Width=48`. Runtime `CompactPaneLength` changes apply immediately. ## Customer Impact Visible polish bug in the default NavigationView (hamburger hover state clipped on launch) in WinUI 2 and WinUI 3, reported since 2021. ## Regression Potential - [x] Low risk — isolated change, limited scope Small change in three functions. Each new `UpdatePaneToggleSize()` call is guarded so it only runs when its input actually changes: `PaneToggleButtonWidth` (first load, `CompactPaneLength` changes) or the effective open pane length (`OpenPaneLength` changes, and resizes narrower than `OpenPaneLength`). Neither runs on every pane open/close. The clamp only affects values that used to throw. `UpdatePaneToggleSize()` already exists, is idempotent and null-guarded. Top mode hides the toggle button. With a `PaneTitle` set, the button keeps its existing `OpenPaneLength` path. ## How Has This Been Tested? - [x] I have performed a self-review of my own code - [x] I have added tests to cover my changes - [x] Existing tests pass locally (failures match `main`; see below) New API test `NavigationViewTests.VerifyPaneToggleButtonWidthOnFirstRender`. It has 6 cases: `PaneDisplayMode` left at its default at 1200/800/500px, plus explicit `Left`, `LeftCompact` and `LeftMinimal`. Each case checks that the toggle button's `ActualWidth` equals `CompactPaneLength` on first render and again after `CompactPaneLength = 60`. New API tests (`NavigationViewTests`): - `VerifyPaneToggleButtonWidthOnFirstRender`: 6 cases (`PaneDisplayMode` left at its default at 1200/800/500px, plus explicit `Left`/`LeftCompact`/`LeftMinimal`). It checks `ActualWidth == CompactPaneLength` on first render and again after `CompactPaneLength = 60`. - `VerifyPaneToggleButtonWidthFollowsOpenPaneLengthWithPaneTitle`: `PaneTitle`, `OpenPaneLength` 250 → 280, then the NavigationView narrowed to 200. - `VerifyPaneToggleButtonWidthIsNotNegativeWithSmallOpenPaneLength`: @godlytalias's repro. Open LeftMinimal pane with the close button visible, `OpenPaneLength = 32`, then `CompactPaneLength` 24 → 28. All runs were full builds on the test VM: - **Red: main's product code + the new tests** (scratch commit 61cf071e4): all 3 fail. `AreEqual(48, 40)` (default mode, Expanded, first render); `AreEqual(60, 52)` ×5 (CompactPaneLength change); `AreEqual(200, 250)` (narrowed NavigationView); `AreEqual(0, 280)` ×2 (small OpenPaneLength / CompactPaneLength with an open overlay pane). - **Green: |
||
|
|
e8d5ddd1e4 |
ScrollViewer::OnPointerReleasedHandledEventsToo: clear stale press state
Keep handled-release cleanup and press counting inside ScrollViewer. Remove the core per-press event-state changes and preserve the existing normal focus decisions. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> |
||
|
|
c0587a63bf |
NavigationView: don't clip the LeftMinimal header when back and toggle buttons are hidden (#12316)
## Fixes Fixes #9789 ## PR Type - [x] Bugfix ## Description In `Minimal` display mode (`PaneDisplayMode="LeftMinimal"`, or `Auto` in a narrow window), `HeaderContent` uses the theme margin `NavigationViewMinimalHeaderMargin = -24,44,0,0`. The negative left value is designed to pull the header back into the button slot that `ContentLeftPadding` reserves for the pane toggle, back, or close button: 40px - 24px = a 16px inset. `UpdateBackAndCloseButtonsVisibility()` sizes `ContentLeftPadding` from the buttons that are actually visible. When the back button, the close button, *and* the pane toggle button are all hidden, the padding is 0, so the header lands at x = -24 and gets clipped. ### Current Behavior With `PaneDisplayMode="LeftMinimal"`, `IsBackButtonVisible="Collapsed"` and `IsPaneToggleButtonVisible="False"`, the header is shifted 24px past the left edge and its first characters are cut off. If either button is shown, the header looks right. ### New Behavior When the control is in Minimal mode and no back, close or pane toggle button is visible, `ContentLeftPadding` keeps one pane-toggle-button slot (`PaneToggleButtonWidth`, 40px by default). The header then has the same 16px inset it has when exactly one button is visible. Nothing else changes: when any button is visible the padding is identical to before, and non-Minimal modes don't use this padding at all. ```cpp if (useLeftPaddingForBackOrCloseButton && leftPaddingForBackOrCloseButton == 0.0) { leftPaddingForBackOrCloseButton = GetPaneToggleButtonWidth(); } ``` `UpdateBackAndCloseButtonsVisibility()` now also runs after a runtime change of `PaneDisplayMode` (so LeftMinimal→Top clears the slot) and of `CompactModeThresholdWidth`/`ExpandedModeThresholdWidth` (in `Auto` these can flip `DisplayMode` between Minimal and Compact/Expanded without a resize). Before this, the SplitView `DisplayMode` callback refreshed the padding while `DisplayMode` still held the *old* value, so the padding went stale: 40px kept in Compact, or the clipped 0 back in Minimal. Every other path that changes the display mode (resize, `OnApplyTemplate`, `IsBackButtonVisible`, `IsPaneToggleButtonVisible`, `IsPaneOpen`) already refreshed it. ## Customer Impact User-facing. Apps that hide both NavigationView buttons in LeftMinimal (or Auto when narrow) now see their whole header instead of a clipped one. ## Regression Potential - [x] Low risk: 7 lines in one function (only the "Minimal + no buttons visible" case changes), plus two one-line refresh calls on property changes that re-run the existing padding computation. Modes checked: Left, LeftCompact, Top and Auto (wide) don't use `ContentLeftPadding` (`useLeftPaddingForBackOrCloseButton` is false), so they're unchanged. LeftMinimal and Auto (narrow) change only when no button is visible. Pane open (close button shown) is unchanged. RTL mirrors the Grid column, so it's fine. Possible visible difference: an app that overrides `NavigationViewMinimalHeaderMargin` with a non-negative left value **and** hides both buttons will see its header move 40px right. That's the same offset those apps already get when one button is visible. Overlap with open PR #12258 (#5306): no conflict. That PR doesn't touch `NavigationView.cpp`, and its test asserts `ContentLeftPadding == 80` with both buttons visible, a path this PR doesn't change. Both PRs add tests to `NavigationViewTests.cs`, but in different places. ## How Has This Been Tested? - [x] I have performed a self-review of my own code - [x] I have added tests to cover my changes: `NavigationViewTests.VerifyHeaderIsNotClippedInLeftMinimalWhenBackAndPaneToggleButtonsAreHidden`. It measures `HeaderContent`'s left offset with only the toggle button visible and again with no buttons, then checks that the second offset is ≥ 0 and equal to the first. - [x] Existing tests pass (run on a test VM, x64chk; see the results below) Results: - **Red** (test-only commit `0fcb1e91f`): the new test fails with `IsGreaterThanOrEqual(-24, 0)` / `AreEqual(16, -24)`. The header is at x=-24 with no buttons and at x=16 with one button. - **Green** (fix commit `85060bb04`): the new test passes (1/1). - **Regression sweep** `*NavigationViewTests*` (API + Interaction) on the fix commit: 121 passed, 58 failed, 1 skipped. The same sweep on `origin/main` `866ba81a1`: 36 passed, 142 failed, 1 skipped. **Every one of the 58 failures also fails on `origin/main`, so there are 0 regressions.** The 5 API failures (`VerifyHeaderContentMarginOn{Minimal,Top}Nav`, `VerifyPaneDisplayModeAndDisplayModeMapping`, `VerifyVisualTree_{Old,Optimized}Styles`) are pre-existing visual-tree `RenderSize`/OS-version diffs on this VM. The Interaction-test failures are environmental and flaky there. Review follow-ups (full builds, Oct 19; compared with a shared `origin/main` `866ba81a1` baseline run on the same VM): - `VerifyHeaderPaddingUpdatesWhenSwitchingBetweenLeftMinimalAndTop` (with `2ad8d8434`): passes. - `VerifyHeaderPaddingUpdatesWhenCompactModeThresholdWidthChangesDisplayMode`: **red** on test-only `2b9aa04ed` (`AreEqual(0, 40)` in Compact, `AreEqual(40, 0)` back in Minimal), **green** on fix `e6d44591f`. - Sweep on `e6d44591f`: `*ApiTests.NavigationViewTests*` 39/44, where the 5 failures are the same 5 pre-existing failures as on main. `*NavigationViewTests.PaneBehaviorTests*` 33/34, where the 1 failure (`SelectingAnItemInLeftCompactOrLeftMinimalClosesPane`) also fails on main. **0 regressions.** ## Screenshots (if appropriate) After fix: <img width="899" height="604" alt="image" src="https://github.com/user-attachments/assets/04494ae2-cef3-4875-b30b-6b91eb1c0872" /> <img width="881" height="587" alt="image" src="https://github.com/user-attachments/assets/85907aad-1abf-47fe-9f2f-5b49164515ce" /> |
||
|
|
83ebb939f7 |
TableView: low-risk fixes (keyboard group toggle, gripper cursor and visuals, Fluent grid lines, scroller lookup, samples) (#12349)
## Fixes | Internal issue | Type | Change | |---|---|---| | https://task.ms/64512114 | Bug | Toggling a focused group header with the keyboard no longer jumps the viewport to earlier groups | | https://task.ms/64475631 | Bug | A missing `TabularSurfaceGridLineBrush` is reported once per instance (debug diagnostic) | | https://task.ms/63787021 | Task (part) | Scroller ancestor lookup stops at the TableView | | https://task.ms/64475630 | Bug | Selection indicator seeded from accent fill tiers; unused `TabularSurfaceHeaderForegroundBrush` removed | | https://task.ms/63790855 | Task (part) | Sort and scroller-structure IDL docs match current behavior (comments only) | | https://task.ms/64475633 | Bug | Samples: remove the extra card frame around examples | | https://task.ms/64475637 | Bug | Samples: opt out of automatic high-contrast backplates | | https://task.ms/64491943 | Bug | Test only: row banding follows the element theme (app-side fix is theme-aware brushes) | | https://task.ms/64475632 | Bug | ResizeGripper template collapsed to one Border; overwritten row `MinHeight` setter removed | | https://task.ms/64475628 | Bug | ResizeGripper: no accent hover line; 1px neutral line only while dragging | | https://task.ms/64143434 | Bug | ResizeGripper shows the East-West resize cursor; mouse captured while dragging, released on Escape, capture loss, disable or unload | | https://task.ms/64475611 | Bug (part) | Grid lines use the `DividerStrokeColorDefault` values; the new outer-border key stays in #12307 | ## PR Type - [x] Bugfix - [ ] Feature - [ ] Code style update (formatting, renaming) - [ ] Refactoring (no functional changes, no API changes) - [ ] Build related changes - [x] Documentation content changes - [ ] Other (please describe): ## Description First of two PRs split out of #12307. This one carries only small, low-risk fixes with **no new API** (product: +158 / −72 lines; IDL changes are comment-only; samples +51 / −52). New API and the larger changes stay in #12307, which is stacked on this PR. | File | +/- | Change | |---|---|---| | `TableView/TableView_Keyboard.cpp` | +14 / −1 | `OnBodyBringIntoViewRequested` ignores the focus-rescue element (row or group header) only while a group-toggle focus restore is pending; with no body scroller, focus reveal goes through the app's ScrollViewer | | `TableView/TableView.cpp` (+ .h) | +11 / −4 | Missing grid-line brush logged once; `FindScrollViewerAncestor` stops at the TableView | | `ResizeGripper/ResizeGripper.cpp` (+ .h) | +97 / −21 | Resize cursor; mouse capture during drag, released on Escape, capture loss, disable and unload | | `ResizeGripper/ResizeGripper.xaml`, `ResizeGripper_themeresources.xaml` | +18 / −26 | Template collapsed to one Border; hover line removed; 1px neutral line only while dragging | | `CommonStyles/TabularSurfaces_themeresources.xaml` | +8 / −15 | Selection indicator uses `SystemAccentColorLight2`/`Dark1`; grid lines use `DividerStrokeColorDefault` values; unused header foreground key removed | | `TableView/TableView.xaml` | +0 / −2 | Overwritten row `MinHeight` setter removed | | `TableView/TableView.idl`, `TableViewSource.idl` | +10 / −3 | Doc comments only | ### Current Behavior - Toggling a focused group header near the bottom of a grouped list with the keyboard scrolls the viewport to unrelated earlier groups. - A missing grid-line brush is silently replaced; the scroller lookup can walk past the TableView into app content. - The selection indicator uses the raw accent seed color. - The column resize gripper shows no resize cursor and draws an accent line on hover. - Grid lines are darker than the Fluent divider stroke. - Samples wrap every example in an extra card and let the system draw high-contrast backplates over the control's own colors. ### New Behavior - The focused group header stays in view after a keyboard toggle. - In custom templates without a body ScrollViewer, keyboard focus reveals the focused row through the app's ScrollViewer. - A missing grid-line brush produces one debug diagnostic per instance; the scroller lookup is bounded by the TableView. - The selection indicator follows Fluent accent fill tiers (small visible color change). - The gripper shows the East-West resize cursor and holds it during a drag; no hover line; a 1px neutral line shows only while dragging. - Grid lines use the fainter Fluent divider values (visible change). - Samples show the control directly and keep its high-contrast colors. ## Customer Impact Keyboard group toggling keeps the user's place; column resizing shows the expected cursor and Fluent visuals; selection indicator and grid lines match Fluent tokens; cleaner samples. ## Regression Potential - [x] Low risk — isolated change, limited scope - [ ] Medium risk — touches shared components or public APIs - [ ] High risk — architectural or breaking API change The keyboard guard applies only while a group-toggle focus restore is pending. The removed theme key is a preview key that nothing reads. The gripper now captures the mouse during a drag; capture is released on Escape, capture loss, disable and unload. Grid lines use the Fluent divider values, a visible but intended change. ## How Has This Been Tested? - [x] I have performed a self-review of my own code - [x] I have added tests to cover my changes - [x] Existing tests pass locally Full TableView suites on test VMs: 639 API tests pass (3 Localization tests are `[Ignore]`) and 150/150 interaction tests pass. Product, test apps and TableView samples build (x64 chk). Four rounds of automated review (code, WinUI, regression, security) until no High+ findings. New tests: `VerifyFocusedBottomGroupToggleKeepsViewport`, `VerifyFocusedGroupToggleAmongCollapsedHeadersKeepsViewport`, `VerifyDroppedGroupToggleKeepsRowBringIntoView`, `VerifyScrollerLookupStopsAtTheTableView`, `VerifySelectionIndicatorBrushFollowsAccentFillTiers`, `VerifyAlternatingRowBackgroundFollowsElementThemeOppositeToApp`, `VerifyResizeGripperHoldsResizeCursor`, `VerifyDisablingGripperMidDragCancelsResize`, `VerifyEndDragOnUnloadedGripperRaisesDragCompleted`, `VerifyRemovingTableMidDragCancelsResize`, `VerifyResizeGripperSeparatorShowsOnlyWhileDragging`, `VerifyGridLineBrushesMatchFluentStrokeTokens`; interaction tests `GroupHeaderKeyboardToggleAtBottomKeepsViewport`, `PointerOverGripperShowsResizeCursor`, `PointerResizeEscapeReleasesCapture`, `KeyboardNavigationRevealsRowInAppScrollerWithoutBodyScroller`. --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 94961bb8-d187-483c-bece-19e203752c67 |