Skip to content

Add Avalonia.Controls.Maui support to templates - #35950

Merged
kubaflo merged 9 commits into
dotnet:net11.0from
drasticactions:dev/timill/net11.0-avalonia-templates
Aug 1, 2026
Merged

Add Avalonia.Controls.Maui support to templates#35950
kubaflo merged 9 commits into
dotnet:net11.0from
drasticactions:dev/timill/net11.0-avalonia-templates

Conversation

@drasticactions

@drasticactions drasticactions commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

(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-avalonia to the templates, which adds Avalonia.Controls.Maui desktop support the generated MAUI app.

app.mp4

This updates the basic template so it

  • Includes the Avalonia.Controls.Maui and Avalonia.Controls.Maui.Desktop nugets
  • Includes the net11.0 TFM as a deployable platform.
  • Updates the default Maui AppBuilder to include our handlers and embedded support.
public static class MauiProgram
{
	public static MauiApp CreateMauiApp(bool useSingleAppLifetime = false)
	{
		var builder = MauiApp.CreateBuilder();
		builder
			.UseMauiApp<App>()
#if !IOS && !MACCATALYST && !ANDROID && !WINDOWS
			.UseAvaloniaApp(useSingleAppLifetime)
#else
			.UseAvaloniaEmbedding<AvaloniaApp>()
#endif
			.ConfigureFonts(fonts =>
			{
				fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
				fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
			});

#if DEBUG
		builder.Logging.AddDebug();
#endif

		return builder.Build();
	}
}

useSingleAppLifetime is a parameter value that would get passed in from the Avalonia builder for which lifetime to use.
UseAvaloniaApp is 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.
UseAvaloniaEmbedding is 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.

  • It only edits the maui-mobile template for Desktop support. It doesn't change maui-blazor, which we don't support. I can add maui-multiplatform support if we want now, or that can be another PR, I don't have a preference.
  • This does not include support for the sample content template, since we would need to have Syncfusion support for that to work for our handlers. I would be happy to help with that effort.
  • This only implements it for Avalonia Desktop support. The browser support inside the template will come with a future PR, once we improve the accessibility story for WASM. You can, of course, add WASM support to your application yourself.
  • I used a wildcard for the version number in the template. I've aligned our versions on the .NET runtime version (So 11.0.0-...) but, of course, we can't align on MauiVersion. I can pin to a version if that's best.

@drasticactions drasticactions changed the title Dev/timill/net11.0 avalonia templates [test] Jun 16, 2026
@dotnet-policy-service dotnet-policy-service Bot added the community ✨ Community Contribution label Jun 16, 2026
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

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.

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

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.

@drasticactions drasticactions changed the title [test] Add Avalonia.Controls.Maui support to templates Jun 16, 2026
@github-actions github-actions Bot added the area-templates Project templates, Item Templates for Blazor and MAUI label Jun 16, 2026
@github-actions

github-actions Bot commented Jun 16, 2026

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 -- 35950

Or

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

@kubaflo kubaflo left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

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:27UseAvaloniaEmbedding<AvaloniaApp>() references an undefined AvaloniaApp. On the mobile heads the embedding path needs an Avalonia Application type that the template never generates, so --with-avalonia produces a project that fails to build (CS0246). Generate the AvaloniaApp type as template content (or target an existing type). (3 models + code-confirmed.)

Suggestions (not blocking)

  • Integration test coverage (SimpleTemplateTest.cs): WithAvaloniaAddsHandlersAndDesktopHead currently mostly asserts generated content rather than building the Avalonia-on project. A build assertion would have caught the AvaloniaApp issue above — consider compiling the generated project with the option enabled. (gemini)
  • template.json version default (~line 202): the 11.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:199 parameter dataType — 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.

Comment thread src/Templates/src/templates/maui-mobile/MauiProgram.cs
@kubaflo

kubaflo commented Jun 16, 2026

Copy link
Copy Markdown
Collaborator

Re-review PR #35950 — Add Avalonia.Controls.Maui to templates (new head 5f14dc8)

Verdict: NEEDS_DISCUSSION (confidence: medium) — ⬆️ from NEEDS_CHANGES. Thanks for the fast turnaround on the AvaloniaApp issue.

The fix

The new commit adds (guarded by UseAvaloniaHandlers) to MauiApp.1.csproj:

<AvaloniaControlsMauiGenerateBootstrap>true</AvaloniaControlsMauiGenerateBootstrap>

This is the right mechanism: Avalonia.Controls.Maui's AvaloniaControlsMauiGenerateBootstrap MSBuild property auto-generates the Avalonia bootstrap class used for MAUI embedding, which is what MauiProgram.cs's .UseAvaloniaEmbedding<AvaloniaApp>() (line 27) references. So the previously-undefined AvaloniaApp is now expected to be source-generated by the package rather than missing — that addresses the compile-break I flagged.

What's left to confirm (why NEEDS_DISCUSSION, not LGTM)

  1. Verify it actually builds. The fix relies on the external package generating a type named exactly AvaloniaApp in the project's namespace. I can't confirm that from the MAUI repo. The strongest way to close this is for the integration test to build the --with-avalonia project, not just assert on generated file text — that would prove AvaloniaApp is generated and the project compiles. (gemini/opus-4.8 raised the same point about the new tests being content-only.)
  2. Per the package docs, embedding usually also wants a <UseAvaloniaEmbedding Include="…App" /> item and/or a concrete Avalonia App — please confirm the bootstrap generation alone produces everything UseAvaloniaEmbedding<AvaloniaApp>() needs at the mobile heads (vs. only the desktop head).

Minor (carried over)

  • The 11.0.0-preview.* floating version default pins consumers to whatever floats at restore time — consider a fixed version.

Net: the right fix mechanism is in place; the remaining ask is evidence it compiles (ideally a build assertion in SimpleTemplateTest). Happy to flip to LGTM once the Avalonia-on project is shown to build.

Multi-model review (gpt-5.5 · opus-4.8 · opus-4.6 · gemini-3.1-pro). Comments only — not a formal approval.

@drasticactions

drasticactions commented Jun 16, 2026

Copy link
Copy Markdown
Contributor Author

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 Redth left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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.

Comment thread src/Templates/src/templates/maui-mobile/MauiProgram.cs Outdated
Comment thread src/Templates/src/templates/maui-mobile/.template.config/template.json Outdated
Comment thread src/Templates/src/templates/maui-mobile/MauiApp.1.csproj
Copilot AI review requested due to automatic review settings July 29, 2026 15:27

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

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-content is used) and update the generated MauiProgram to use Avalonia app/embedding setup.
  • Extend the template project to add a DOTNET_TFM desktop 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.

