Repository navigation
Fix design-time XAML compile items and defer to WinUI's own target - #826
Conversation
Outside Visual Studio the XAML markup compiler never runs at design time, so the C# language service cannot see the generated code-behind and reports false errors on InitializeComponent and every x:Name field. This package worked around that by globbing every .cs file under obj\$(Configuration)\$(TargetFramework). That glob omits $(Platform), so it surfaced nothing at all for the x64 builds real WinUI apps use, and where it did match it re-added the SDK's AssemblyInfo file by absolute path alongside the SDK's own relative entry. Replace it with a target that mirrors the IncludeGeneratedXamlCompileItemsForDesignTime target WinUI is shipping: a narrow *.g.cs / *.g.i.cs glob rooted at $(IntermediateOutputPath), the same exclude set, and the same non-VS and SDK-style guards. Because the excludes end with @(Compile), whichever of the two targets runs second contributes nothing, so the two cannot conflict in either import order. Also stand down entirely when the referenced WinUI already provides the target. MSBuild has no "is this target defined" test, so probe the file that ships it via $(XamlCompilerPropsAndTargetsDirectory). That needs no version comparison, so a servicing branch that cherry-picks the target is detected the same as a newer feature release. EnableWinUIDesignTimeGeneratedXamlCompileItems=false is honored so one setting disables both. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: f27fcd4e-9c6e-4583-88a6-c563c8a9f492
There was a problem hiding this comment.
🟡 Changes recommended
The documented shared opt-out does not disable WinUI’s current upstream target.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Fixes design-time XAML code-behind discovery for platform-specific WinUI builds while avoiding duplicate compile items and deferring to WinUI’s target.
Changes:
- Uses
IntermediateOutputPathand narrow generated-code globs. - Adds compatibility guards and 13 regression tests.
File summaries
| File | Description |
|---|---|
src/winapp-NuGet/build/Microsoft.Windows.SDK.BuildTools.WinApp.targets |
Implements guarded design-time compile-item discovery. |
src/winapp-NuGet/tests/NuGet.Tests.ps1 |
Tests platform paths, exclusions, guards, and deduplication. |
Review details
- Files reviewed: 2/2 changed files
- Comments generated: 1
- Review effort level: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Build Metrics ReportBinary Sizes
Test Results✅ 4786 passed, 5 skipped out of 4791 tests in 932.5s (+129.4s vs. baseline) Test Coverage✅ 89% line coverage, 82.3% branch coverage · ✅ no change vs. baseline CLI Startup Time61ms median (x64, Try This BuildInstalls the MSIX for your architecture, replacing any previously installed build. Needs the GitHub CLI — the command offers to install it and sign you in if it is missing. & ([scriptblock]::Create((irm https://raw.githubusercontent.com/microsoft/winappCli/main/scripts/winapp-pr.ps1))) 826Switching between builds often?Put the tool on your PATH once: & ([scriptblock]::Create((irm https://raw.githubusercontent.com/microsoft/winappCli/main/scripts/winapp-pr.ps1))) -AddToPathThen this build is just: winapp-pr 826Run Updated 2026-09-09 01:31:03 UTC · commit |
WinUI's 3.0 build ships IncludeGeneratedXamlCompileItemsForDesignTime without an EnableWinUIDesignTimeGeneratedXamlCompileItems condition, so setting that switch to false does not stop the platform from adding the generated code-behind. Only the 2.x servicing cherry-pick carries the gate. Say so instead of promising the switch disables both targets. Suppressing the platform's target is deliberately not attempted. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: f27fcd4e-9c6e-4583-88a6-c563c8a9f492
Description
Outside Visual Studio (VS Code / C# Dev Kit) the XAML markup compiler never runs at design time, so the C# language service cannot see the generated code-behind and reports false errors on
InitializeComponentand everyx:Namefield. This package worked around that by globbing every.csfile underobj\$(Configuration)\$(TargetFramework).That glob has two problems:
$(Platform). Real WinUI apps set<Platforms>x86;x64;ARM64</Platforms>and build x64, so their intermediates land inobj\x64\Debug\.... The workaround surfaced nothing at all there, which means it has been silently doing nothing for the most common WinUI configuration.AssemblyInfo.csby absolute path alongside the SDK's own relative entry. Roslyn dedupes that by path, so it is invisible in the IDE, butcscwould not.Separately, WinUI is shipping this same fix as its own
IncludeGeneratedXamlCompileItemsForDesignTimetarget. It is already inMicrosoft.WindowsAppSDK.WinUI 3.0.0-experimental.260818.21, with a 2.x servicing cherry-pick. We need to not fight it.Adopt the platform's approach. The workaround becomes a target that mirrors WinUI's: a narrow
*.g.cs/*.g.i.csglob rooted at$(IntermediateOutputPath)(which already accounts for$(Platform)and any customBaseIntermediateOutputPath), the same exclude set for source-generator output, intermediate XAML, and the SDK'sAssemblyInfo/AssemblyAttributes/GlobalUsingsfiles, and the sameBuildingInsideVisualStudioandUsingMicrosoftNETSdkguards.Stand down when the platform already has it. MSBuild has no "is this target defined" test, so the check probes the file that ships the target, via
$(XamlCompilerPropsAndTargetsDirectory)(set by WinUI's ownMicrosoft.WinUI.props, so it resolves for direct and transitive references alike). This needs no version comparison, which matters because a servicing cherry-pick has to be detected exactly like a newer feature release.Worth flagging for review: detection is an optimization, not the safety net. Our package sorts before
Microsoft.WindowsAppSDK.WinUIin NuGet import order, so our target often runs first and detection cannot help. What actually makes the two safe together is that both excludes end with@(Compile), so whichever runs second contributes nothing. Verified in both import orders. If detection ever silently breaks (say WinUI renames or relocates the target), the result is a redundant no-op, not a duplicate.Usage Example
EnableWinUIDesignTimeGeneratedXamlCompileItems=falseturns off the target added here:The name is WinUI's, deliberately. Their 2.x servicing build gates their target on the same switch, so on that build one setting disables both. Their 3.0 build ships the target ungated, so there this only turns off the winapp fallback and the platform still contributes its items. Suppressing the platform's target is deliberately not attempted.
Related Issue
N/A. The underlying language-service behavior is tracked upstream at microsoft/vscode-dotnettools#1018, which the targets file already links.
Type of Change
Checklist
Screenshots / Demo
Measured in VS Code with C# Dev Kit against a real WinUI 3 app (WindowsAppSDK 1.8) consuming this package built by
dotnet pack. "Platform target" means WinUI'sIncludeGeneratedXamlCompileItemsForDesignTime, injected verbatim from the shipped 3.0.0-experimental package to simulate a newer WinUI.InitializeComponent,AppTitleBar,RichInputBox, ...)So there is no user-visible conflict either way today. The regression this PR actually fixes shows up when you ask for the design-time
@(Compile)set atPlatform=x64:Platform=x64App.g.cs,MainWindow.g.cs,XamlTypeInfo.g.cs,App.g.i.cs,MainWindow.g.i.cs)Additional Notes
13 new Pester tests in
src/winapp-NuGet/tests/NuGet.Tests.ps1cover the behavior, including thePlatform=x64regression, each individual exclude, the stand-down path against a stubbed newer WinUI, and the "never contributes a file already in@(Compile)" property that keeps the two targets compatible. They are hermetic and need no build output.Validation: 54/54 in that suite, plus a clean
scripts/build-cli.ps1run. No user-facing docs cover design-time or IntelliSense behavior today, so none needed updating.The one failing CI test,
Mp4SinkWriterEncoderTests.Mp4SinkWriterEncoder_RealEncoderCoversValidationAndSuccessfulComplete, is unrelated. It drives the real Media Foundation H.264 sink writer and failed withMF_E_SINK_NO_SAMPLES_PROCESSED (0xC00D4A44), meaning the encoder accepted its single 64x64 frame but the sink emitted nothing beforeComplete(). This branch changes only a packaged.targetsfile and a Pester script, neither of which is part ofWinApp.Cli.Tests.dll.AI Description
This section is auto-generated by AI when the PR is opened or updated. To opt out, delete this entire section including the marker comments.