diff --git a/.opencode/commands/issue.md b/.opencode/commands/issue.md
index ffdb70a0..a1d47deb 100644
--- a/.opencode/commands/issue.md
+++ b/.opencode/commands/issue.md
@@ -128,8 +128,9 @@ If that value is blank, stop and ask the user for an issue number before doing a
- For library/test changes (`src/**`, `tests/**`), trigger `build-library.yml`.
- For sample changes (`samples/**`), trigger `build-samples.yml`.
- For template changes (`templates/**`), trigger `build-template.yml`.
+ - For docs changes (`docs/**`, `mkdocs.yml`, `mkdocs.shared.yml`, `mkdocs.production.yml`), trigger `build-docs.yml`.
- Do **not** dispatch `build-docs.yml`. Its `deploy` job has no branch guard, so any `workflow_dispatch` — including from a feature branch — publishes straight to the live GitHub Pages site.
+ `build-docs.yml`'s `deploy` job only runs for a `release` event, or for `workflow_dispatch` when `github.ref` starts with `refs/tags/`. Dispatching it with `--ref issues/$1` (a branch ref, not a tag) only runs its `build`/`validate` jobs — it is safe to dispatch from a feature branch and will never publish to the live GitHub Pages site. Do not pass a tag ref to `--ref` when dispatching `build-docs.yml` for pre-PR validation.
If none of the changed paths match any of the above, state that no CI workflow applies and continue with local test results only.
diff --git a/samples/todoapp/TodoApp.Avalonia/Directory.Build.props b/samples/todoapp/TodoApp.Avalonia/Directory.Build.props
index 5732b26f..3c58333f 100644
--- a/samples/todoapp/TodoApp.Avalonia/Directory.Build.props
+++ b/samples/todoapp/TodoApp.Avalonia/Directory.Build.props
@@ -2,8 +2,9 @@
enable
-
- 11.3.18
+
+ 12.1.0
diff --git a/samples/todoapp/TodoApp.Avalonia/TodoApp.Avalonia.Android/Application.cs b/samples/todoapp/TodoApp.Avalonia/TodoApp.Avalonia.Android/Application.cs
new file mode 100644
index 00000000..3b70f4da
--- /dev/null
+++ b/samples/todoapp/TodoApp.Avalonia/TodoApp.Avalonia.Android/Application.cs
@@ -0,0 +1,23 @@
+using Android.App;
+using Android.Runtime;
+using Avalonia;
+using Avalonia.Android;
+
+namespace TodoApp.Avalonia.Android;
+
+// Avalonia 12 changed Android app initialization so that CreateAppBuilder()/CustomizeAppBuilder()
+// are handled by an AvaloniaAndroidApplication subclass instead of AvaloniaMainActivity.
+// See https://docs.avaloniaui.net/docs/avalonia12-breaking-changes#android.
+[Application]
+public class Application : AvaloniaAndroidApplication
+{
+ protected Application(nint javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
+ {
+ }
+
+ protected override AppBuilder CustomizeAppBuilder(AppBuilder builder)
+ {
+ return base.CustomizeAppBuilder(builder)
+ .WithInterFont();
+ }
+}
diff --git a/samples/todoapp/TodoApp.Avalonia/TodoApp.Avalonia.Android/MainActivity.cs b/samples/todoapp/TodoApp.Avalonia/TodoApp.Avalonia.Android/MainActivity.cs
index 5ad26437..d2cb4908 100644
--- a/samples/todoapp/TodoApp.Avalonia/TodoApp.Avalonia.Android/MainActivity.cs
+++ b/samples/todoapp/TodoApp.Avalonia/TodoApp.Avalonia.Android/MainActivity.cs
@@ -1,6 +1,5 @@
using Android.App;
using Android.Content.PM;
-using Avalonia;
using Avalonia.Android;
namespace TodoApp.Avalonia.Android;
@@ -11,11 +10,6 @@ namespace TodoApp.Avalonia.Android;
Icon = "@drawable/icon",
MainLauncher = true,
ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize | ConfigChanges.UiMode)]
-public class MainActivity : AvaloniaMainActivity
+public class MainActivity : AvaloniaMainActivity
{
- protected override AppBuilder CustomizeAppBuilder(AppBuilder builder)
- {
- return base.CustomizeAppBuilder(builder)
- .WithInterFont();
- }
}
\ No newline at end of file
diff --git a/samples/todoapp/TodoApp.Avalonia/TodoApp.Avalonia.Android/Properties/AndroidManifest.xml b/samples/todoapp/TodoApp.Avalonia/TodoApp.Avalonia.Android/Properties/AndroidManifest.xml
index 7d86d689..b7a8b08c 100644
--- a/samples/todoapp/TodoApp.Avalonia/TodoApp.Avalonia.Android/Properties/AndroidManifest.xml
+++ b/samples/todoapp/TodoApp.Avalonia/TodoApp.Avalonia.Android/Properties/AndroidManifest.xml
@@ -1,5 +1,5 @@
-
+
diff --git a/samples/todoapp/TodoApp.Avalonia/TodoApp.Avalonia.Android/TodoApp.Avalonia.Android.csproj b/samples/todoapp/TodoApp.Avalonia/TodoApp.Avalonia.Android/TodoApp.Avalonia.Android.csproj
index 10129929..41f42f7f 100644
--- a/samples/todoapp/TodoApp.Avalonia/TodoApp.Avalonia.Android/TodoApp.Avalonia.Android.csproj
+++ b/samples/todoapp/TodoApp.Avalonia/TodoApp.Avalonia.Android/TodoApp.Avalonia.Android.csproj
@@ -15,9 +15,9 @@
`dotnet build` (not just `dotnet publish`) by SDK default. This CI pipeline only ever runs
`dotnet build` (see build-samples-todoapp-avalonia.yml) - it never publishes with
PublishTrimmed, so no actual IL trimming/linking happens here; the analyzer is purely
- diagnostic noise on framework APIs (Avalonia's BindingPlugins.DataValidators, EF Core's
- DbContext(DbContextOptions) ctor) that their own maintainers have marked
- [RequiresUnreferencedCode]. Setting this also sets EnableTrimAnalyzer=false (see
+ diagnostic noise on framework APIs (e.g. EF Core's DbContext(DbContextOptions) ctor)
+ that their own maintainers have marked [RequiresUnreferencedCode]. Setting this also
+ sets EnableTrimAnalyzer=false (see
Microsoft.NET.ILLink.targets), which disables the analyzer pass itself, not just its
warning output. See https://github.com/CommunityToolkit/Datasync/issues/521.
-->
diff --git a/samples/todoapp/TodoApp.Avalonia/TodoApp.Avalonia.Desktop/TodoApp.Avalonia.Desktop.csproj b/samples/todoapp/TodoApp.Avalonia/TodoApp.Avalonia.Desktop/TodoApp.Avalonia.Desktop.csproj
index 719c5fbd..a5ed319a 100644
--- a/samples/todoapp/TodoApp.Avalonia/TodoApp.Avalonia.Desktop/TodoApp.Avalonia.Desktop.csproj
+++ b/samples/todoapp/TodoApp.Avalonia/TodoApp.Avalonia.Desktop/TodoApp.Avalonia.Desktop.csproj
@@ -14,8 +14,6 @@
-
-
diff --git a/samples/todoapp/TodoApp.Avalonia/TodoApp.Avalonia.iOS/TodoApp.Avalonia.iOS.csproj b/samples/todoapp/TodoApp.Avalonia/TodoApp.Avalonia.iOS/TodoApp.Avalonia.iOS.csproj
index 24c2d933..94f5ae7d 100644
--- a/samples/todoapp/TodoApp.Avalonia/TodoApp.Avalonia.iOS/TodoApp.Avalonia.iOS.csproj
+++ b/samples/todoapp/TodoApp.Avalonia/TodoApp.Avalonia.iOS/TodoApp.Avalonia.iOS.csproj
@@ -10,9 +10,9 @@
because iOS app projects are marked trimmable by the SDK by default. This CI pipeline only
ever runs `dotnet build` (see build-samples-todoapp-avalonia.yml) - it never publishes with
PublishTrimmed, so no actual IL trimming/linking happens here; the analyzer is purely
- diagnostic noise on framework APIs (Avalonia's BindingPlugins.DataValidators, EF Core's
- DbContext(DbContextOptions) ctor) that their own maintainers have marked
- [RequiresUnreferencedCode]. Setting this also sets EnableTrimAnalyzer=false (see
+ diagnostic noise on framework APIs (e.g. EF Core's DbContext(DbContextOptions) ctor)
+ that their own maintainers have marked [RequiresUnreferencedCode]. Setting this also
+ sets EnableTrimAnalyzer=false (see
Microsoft.NET.ILLink.targets), which disables the analyzer pass itself, not just its
warning output. See https://github.com/CommunityToolkit/Datasync/issues/521.
-->
diff --git a/samples/todoapp/TodoApp.Avalonia/TodoApp.Avalonia/App.axaml.cs b/samples/todoapp/TodoApp.Avalonia/TodoApp.Avalonia/App.axaml.cs
index b3444e8d..c9f33c42 100644
--- a/samples/todoapp/TodoApp.Avalonia/TodoApp.Avalonia/App.axaml.cs
+++ b/samples/todoapp/TodoApp.Avalonia/TodoApp.Avalonia/App.axaml.cs
@@ -2,8 +2,6 @@
using System.IO;
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
-using Avalonia.Data.Core;
-using Avalonia.Data.Core.Plugins;
using Avalonia.Markup.Xaml;
using Avalonia.Threading;
using Microsoft.Data.Sqlite;
@@ -52,16 +50,19 @@ public override void Initialize()
///
public override void OnFrameworkInitializationCompleted()
{
- // Line below is needed to remove Avalonia data validation.
- // Without this line you will get duplicate validations from both Avalonia and CT
- BindingPlugins.DataValidators.RemoveAt(0);
-
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
desktop.MainWindow = new MainWindow { DataContext = GetRequiredService() };
}
+ else if (ApplicationLifetime is IActivityApplicationLifetime activityLifetime)
+ {
+ // Android now uses IActivityApplicationLifetime instead of ISingleViewApplicationLifetime.
+ // See https://docs.avaloniaui.net/docs/avalonia12-breaking-changes#android.
+ activityLifetime.MainViewFactory = () => new MainView { DataContext = GetRequiredService() };
+ }
else if (ApplicationLifetime is ISingleViewApplicationLifetime singleViewPlatform)
{
+ // ISingleViewApplicationLifetime is still used by iOS, browser, and embedded Linux platforms.
singleViewPlatform.MainView = new MainView { DataContext = GetRequiredService() };
}
diff --git a/samples/todoapp/TodoApp.Avalonia/TodoApp.Avalonia/TodoApp.Avalonia.csproj b/samples/todoapp/TodoApp.Avalonia/TodoApp.Avalonia/TodoApp.Avalonia.csproj
index 93253b0e..3610583d 100644
--- a/samples/todoapp/TodoApp.Avalonia/TodoApp.Avalonia/TodoApp.Avalonia.csproj
+++ b/samples/todoapp/TodoApp.Avalonia/TodoApp.Avalonia/TodoApp.Avalonia.csproj
@@ -14,8 +14,6 @@
-
-
diff --git a/samples/todoapp/TodoApp.Avalonia/TodoApp.Avalonia/Views/MainView.axaml b/samples/todoapp/TodoApp.Avalonia/TodoApp.Avalonia/Views/MainView.axaml
index 86ffb4ce..65426cd3 100644
--- a/samples/todoapp/TodoApp.Avalonia/TodoApp.Avalonia/Views/MainView.axaml
+++ b/samples/todoapp/TodoApp.Avalonia/TodoApp.Avalonia/Views/MainView.axaml
@@ -62,7 +62,7 @@
+ PlaceholderText="Add a new Item">