Comment thread src/Templates/src/cgmanifest.json
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

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 10 out of 10 changed files in this pull request and generated no new comments.

@Redth

Redth commented Jul 30, 2026

Copy link
Copy Markdown
Member

Follow-up: all of my review threads are now resolved, and I am withdrawing the two remaining loose ends from my original review body.

  • dotnet run requiring -f — withdrawn. On .NET 11 dotnet run prompts you to pick a target framework when the project is multi-targeted, so the extra desktop TFM does not actually regress the F5/CLI experience. No launchSettings.json change needed.
  • useSingleViewLifetime — dropped, see the thread. Non-blocking either way.

The only caveat left is that I validated from a macOS host, so the net11.0-windows10.0.19041.0 head was not exercised locally. CI covers it.

For the record, my final re-validation ran against the actual packed template rather than a hand-emulated substitution:

dotnet pack src/Templates/src/Microsoft.Maui.Templates.csproj -c Release
dotnet new install <nupkg>
dotnet new maui --with-avalonia
  • Substituted template.json comes out with "defaultValue": "11.0.0-preview.6.*" and zero leftover *_VALUE tokens anywhere in the file.
  • Generated project restores Avalonia.Controls.Maui/11.0.0-preview.6.26198.1455.
  • net11.0, net11.0-ios, net11.0-maccatalyst, and net11.0-android all build clean.
  • The same pack regenerated cgmanifest.json from the reverted base file and emitted both Avalonia registrations, confirming 6a15d9a was safe.

(One unrelated snag worth knowing about if you hit it locally: Microsoft.Extensions.Logging.Debug 11.0.0-preview.7.* is not on nuget.org yet, so restore fails against a preview.6 SDK. Pre-existing repo/SDK skew, nothing to do with this change.)

Nice work — this looks good to me from a template-mechanics standpoint.

@kubaflo
kubaflo merged commit b088593 into dotnet:net11.0 Aug 1, 2026
36 of 40 checks passed
@github-actions github-actions Bot added this to the .NET 11.0-preview7 milestone Aug 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-templates Project templates, Item Templates for Blazor and MAUI community ✨ Community Contribution

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants