Add Avalonia.Controls.Maui support to templates - #35950
Conversation
|
Hey there @@drasticactions! Thank you so much for your PR! Someone from the team will get assigned to your PR shortly and we'll get it reviewed. |
|
Hey there @drasticactions! Thank you so much for your PR! Someone from the team will get assigned to your PR shortly and we'll get it reviewed. |
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 35950Or
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 35950" |
kubaflo
left a comment
There was a problem hiding this comment.
PR #35950 — Add Avalonia.Controls.Maui support to templates
Verdict: NEEDS_CHANGES (confidence: high). Nice addition — the new --with-avalonia option is structurally well-wired through template.json symbols, dotnetcli.host.json, the csproj (desktop head + Avalonia.Controls.Maui package refs), and there's an integration test. But the generated project doesn't compile in the Avalonia-on case.
Blocking (inline)
- ❌
MauiProgram.cs:27—UseAvaloniaEmbedding<AvaloniaApp>()references an undefinedAvaloniaApp. On the mobile heads the embedding path needs an AvaloniaApplicationtype that the template never generates, so--with-avaloniaproduces a project that fails to build (CS0246). Generate theAvaloniaApptype as template content (or target an existing type). (3 models + code-confirmed.)
Suggestions (not blocking)
- Integration test coverage (
SimpleTemplateTest.cs):WithAvaloniaAddsHandlersAndDesktopHeadcurrently mostly asserts generated content rather than building the Avalonia-on project. A build assertion would have caught theAvaloniaAppissue above — consider compiling the generated project with the option enabled. (gemini) template.jsonversion default (~line 202): the11.0.0-preview.*floating range is reasonable for a preview-stage integration but pins consumers to whatever floats at restore time; consider a fixed version or document the intent. (opus-4.6)template.json:199parameterdataType— minor convention nit per gemini; worth a glance.
CI
Check Build/*Templates* integration legs once the AvaloniaApp issue is fixed — the template-generation tests are the relevant signal here.
Solid groundwork; the one real blocker is the missing AvaloniaApp in the generated output. Happy to re-review.
Re-review PR #35950 — Add Avalonia.Controls.Maui to templates (new head
|
|
With the pinning of versions, I'm fine with it. I'm not sure what's the right way for us to handling bumping the templates for new versions and what the other dependencies do. I'm open to discussions on that. |
Redth
left a comment
There was a problem hiding this comment.
Local validation on macOS — all four heads build and run ✅
I generated the template out of this branch and exercised it end-to-end rather than just eyeballing the diff. Emulated the pack-time token substitution (DOTNET_TFM_VALUE etc.) into a temp copy of maui-mobile, dotnet new install'd it, generated all three variants, then restored / built / deployed.
Environment: SDK 11.0.100-preview.6.26359.118, maui workload 11.0.0-preview.6.26360.8. Avalonia.Controls.Maui resolved to 11.0.0-preview.6.26198.1455.
| Check | Result |
|---|---|
Generation: --with-avalonia, plain, --with-avalonia --sample-content |
✅ all correct |
Gating — --sample-content suppresses all Avalonia content |
✅ |
| Restore (4 TFMs) | ✅ correct lib folders: net11.0, -android37.0, -ios26.5, -maccatalyst26.5 |
Debug build: net11.0 / -android / -ios / -maccatalyst |
✅ |
Release + TrimMode=partial + -warnaserror |
✅ 0 warnings, all 4 |
Full multi-TFM dotnet build (what the CI Build theory does) |
✅ |
| Runtime | ✅ desktop head renders MAUI through Avalonia; iOS sim, Android emulator, and MacCatalyst all launch and render |
--avaloniaControlsMauiVersion override |
✅ works, correctly hidden from CLI help |
| Default create (with restore post-action) | ✅ |
I also confirmed the AvaloniaControlsMauiGenerateBootstrap commit is genuinely needed, not just belt-and-braces: the Core package's props only auto-sets it when UseMaui == true, and NuGet props are imported before the project body sets that. Removing the explicit property fails the platform heads with CS0246: The type or namespace name 'AvaloniaApp' could not be found. Good catch.
So the functional core of this is solid. Comments below are about plumbing and long-term maintenance rather than correctness.
Not tied to a specific line
Component governance. eng/scripts/update-cgmanifest.ps1 drives src/Templates/src/cgmanifest.json from a hardcoded $packageMap. Avalonia.Controls.Maui and Avalonia.Controls.Maui.Desktop aren't in it, so they won't be registered. Every other third-party package referenced by these templates (CommunityToolkit.Maui, CommunityToolkit.Mvvm, Syncfusion.Maui.Toolkit, Microsoft.Data.Sqlite.Core, SQLitePCLRaw.bundle_green) is. These need adding before this ships.
Description drift. template.json says "embedding Avalonia on mobile and Windows"; templatestrings.en.json says "embedding Avalonia on the mobile and Windows heads". The loc file wins at runtime — worth syncing so the strings don't diverge further.
Missing loc entry. symbols/avaloniaControlsMauiVersion/description isn't in templatestrings.en.json, unlike every other symbol. Low impact since it's isHidden for the CLI, but other hosts may surface it unlocalized.
VS wizard UX. WithAvalonia has a displayName, so it renders as a checkbox in the VS project wizard alongside "Include sample content". Checking both silently produces a project with no Avalonia in it. Either make it CLI-only or find a way to surface the conflict.
dotnet run needs -f. The generated project has 4-5 TFMs, so plain dotnet run fails with "Your project targets multiple frameworks". The Avalonia README's "run with dotnet run" story doesn't hold out of the box, and launchSettings.json only has a "Windows Machine" profile. Pre-existing MAUI behaviour so not a blocker, but a desktop profile would make the new head much more discoverable.
Untested: the Windows head — I'm on macOS. On Windows the project ends up with both net11.0 and net11.0-windows10.0.19041.0; worth someone running it there before merge.
Product question: this puts a third-party preview dependency into the first-party dotnet new maui template. Assuming that's already agreed, but flagging in case it needs explicit sign-off.
There was a problem hiding this comment.
Pull request overview
Adds an opt-in --with-avalonia switch to the maui-mobile (maui) template to wire up Avalonia.Controls.Maui, including a non-platform DOTNET_TFM desktop head and corresponding app-builder configuration. Also updates template packaging/version substitution and adds integration-test coverage to validate the template output.
Changes:
- Add
--with-avalonia(gated off when--sample-contentis used) and update the generatedMauiProgramto use Avalonia app/embedding setup. - Extend the template project to add a
DOTNET_TFMdesktop target and Avalonia package references (with template-replaced version token). - Add integration tests for
--with-avalonia, plus introduce Avalonia package version plumbing and cgmanifest version mapping.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| src/TestUtils/src/Microsoft.Maui.IntegrationTests/SimpleTemplateTest.cs | Adds build matrix entries and new assertions validating Avalonia template output + gating behavior. |
| src/Templates/src/templates/maui-mobile/MauiProgram.cs | Conditionally alters CreateMauiApp signature and wires Avalonia app/embedding calls when enabled. |
| src/Templates/src/templates/maui-mobile/MauiApp.1.csproj | Adds DOTNET_TFM desktop head, bootstrap property, and Avalonia package references behind a computed template symbol. |
| src/Templates/src/templates/maui-mobile/.template.config/template.json | Defines WithAvalonia, computed UseAvaloniaHandlers, and version token replacement symbol. |
| src/Templates/src/templates/maui-mobile/.template.config/localize/templatestrings.en.json | Adds localized strings for the new template option and version override. |
| src/Templates/src/templates/maui-mobile/.template.config/ide.host.json | Hides the Avalonia-related symbols from IDE host UI. |
| src/Templates/src/templates/maui-mobile/.template.config/dotnetcli.host.json | Maps WithAvalonia to --with-avalonia and hides the version override symbol. |
| src/Templates/src/Microsoft.Maui.Templates.csproj | Adds template-time replacement plumbing for AVALONIA_CONTROLS_MAUI_VERSION_VALUE. |
| src/Templates/src/cgmanifest.json | Adds Avalonia package registrations (noted issue: file is generator-owned). |
| eng/Versions.props | Introduces AvaloniaControlsMauiPackageVersion for consistent versioning. |
| eng/scripts/update-cgmanifest.ps1 | Maps Avalonia packages to the new version property for cgmanifest generation. |
The Avalonia registrations are produced automatically by eng/scripts/update-cgmanifest.ps1 from the AvaloniaControlsMauiPackageVersion property in eng/Versions.props, both of which this PR already adds. eng/CgManifest.targets is imported by Microsoft.Maui.Templates.csproj and its UpdateCgManifest target runs BeforeTargets="BeforeBuild" (enabled by default), so src/Templates/src/cgmanifest.json is rewritten on every build of the templates project. Component Governance only needs the manifest present on disk at build time, which that regeneration guarantees, and the file is not packed into the nupkg (no pipeline sets GenerateCgManifest=true). Committing a hand-edited copy therefore adds diff noise that is overwritten on the next build, so drop it and keep only the generator inputs. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 53749cde-edca-4995-aac1-33d30bc2d2fc
|
Follow-up: all of my review threads are now resolved, and I am withdrawing the two remaining loose ends from my original review body.
The only caveat left is that I validated from a macOS host, so the For the record, my final re-validation ran against the actual packed template rather than a hand-emulated substitution:
(One unrelated snag worth knowing about if you hit it locally: Nice work — this looks good to me from a template-mechanics standpoint. |
<!-- 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! ### Root Cause MAUI uses package versions from the in-development .NET SDK for shipping projects, tests, and generated templates. During previews, each SDK update therefore changes shipping dependencies and template defaults even though the previous band's compatible packages are already published. An unconstrained NuGet `*` is also not prerelease-aware: it selected stable .NET 10 ASP.NET Core packages for prerelease MAUI artifacts, causing the Blazor integration builds to fail with assembly version conflicts. ### Description of Change - consumes the SDK-flowed .NET 11 dependency versions as N without changing `eng/Versions.props` — currently `11.0.0-rc.1.26379.102` - defines one explicit `MicrosoftDotNetN1PackageVersion` in `eng/NuGetVersions.targets`, pinned to the published Preview 7 packages (`11.0.0-preview.7.26381.103`) - patches the existing package version properties to N-1 for shipping/source projects while current N is prerelease - uses current N for standard and MAUI test projects, plus explicit current-source runners, benchmarks, and manual test apps - automatically returns shipping projects to exact N when the current version becomes stable - covers Microsoft.Extensions.*, Microsoft.AspNetCore.*, Microsoft.JSInterop, System.Text.Json, System.Text.Encodings.Web, Microsoft.Bcl.AsyncInterfaces, and System.CodeDom - removes stale hardcoded test versions so centralized N selection applies consistently - emits prerelease-aware `11.0.0-*` template defaults while N is prerelease and exact current versions when N is stable - records N-1 Microsoft.Extensions template dependencies in component governance during previews and exact current dependencies at GA The N-1 substitution is gated on N containing a prerelease label, so it remained active across the Preview → RC transition and disables itself automatically at GA. ### Validation Verified against the current N / N-1 pair (`11.0.0-rc.1.26379.102` / `11.0.0-preview.7.26381.103`): - the pin is taken from the official release metadata (`releases.json` → `runtime.version` for `11.0.0-preview.7`), and all affected packages were confirmed published on nuget.org - confirmed the PR has no diff under `eng/Version*`, and adds no `preview.6` literal - provisioned the RC 1 SDK with `dotnet cake --target=dotnet` and restored with it - `Core`, `Controls.Core`, and `Microsoft.AspNetCore.Components.WebView.Maui` resolve **only** N-1 packages — scanned each `project.assets.json`, zero RC 1 leakage into shipping projects - `Core.UnitTests` resolves the `Microsoft.Extensions.*` stack at current N (RC 1), confirming the test lane is unaffected - `dotnet build` of `Core` and `Microsoft.AspNetCore.Components.WebView.Maui` succeeds with 0 warnings and 0 errors — the BlazorWebView surface is where the original assembly version conflict appeared - template version items evaluate to `11.0.0-*` for the .NET tokens while N is prerelease - `eng/scripts/update-cgmanifest.ps1` resolves `Microsoft.Extensions.*` to N-1, and missing N-1 data still fails generation explicitly Validated earlier against the previous band, with the mechanism unchanged since: consecutive preview-then-stable template packs switching all seven generated defaults from `11.0.0-*` to exact `11.0.0`; generating and building Blazor Server and Auto/WebAssembly projects from an installed template `.nupkg`; and inspecting 22 produced `.nupkg` nuspecs for dependency-band leakage. ### Merge Notes Merging the latest `net11.0` conflicted with #35950, which added an `AVALONIA_CONTROLS_MAUI_VERSION_VALUE` template token to the property-group replacement block that this PR replaces with `_TemplatePackageVersion` items. The token was folded into the new item form, and its computed value is byte-identical to the previous behaviour (`11.0.0-preview.6.*`). `Avalonia.Controls.Maui` is third-party, pinned separately in `eng/Versions.props`, and is intentionally **not** wired into the N/N-1 mechanism, so its band is preserved rather than floated to `11.0.0-*`. ### Alternatives Considered PR #36598 attempted the same decoupling from `main`, using generic previous-published aggregates and private selector properties. This replacement targets the actual `net11.0` dependency graph, treats the most recently published band as N-1, and uses an explicit `N1`-named property as requested. A bare `*` was initially used for preview template defaults, but NuGet excludes prerelease packages from that range. Constraining the floating version to the current release (`11.0.0-*`) keeps preview templates floating while preventing fallback to stable packages from an older .NET release. The initial implementation added duplicate conditional `PackageReference` items and a self-invoking MSBuild test harness to the template project. The final implementation instead patches the existing central version properties and keeps test-only targets out of the production project. ### Issues Fixed Fixes #36434 --------- Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: f8808afd-de8d-414a-b271-fae23ee7b596 Copilot-Session: 7d43af42-9e93-4be0-9754-b362beec3456
(I know it says "MAKE SURE THIS PR IS TARGETING MAIN" but since these packages only run in net11.0, I think it's right to target it there, since it shouldn't be in net10 builds, but we can always retarget it)
Description of Change
This PR adds the command
--with-avaloniato the templates, which addsAvalonia.Controls.Mauidesktop support the generated MAUI app.app.mp4
This updates the basic template so it
Avalonia.Controls.MauiandAvalonia.Controls.Maui.Desktopnugetsnet11.0TFM as a deployable platform.useSingleAppLifetimeis a parameter value that would get passed in from the Avalonia builder for which lifetime to use.UseAvaloniaAppis where the Avalonia handlers are registered. It's if-defed out for the existing MAUI platforms so they render with native controls by default, since we're still working in this repo to enable running these handlers on those platforms.UseAvaloniaEmbeddingis enabled for those platforms to allow for "Embedded" Avalonia Maui controls and handlers, which is our existing Avalonia Maui Hybrid platform and lets you intergrate native and drawn controls together, with the native controls being the default.This is our first run of getting a template change in, so we're keeping it simple.
maui-mobiletemplate for Desktop support. It doesn't changemaui-blazor, which we don't support. I can addmaui-multiplatformsupport if we want now, or that can be another PR, I don't have a preference.11.0.0-...) but, of course, we can't align onMauiVersion. I can pin to a version if that's best.