Skip to content

[XAML] Enable Incremental Hot Reload by default in Debug - #36682

Merged
kubaflo merged 2 commits into
net11.0from
feature/xihr-default-on
Jul 23, 2026
Merged

[XAML] Enable Incremental Hot Reload by default in Debug#36682
kubaflo merged 2 commits into
net11.0from
feature/xihr-default-on

Conversation

@StephaneDelcroix

Copy link
Copy Markdown
Contributor

Note

Are you waiting for the changes in this PR to be merged?
It would be very helpful if you could test the resulting artifacts from this PR and let us know in a comment if this change resolves your issue. Thank you!

Description

XAML Incremental Hot Reload (XIHR) shipped in #34338 as opt-in, with EnableMauiIncrementalHotReload defaulting to false everywhere. Per the .NET 11 Hot Reload plan (Phase 3 — Default-On, Preview 7), this PR turns it on by default.

What changed

A single MSBuild default in Microsoft.Maui.Controls.targets:

  • EnableMauiIncrementalHotReload now defaults to true for Debug builds and stays false for Release/publish.
  • As a result, MauiXamlHotReload resolves to SourceGen (instead of Legacy) for Debug builds.

Hot reload is a dev-time feature: gating default-on to Debug keeps the per-page registry calls and the Microsoft.Maui.RuntimeFeature.IsIncrementalHotReloadEnabled runtime switch off in shipped apps, so they trim away in Release/publish (the RuntimeHostConfigurationOption is emitted with Trim="true"). This mirrors the existing EnableMauiXamlDiagnostics Debug-gating pattern a few lines below in the same file.

Users can still opt out with <EnableMauiIncrementalHotReload>false</EnableMauiIncrementalHotReload>; legacy XAML Hot Reload remains available as a fallback (VS / VS Code toggle).

Behavior matrix

Configuration EnableMauiIncrementalHotReload MauiXamlHotReload
Debug (default) true SourceGen
Release / publish (default) false Legacy
Debug + explicit false false Legacy
Release + explicit true true SourceGen

Testing

Validated the MSBuild default cascade across all of the above scenarios (Debug→on/SourceGen, Release→off/Legacy, empty config→off, explicit opt-out/opt-in both respected). No public API changes; templates don't set the flag, so new projects pick up the Debug default automatically. Existing SourceGen unit tests (which set the flag per-file) continue to cover both on and off code-generation paths.

Copilot AI review requested due to automatic review settings July 20, 2026 14:29
@github-actions

Copy link
Copy Markdown
Contributor

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 36682

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 36682"

@StephaneDelcroix StephaneDelcroix added this to the .NET 11.0-preview7 milestone Jul 20, 2026
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).
There may be pipelines that require an authorized user to comment /azp run to run.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Enables XAML Incremental Hot Reload (XIHR) by default for Debug builds by changing the MSBuild default for EnableMauiIncrementalHotReload in Microsoft.Maui.Controls.targets, while keeping the default off for non-Debug configurations.

Changes:

  • Default EnableMauiIncrementalHotReload to true when $(Configuration) == Debug.
  • Keep the fallback default EnableMauiIncrementalHotReload to false when not explicitly set.
  • Update the surrounding MSBuild comment describing XIHR defaulting behavior.

Comment on lines +13 to +18
<!-- XAML Incremental Hot Reload (XIHR): on by default for .NET 11 projects (Preview 7). Hot reload is a
dev-time feature; when enabled it emits per-page registry calls and keeps the runtime feature
switch on. Default off everywhere — set <EnableMauiIncrementalHotReload>true</...> to opt in.
Default-on is planned for a later preview (see the .NET 11 Hot Reload plan). Resolves to an
explicit true/false so the value always flows to the source generator and runtime config. -->
switch on, so it defaults on for Debug builds only and stays off for Release/publish, where the
registry calls and runtime switch trim away. Set <EnableMauiIncrementalHotReload>false</...> to
opt out (legacy XAML Hot Reload remains available as a fallback). Resolves to an explicit
true/false so the value always flows to the source generator and runtime config. -->
@github-actions github-actions Bot added the area-tooling XAML & C# Hot Reload, XAML Editor, Live Visual Tree, Live Preview, Debugging label Jul 20, 2026
@StephaneDelcroix

Copy link
Copy Markdown
Contributor Author

⚠️ Depends on #36683. Enabling XIHR by default (this PR) makes the repo's own projects build through the DataTemplate hot-reload source-gen path, which currently emits invalid code for required/multi-set and scoped templates (CS0128/CS0103/CS1503) — see #36683. This PR should not merge until #36683 lands and CI here is green.

StephaneDelcroix added a commit that referenced this pull request Jul 22, 2026
…tes (#36683)

<!-- Please let the below note in for people that find this PR -->
> [!NOTE]
> Are you waiting for the changes in this PR to be merged?
> It would be very helpful if you could [test the resulting
artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from
this PR and let us know in a comment if this change resolves your issue.
Thank you!

### Description

Hardens the XAML Incremental Hot Reload (XIHR, #34338) `DataTemplate`
source-gen so it produces valid code across real-world XAML. This is a
**prerequisite for enabling XIHR by default** (#36682) — with XIHR on,
several repo projects (`Essentials.AI.Sample`,
`Controls.TestCases.HostApp`, `Controls.Xaml.UnitTests`) currently fail
to build.

XIHR emits a `DataTemplate`'s content as a stably-named
`LoadTemplate_{line}_{pos}` local function so Edit-and-Continue has a
stable anchor (#36482). That function was **hoisted** to the top of the
generated method via `AddLocalMethod`, which breaks in two ways once
XIHR is actually enabled:

- **Duplicate method (CS0128 / CS8321).** A template value set more than
once in the same scope — e.g. a `required` `DataTemplate` property,
which the generator sets both in the object initializer *and* as an
assignment — emitted the named function **twice** → `error CS0128:
'LoadTemplate_L_P' is already defined` (+ `CS8321` unused).
- **Out-of-scope references (CS0103 / CS1503).** Hoisting to the method
top **lost the lambda's lexical scope**, so a template body that
referenced enclosing locals (the `DataTemplate` variable, name scopes,
resources) generated references to names that don't exist at that scope
→ `CS0103`, with a cascading `CS1503`.

These stayed latent because MAUI ships XIHR **opt-in (default off)**, so
no repo project built through this path until default-on was attempted.

### Fix

Emit the named local function **inline at the point of use** (not
hoisted), which restores the exact lexical scope the anonymous lambda
had, and **reserve each method name once per compilation unit** so it is
declared a single time — every set-site just re-points `LoadTemplate` at
that one function. Also removes ~25 lines of buffering. Non-HR builds
are unchanged (still an anonymous lambda).

### Tests

- New regression test
`DataTemplate_HotReload_SetMultipleTimes_EmitsSingleNamedMethod` — a
`required` `DataTemplate` property under XIHR. Verified it **fails
without the fix** (`CS0128 'LoadTemplate_9_18' already defined`) and
**passes with it**.
- Full `SourceGen.UnitTests` suite green (**452/452**), including all
existing #36482 XIHR tests.
- Verified locally that `Controls.Xaml.UnitTests` builds with
`EnableMauiIncrementalHotReload=true` with no `LoadTemplate` errors (the
CS0128/CS8321 pair is gone).

### Related

- Prerequisite for #36682 (enable XIHR by default in Debug).
- Follow-up hardening of #34338 / #36482.

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
XAML Incremental Hot Reload (XIHR, #34338) shipped opt-in behind
EnableMauiIncrementalHotReload, defaulting to false everywhere. Per the
.NET 11 Hot Reload plan (Phase 3, Preview 7), turn it on by default.

The property now defaults to true for Debug builds and stays false for
Release/publish, so the per-page registry calls and the
Microsoft.Maui.RuntimeFeature.IsIncrementalHotReloadEnabled runtime
switch trim away in shipped apps. This mirrors the existing
EnableMauiXamlDiagnostics Debug-gating pattern in the same file.

Opt out with <EnableMauiIncrementalHotReload>false</...>; legacy XAML
Hot Reload remains available as a fallback.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 22, 2026 11:12
@StephaneDelcroix
StephaneDelcroix force-pushed the feature/xihr-default-on branch from be29c94 to 8a2f3be Compare July 22, 2026 11:12

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

src/Controls/src/Build.Tasks/nuget/buildTransitive/netstandard2.0/Microsoft.Maui.Controls.targets:17

  • The comment is time-bound and overly specific (".NET 11 projects (Preview 7)") even though this targets file applies generally. This will quickly become outdated and makes it harder to understand the current contract. Consider describing the behavior in terms of Debug vs non-Debug configurations without referencing preview milestones.
    <!-- XAML Incremental Hot Reload (XIHR): on by default for .NET 11 projects (Preview 7). Hot reload is a
         dev-time feature; when enabled it emits per-page registry calls and keeps the runtime feature
         switch on, so it defaults on for Debug builds only and stays off for Release/publish, where the
         registry calls and runtime switch trim away. Set <EnableMauiIncrementalHotReload>false</...> to
         opt out (legacy XAML Hot Reload remains available as a fallback). Resolves to an explicit

Comment on lines +19 to 20
<EnableMauiIncrementalHotReload Condition="'$(EnableMauiIncrementalHotReload)' == '' and '$(Configuration)' == 'Debug'">true</EnableMauiIncrementalHotReload>
<EnableMauiIncrementalHotReload Condition="'$(EnableMauiIncrementalHotReload)' == ''">false</EnableMauiIncrementalHotReload>
Enabling XIHR by default (#36682) makes Debug builds omit the legacy
ResourceProvider2 hot-reload fallback (InitializeComponentRuntime), which
XIHR intentionally supersedes (InitializeComponentCodeWriter gates it on
!EnableIncrementalHotReload). Three Xaml.UnitTests exercise that legacy
path and broke under the new default:

- HotReloadWorks(SourceGen): the ResourceProvider2 update no longer applies
- HotReloadSupportForXSG: InitializeComponentRuntime no longer in the .xsg.cs
- TestSourceGenInflator: InitializeComponent IL length changed (394 -> 307)

Pin these tests to EnableMauiIncrementalHotReload=false (per-file MauiXaml
metadata for the two compiled-in XAML files, and a build arg for the
nested-build MSBuild test) so they keep covering the legacy fallback
regardless of the default. XIHR-on codegen is covered by SourceGen.UnitTests.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 22, 2026 12:59

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (1)

src/Controls/src/Build.Tasks/nuget/buildTransitive/netstandard2.0/Microsoft.Maui.Controls.targets:16

  • The updated XIHR comment says it "stays off for Release/publish", but the actual default is based only on $(Configuration) == 'Debug'. dotnet publish -c Debug (or any non-Release publish) would still default this to true, so the comment is misleading. Consider rewording to describe the real condition (Debug configuration vs non-Debug), or add an explicit publish check if that is the intent.
         switch on, so it defaults on for Debug builds only and stays off for Release/publish, where the
         registry calls and runtime switch trim away. Set <EnableMauiIncrementalHotReload>false</...> to

@kubaflo
kubaflo merged commit 5c7aebb into net11.0 Jul 23, 2026
32 checks passed
@kubaflo
kubaflo deleted the feature/xihr-default-on branch July 23, 2026 10:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-tooling XAML & C# Hot Reload, XAML Editor, Live Visual Tree, Live Preview, Debugging

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants