From 6fe6186075dc366ed06c4145549778f67eb2e747 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Mon, 3 Aug 2026 17:29:13 +0000
Subject: [PATCH 1/2] chore(deps): bump ReactiveUI.SourceGenerators from 3.1.0
to 3.2.0
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---
Directory.Packages.props | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Directory.Packages.props b/Directory.Packages.props
index c27665d..5853c03 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -28,7 +28,7 @@
-
+
From ec52f1120a8398e97ca2e3cc88160e25d0c81943 Mon Sep 17 00:00:00 2001
From: Michael Stonis <120685+michaelstonis@users.noreply.github.com>
Date: Thu, 6 Aug 2026 10:59:12 -0500
Subject: [PATCH 2/2] feat!: migrate to ReactiveUI 24 (.Reactive), Avalonia 12,
and add WPF/WinUI 3/Uno platforms
ReactiveUI 24 re-platformed onto ReactiveUI.Primitives and split into two
distributions. Stellar adopts the .Reactive family (ReactiveUI.Reactive,
ReactiveUI.Maui.Reactive, ReactiveUI.Blazor.Reactive): same new engine and
performance as the default packages, but keeps the System.Reactive 7 types
(Unit, IScheduler, Subject) that Stellar's public API is built on.
System.Reactive 7 becomes a direct dependency since ReactiveUI no longer
flows it transitively. Consumers must add `global using ReactiveUI.Reactive;`
(this also fixes code emitted by ReactiveGenerator and
ReactiveUI.SourceGenerators, which still target the 23-era namespace).
Avalonia.ReactiveUI has no ReactiveUI 24- or Avalonia 12-compatible release,
so the dependency is removed: Stellar.Avalonia now provides its own IViewFor
base classes, activation fetcher and UI-thread scheduler. That unlocks
Avalonia 12.1.1 (Avalonia.Diagnostics dropped - discontinued after 11.3 and
was referenced but unused). ReactiveUI 24 also dropped ReactiveViewCell;
Stellar.Maui now ships its own.
New platform heads, mirroring the existing per-platform pattern:
- Stellar.Wpf (ReactiveUI.WPF.Reactive; targets net10.0-windows10.0.19041.0 -
the plain net10.0-windows TFM silently resolves the package's .NET
Framework assets via NU1701)
- Stellar.WinUI (ReactiveUI.WinUI.Reactive + WindowsAppSDK 2.3.1; PRI/MSIX
tooling disabled on non-Windows hosts so it cross-compiles)
- Stellar.Uno (Uno.WinUI 6.6.176; no ReactiveUI 24-compatible Uno package
exists, so it hand-rolls its activation fetcher and scheduler like
Stellar.Avalonia)
All three are in Stellar.sln, Stellar.slnf (ubuntu CI builds them; verified
in a Linux container) and the nuget.yml release matrix, which previously
would have skipped them silently on a v-tag.
Other upgrades: CommunityToolkit.Maui 15.0.0, CommunityToolkit.Maui.Markup
8.0.0. Everything else was already latest; deliberate pins kept
(Microsoft.CodeAnalysis.CSharp 5.0.0, stylecop beta).
Validated: Release build of Stellar.slnf on macOS and Linux, 259/259 unit
tests, pack rehearsal of all 9 non-MAUI release-matrix projects, and
Release builds of Stellar.Maui/Stellar.Maui.PopUp for android and ios.
MacCatalyst and Windows TFMs are compile-verified only (no local workload/
runner).
Co-Authored-By: Claude Fable 5
---
.github/workflows/nuget.yml | 12 ++
Directory.Packages.props | 53 ++++---
.../AvaloniaActivationForViewFetcher.cs | 58 ++++++++
Stellar.Avalonia/AvaloniaScheduler.cs | 75 ++++++++++
.../Extensions/AppBuilderExtensions.cs | 24 ++-
Stellar.Avalonia/Stellar.Avalonia.csproj | 3 -
Stellar.Avalonia/UserControlBase.cs | 62 +++++++-
Stellar.Avalonia/WindowBase.cs | 39 ++++-
Stellar.AvaloniaSample/GlobalUsings.cs | 1 +
Stellar.AvaloniaSample/Program.cs | 4 +-
.../Stellar.AvaloniaSample.csproj | 3 -
Stellar.Blazor/ComponentBase.cs | 2 +-
.../Extensions/BuilderExtensions.cs | 2 +-
Stellar.Blazor/GlobalUsings.cs | 3 +-
Stellar.Blazor/InjectableComponentBase.cs | 2 +-
Stellar.Blazor/LayoutComponentBase.cs | 2 +-
Stellar.Blazor/Stellar.Blazor.csproj | 2 +-
Stellar.BlazorSample/GlobalUsings.cs | 1 +
.../PopupNavigationObservableExtensions.cs | 1 +
Stellar.Maui.PopUp/ReactivePopupPage.cs | 1 +
Stellar.Maui.PopUp/Stellar.Maui.PopUp.csproj | 2 +-
.../Extensions/MauiAppBuilderExtensions.cs | 4 +-
.../Extensions/VisualElementExtensions.cs | 1 -
Stellar.Maui/GlobalUsings.cs | 6 +
Stellar.Maui/Stellar.Maui.csproj | 2 +-
Stellar.Maui/Views/ReactiveViewCell.cs | 59 ++++++++
Stellar.Maui/Views/ViewCellBase.cs | 1 +
.../GlobalUsings.cs | 1 +
Stellar.MauiSample/GlobalUsings.cs | 1 +
Stellar.MauiSample/Stellar.MauiSample.csproj | 2 +-
Stellar.UnitTests/Usings.cs | 3 +-
Stellar.Uno/Extensions/BuilderExtensions.cs | 51 +++++++
Stellar.Uno/GlobalUsings.cs | 8 +
Stellar.Uno/HotReloadService.cs | 21 +++
Stellar.Uno/PageBase.cs | 117 +++++++++++++++
Stellar.Uno/Stellar.Uno.csproj | 27 ++++
Stellar.Uno/UnoActivationForViewFetcher.cs | 37 +++++
Stellar.Uno/UnoScheduler.cs | 80 ++++++++++
Stellar.Uno/UnoViewManager.cs | 66 +++++++++
Stellar.Uno/UserControlBase.cs | 94 ++++++++++++
Stellar.WinUI/Extensions/BuilderExtensions.cs | 39 +++++
Stellar.WinUI/GlobalUsings.cs | 8 +
Stellar.WinUI/HotReloadService.cs | 21 +++
Stellar.WinUI/PageBase.cs | 92 ++++++++++++
Stellar.WinUI/Stellar.WinUI.csproj | 32 ++++
Stellar.WinUI/UserControlBase.cs | 69 +++++++++
Stellar.WinUI/WinUIViewManager.cs | 66 +++++++++
Stellar.Wpf/Extensions/BuilderExtensions.cs | 37 +++++
Stellar.Wpf/GlobalUsings.cs | 8 +
Stellar.Wpf/HotReloadService.cs | 21 +++
Stellar.Wpf/Stellar.Wpf.csproj | 28 ++++
Stellar.Wpf/UserControlBase.cs | 137 ++++++++++++++++++
Stellar.Wpf/WindowBase.cs | 83 +++++++++++
Stellar.Wpf/WpfViewManager.cs | 60 ++++++++
Stellar.sln | 42 ++++++
Stellar.slnf | 5 +-
Stellar/GlobalUsings.cs | 1 +
Stellar/Stellar.csproj | 3 +-
58 files changed, 1621 insertions(+), 64 deletions(-)
create mode 100644 Stellar.Avalonia/AvaloniaActivationForViewFetcher.cs
create mode 100644 Stellar.Avalonia/AvaloniaScheduler.cs
create mode 100644 Stellar.Maui/Views/ReactiveViewCell.cs
create mode 100644 Stellar.Uno/Extensions/BuilderExtensions.cs
create mode 100644 Stellar.Uno/GlobalUsings.cs
create mode 100644 Stellar.Uno/HotReloadService.cs
create mode 100644 Stellar.Uno/PageBase.cs
create mode 100644 Stellar.Uno/Stellar.Uno.csproj
create mode 100644 Stellar.Uno/UnoActivationForViewFetcher.cs
create mode 100644 Stellar.Uno/UnoScheduler.cs
create mode 100644 Stellar.Uno/UnoViewManager.cs
create mode 100644 Stellar.Uno/UserControlBase.cs
create mode 100644 Stellar.WinUI/Extensions/BuilderExtensions.cs
create mode 100644 Stellar.WinUI/GlobalUsings.cs
create mode 100644 Stellar.WinUI/HotReloadService.cs
create mode 100644 Stellar.WinUI/PageBase.cs
create mode 100644 Stellar.WinUI/Stellar.WinUI.csproj
create mode 100644 Stellar.WinUI/UserControlBase.cs
create mode 100644 Stellar.WinUI/WinUIViewManager.cs
create mode 100644 Stellar.Wpf/Extensions/BuilderExtensions.cs
create mode 100644 Stellar.Wpf/GlobalUsings.cs
create mode 100644 Stellar.Wpf/HotReloadService.cs
create mode 100644 Stellar.Wpf/Stellar.Wpf.csproj
create mode 100644 Stellar.Wpf/UserControlBase.cs
create mode 100644 Stellar.Wpf/WindowBase.cs
create mode 100644 Stellar.Wpf/WpfViewManager.cs
diff --git a/.github/workflows/nuget.yml b/.github/workflows/nuget.yml
index dfa7a67..f14ce38 100644
--- a/.github/workflows/nuget.yml
+++ b/.github/workflows/nuget.yml
@@ -38,6 +38,18 @@ jobs:
- name: Stellar.SourceGenerators
project: Stellar.SourceGenerators/Stellar.SourceGenerators.csproj
output: Stellar.SourceGenerators/bin/Release
+ # The Windows-targeted projects cross-compile on the macOS runner via
+ # EnableWindowsTargeting; Stellar.WinUI additionally disables the PRI/MSIX
+ # tooling on non-Windows hosts in its csproj.
+ - name: Stellar.Wpf
+ project: Stellar.Wpf/Stellar.Wpf.csproj
+ output: Stellar.Wpf/bin/Release
+ - name: Stellar.WinUI
+ project: Stellar.WinUI/Stellar.WinUI.csproj
+ output: Stellar.WinUI/bin/Release
+ - name: Stellar.Uno
+ project: Stellar.Uno/Stellar.Uno.csproj
+ output: Stellar.Uno/bin/Release
steps:
- uses: actions/checkout@v7
- uses: actions/setup-dotnet@v6
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 5853c03..fd52e21 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -20,13 +20,20 @@
-
+
-
-
-
+
+
+
+
+
+
@@ -36,24 +43,23 @@
-
-
-
+
+
-
+
-
-
-
-
-
-
+
+
+
+
@@ -62,6 +68,15 @@
+
+
+
+
+
+
-
-
diff --git a/Stellar.Avalonia/UserControlBase.cs b/Stellar.Avalonia/UserControlBase.cs
index 3fb62e4..a0c9bcd 100644
--- a/Stellar.Avalonia/UserControlBase.cs
+++ b/Stellar.Avalonia/UserControlBase.cs
@@ -1,17 +1,36 @@
-using System.ComponentModel;
+using System.ComponentModel;
using System.Reactive;
using System.Reactive.Disposables;
using Avalonia;
-using Avalonia.ReactiveUI;
+using Avalonia.Controls;
+using ReactiveUI;
namespace Stellar.Avalonia;
-public abstract class UserControlBase : ReactiveUserControl, IStellarView
+// Derives from UserControl directly and implements IViewFor itself: this base class
+// previously came from Avalonia.ReactiveUI's ReactiveUserControl, which has no release
+// compatible with ReactiveUI 24.
+public abstract class UserControlBase : UserControl, IStellarView
where TViewModel : class
{
+ public static readonly StyledProperty ViewModelProperty =
+ AvaloniaProperty.Register, TViewModel?>(nameof(ViewModel));
+
[EditorBrowsable(EditorBrowsableState.Never)]
public ViewManager ViewManager { get; } = new AvaloniaViewManager();
+ public TViewModel? ViewModel
+ {
+ get => GetValue(ViewModelProperty);
+ set => SetValue(ViewModelProperty, value);
+ }
+
+ object? IViewFor.ViewModel
+ {
+ get => ViewModel;
+ set => ViewModel = value as TViewModel;
+ }
+
public IObservable UserControlInitialized => ViewManager.Initialized;
public IObservable Activated => ViewManager.Activated;
@@ -65,20 +84,52 @@ protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e
base.OnDetachedFromVisualTree(e);
}
+ protected override void OnDataContextChanged(EventArgs e)
+ {
+ base.OnDataContextChanged(e);
+
+ ViewModel = DataContext as TViewModel;
+ }
+
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
+ if (change.Property == ViewModelProperty)
+ {
+ var newViewModel = change.GetNewValue();
+
+ if (!ReferenceEquals(newViewModel, DataContext))
+ {
+ DataContext = newViewModel;
+ }
+ }
+
ViewManager.PropertyChanged(this, change.Property.Name);
base.OnPropertyChanged(change);
}
}
-public abstract class UserControlBase : ReactiveUserControl, IStellarView
+public abstract class UserControlBase : UserControl, IStellarView
where TViewModel : class
{
+ public static readonly StyledProperty ViewModelProperty =
+ AvaloniaProperty.Register, TViewModel?>(nameof(ViewModel));
+
[EditorBrowsable(EditorBrowsableState.Never)]
public ViewManager ViewManager { get; } = new AvaloniaViewManager();
+ public TViewModel? ViewModel
+ {
+ get => GetValue(ViewModelProperty);
+ set => SetValue(ViewModelProperty, value);
+ }
+
+ object? IViewFor.ViewModel
+ {
+ get => ViewModel;
+ set => ViewModel = value as TViewModel;
+ }
+
public IObservable UserControlInitialized => ViewManager.Initialized;
public IObservable Activated => ViewManager.Activated;
@@ -117,6 +168,9 @@ protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e
base.OnDetachedFromVisualTree(e);
}
+ // No ViewModel<->DataContext syncing here: unlike the single-parameter base class,
+ // DataContext holds a TDataModel that is mapped onto the view model, so the two
+ // properties are intentionally independent.
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
ViewManager.PropertyChanged(this, change.Property.Name);
diff --git a/Stellar.Avalonia/WindowBase.cs b/Stellar.Avalonia/WindowBase.cs
index 454f4dc..2083ee7 100644
--- a/Stellar.Avalonia/WindowBase.cs
+++ b/Stellar.Avalonia/WindowBase.cs
@@ -4,17 +4,35 @@
using System.Reactive.Disposables;
using Avalonia;
using Avalonia.Controls;
-using Avalonia.ReactiveUI;
+using ReactiveUI;
using Stellar.ViewModel;
namespace Stellar.Avalonia
{
- public abstract class WindowBase : ReactiveWindow, IStellarView
+ // Derives from Window directly and implements IViewFor itself: this base class
+ // previously came from Avalonia.ReactiveUI's ReactiveWindow, which has no release
+ // compatible with ReactiveUI 24.
+ public abstract class WindowBase : Window, IStellarView
where TViewModel : class
{
+ public static readonly StyledProperty ViewModelProperty =
+ AvaloniaProperty.Register, TViewModel?>(nameof(ViewModel));
+
[EditorBrowsable(EditorBrowsableState.Never)]
public ViewManager ViewManager { get; } = new AvaloniaViewManager();
+ public TViewModel? ViewModel
+ {
+ get => GetValue(ViewModelProperty);
+ set => SetValue(ViewModelProperty, value);
+ }
+
+ object? IViewFor.ViewModel
+ {
+ get => ViewModel;
+ set => ViewModel = value as TViewModel;
+ }
+
public IObservable WindowInitialized => ViewManager.Initialized;
public IObservable WindowActivated => ViewManager.Activated;
@@ -77,8 +95,25 @@ protected override void OnClosing(WindowClosingEventArgs e)
base.OnClosing(e);
}
+ protected override void OnDataContextChanged(EventArgs e)
+ {
+ base.OnDataContextChanged(e);
+
+ ViewModel = DataContext as TViewModel;
+ }
+
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
+ if (change.Property == ViewModelProperty)
+ {
+ var newViewModel = change.GetNewValue();
+
+ if (!ReferenceEquals(newViewModel, DataContext))
+ {
+ DataContext = newViewModel;
+ }
+ }
+
ViewManager.PropertyChanged(this, change.Property.Name);
base.OnPropertyChanged(change);
diff --git a/Stellar.AvaloniaSample/GlobalUsings.cs b/Stellar.AvaloniaSample/GlobalUsings.cs
index eea879e..ae68aa3 100644
--- a/Stellar.AvaloniaSample/GlobalUsings.cs
+++ b/Stellar.AvaloniaSample/GlobalUsings.cs
@@ -2,4 +2,5 @@
global using System.Reactive.Disposables;
global using System.Reactive.Linq;
global using ReactiveUI;
+global using ReactiveUI.Reactive;
global using ReactiveUI.SourceGenerators;
diff --git a/Stellar.AvaloniaSample/Program.cs b/Stellar.AvaloniaSample/Program.cs
index e2a04fa..8a643ff 100644
--- a/Stellar.AvaloniaSample/Program.cs
+++ b/Stellar.AvaloniaSample/Program.cs
@@ -1,6 +1,5 @@
using System;
using Avalonia;
-using Avalonia.ReactiveUI;
using Stellar.Avalonia;
namespace Stellar.AvaloniaSample;
@@ -21,9 +20,8 @@ public static AppBuilder BuildAvaloniaApp() =>
.UsePlatformDetect()
.WithInterFont()
.LogToTrace()
- .UseStellarComponents()
#if DEBUG
.EnableHotReload()
#endif
- .UseReactiveUI();
+ .UseStellarComponents();
}
diff --git a/Stellar.AvaloniaSample/Stellar.AvaloniaSample.csproj b/Stellar.AvaloniaSample/Stellar.AvaloniaSample.csproj
index 6375a9c..f951fe5 100644
--- a/Stellar.AvaloniaSample/Stellar.AvaloniaSample.csproj
+++ b/Stellar.AvaloniaSample/Stellar.AvaloniaSample.csproj
@@ -18,9 +18,6 @@
-
-
-
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/Stellar.Blazor/ComponentBase.cs b/Stellar.Blazor/ComponentBase.cs
index 08fc344..c9de932 100644
--- a/Stellar.Blazor/ComponentBase.cs
+++ b/Stellar.Blazor/ComponentBase.cs
@@ -3,7 +3,7 @@
using System.Runtime.CompilerServices;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Routing;
-using ReactiveUI.Blazor;
+using ReactiveUI.Reactive.Blazor;
namespace Stellar.Blazor;
diff --git a/Stellar.Blazor/Extensions/BuilderExtensions.cs b/Stellar.Blazor/Extensions/BuilderExtensions.cs
index dea3654..92fe1c1 100644
--- a/Stellar.Blazor/Extensions/BuilderExtensions.cs
+++ b/Stellar.Blazor/Extensions/BuilderExtensions.cs
@@ -1,6 +1,6 @@
using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
-using ReactiveUI.Builder;
+using ReactiveUI.Reactive.Builder;
namespace Stellar.Blazor;
diff --git a/Stellar.Blazor/GlobalUsings.cs b/Stellar.Blazor/GlobalUsings.cs
index af4247b..ac002eb 100644
--- a/Stellar.Blazor/GlobalUsings.cs
+++ b/Stellar.Blazor/GlobalUsings.cs
@@ -5,4 +5,5 @@
global using System.Reactive.Linq;
global using System.Reactive.Subjects;
global using ReactiveUI;
-global using ReactiveUI.Blazor;
\ No newline at end of file
+global using ReactiveUI.Reactive;
+global using ReactiveUI.Reactive.Blazor;
\ No newline at end of file
diff --git a/Stellar.Blazor/InjectableComponentBase.cs b/Stellar.Blazor/InjectableComponentBase.cs
index 8bb5ebe..d110f4a 100644
--- a/Stellar.Blazor/InjectableComponentBase.cs
+++ b/Stellar.Blazor/InjectableComponentBase.cs
@@ -1,7 +1,7 @@
using System.ComponentModel;
using System.Runtime.CompilerServices;
using Microsoft.AspNetCore.Components;
-using ReactiveUI.Blazor;
+using ReactiveUI.Reactive.Blazor;
namespace Stellar.Blazor;
diff --git a/Stellar.Blazor/LayoutComponentBase.cs b/Stellar.Blazor/LayoutComponentBase.cs
index 8e7b5d0..43bc7a0 100644
--- a/Stellar.Blazor/LayoutComponentBase.cs
+++ b/Stellar.Blazor/LayoutComponentBase.cs
@@ -1,7 +1,7 @@
using System.ComponentModel;
using System.Runtime.CompilerServices;
using Microsoft.AspNetCore.Components;
-using ReactiveUI.Blazor;
+using ReactiveUI.Reactive.Blazor;
namespace Stellar.Blazor;
diff --git a/Stellar.Blazor/Stellar.Blazor.csproj b/Stellar.Blazor/Stellar.Blazor.csproj
index 02b5be9..4117fa2 100644
--- a/Stellar.Blazor/Stellar.Blazor.csproj
+++ b/Stellar.Blazor/Stellar.Blazor.csproj
@@ -17,7 +17,7 @@
-
+
diff --git a/Stellar.BlazorSample/GlobalUsings.cs b/Stellar.BlazorSample/GlobalUsings.cs
index 17682b3..cc3bcee 100644
--- a/Stellar.BlazorSample/GlobalUsings.cs
+++ b/Stellar.BlazorSample/GlobalUsings.cs
@@ -2,4 +2,5 @@
global using System.Reactive.Disposables;
global using System.Reactive.Linq;
global using ReactiveUI;
+global using ReactiveUI.Reactive;
global using ReactiveUI.SourceGenerators;
diff --git a/Stellar.Maui.PopUp/Extensions/PopupNavigationObservableExtensions.cs b/Stellar.Maui.PopUp/Extensions/PopupNavigationObservableExtensions.cs
index b2791c6..9b97453 100644
--- a/Stellar.Maui.PopUp/Extensions/PopupNavigationObservableExtensions.cs
+++ b/Stellar.Maui.PopUp/Extensions/PopupNavigationObservableExtensions.cs
@@ -4,6 +4,7 @@
using Mopups.Pages;
using Mopups.Services;
using ReactiveUI;
+using ReactiveUI.Reactive;
using Stellar.Exceptions;
using Stellar.Maui.Exceptions;
using static Stellar.Maui.NavigationObservableExtensions;
diff --git a/Stellar.Maui.PopUp/ReactivePopupPage.cs b/Stellar.Maui.PopUp/ReactivePopupPage.cs
index ca59490..4ac774e 100644
--- a/Stellar.Maui.PopUp/ReactivePopupPage.cs
+++ b/Stellar.Maui.PopUp/ReactivePopupPage.cs
@@ -1,5 +1,6 @@
using Mopups.Pages;
using ReactiveUI;
+using ReactiveUI.Reactive;
namespace Stellar.Maui.Pages;
diff --git a/Stellar.Maui.PopUp/Stellar.Maui.PopUp.csproj b/Stellar.Maui.PopUp/Stellar.Maui.PopUp.csproj
index 7f831b4..c10aaf9 100644
--- a/Stellar.Maui.PopUp/Stellar.Maui.PopUp.csproj
+++ b/Stellar.Maui.PopUp/Stellar.Maui.PopUp.csproj
@@ -33,7 +33,7 @@
-
+
diff --git a/Stellar.Maui/Extensions/MauiAppBuilderExtensions.cs b/Stellar.Maui/Extensions/MauiAppBuilderExtensions.cs
index 020c9f4..ac6edcf 100644
--- a/Stellar.Maui/Extensions/MauiAppBuilderExtensions.cs
+++ b/Stellar.Maui/Extensions/MauiAppBuilderExtensions.cs
@@ -1,7 +1,7 @@
using System.Reflection;
using Microsoft.Extensions.DependencyInjection.Extensions;
-using ReactiveUI.Builder;
-using ReactiveUI.Maui;
+using ReactiveUI.Reactive.Builder;
+using ReactiveUI.Reactive.Maui;
using Splat;
namespace Stellar.Maui;
diff --git a/Stellar.Maui/Extensions/VisualElementExtensions.cs b/Stellar.Maui/Extensions/VisualElementExtensions.cs
index 6fe753f..57404cd 100644
--- a/Stellar.Maui/Extensions/VisualElementExtensions.cs
+++ b/Stellar.Maui/Extensions/VisualElementExtensions.cs
@@ -1,5 +1,4 @@
using System.Runtime.CompilerServices;
-using DynamicData.Diagnostics;
using Stellar.Exceptions;
using Stellar.ViewModel;
diff --git a/Stellar.Maui/GlobalUsings.cs b/Stellar.Maui/GlobalUsings.cs
index 3859fcc..da99508 100644
--- a/Stellar.Maui/GlobalUsings.cs
+++ b/Stellar.Maui/GlobalUsings.cs
@@ -10,4 +10,10 @@
global using System.Reactive.Linq;
global using System.Reactive.Subjects;
global using ReactiveUI;
+
+// Stellar's own MAUI view wrappers (ReactiveStackLayout, ReactiveGrid, ReactiveViewCell)
+// live in the ReactiveUI.Maui namespaces for historical compatibility; the ReactiveUI 24
+// platform types now come from ReactiveUI.Reactive.Maui.
global using ReactiveUI.Maui;
+global using ReactiveUI.Reactive;
+global using ReactiveUI.Reactive.Maui;
diff --git a/Stellar.Maui/Stellar.Maui.csproj b/Stellar.Maui/Stellar.Maui.csproj
index ff80ba7..634273d 100644
--- a/Stellar.Maui/Stellar.Maui.csproj
+++ b/Stellar.Maui/Stellar.Maui.csproj
@@ -33,7 +33,7 @@
-
+
diff --git a/Stellar.Maui/Views/ReactiveViewCell.cs b/Stellar.Maui/Views/ReactiveViewCell.cs
new file mode 100644
index 0000000..3fa53b7
--- /dev/null
+++ b/Stellar.Maui/Views/ReactiveViewCell.cs
@@ -0,0 +1,59 @@
+// StellarUI continues to support ListView for as long as .NET MAUI ships it. MAUI marks
+// ListView and its cell types obsolete in favour of CollectionView, but removing this
+// surface would break every consumer still using it, so the deprecation is suppressed here
+// rather than propagated. Revisit when MAUI actually removes the types.
+#pragma warning disable CS0618 // Type or member is obsolete
+
+// ReactiveUI.Maui shipped a ReactiveViewCell up to version 23; ReactiveUI 24 dropped it,
+// so Stellar provides its own. It lives in this namespace alongside ReactiveGrid and
+// ReactiveStackLayout, which Stellar has always supplied itself.
+namespace ReactiveUI.Maui.Views;
+
+///
+/// This is a that is also an .
+///
+/// The type of the view model.
+///
+///
+public class ReactiveViewCell : ViewCell, IViewFor
+ where TViewModel : class
+{
+ ///
+ /// The view model bindable property.
+ ///
+ public static readonly BindableProperty ViewModelProperty = BindableProperty.Create(
+ nameof(ViewModel),
+ typeof(TViewModel),
+ typeof(ReactiveViewCell),
+ default(TViewModel),
+ BindingMode.OneWay,
+ propertyChanged: OnViewModelChanged);
+
+ ///
+ /// Gets or sets the ViewModel to display.
+ ///
+ public TViewModel? ViewModel
+ {
+ get => (TViewModel)GetValue(ViewModelProperty);
+ set => SetValue(ViewModelProperty, value);
+ }
+
+ ///
+ object? IViewFor.ViewModel
+ {
+ get => ViewModel;
+ set => ViewModel = value as TViewModel;
+ }
+
+ ///
+ protected override void OnBindingContextChanged()
+ {
+ base.OnBindingContextChanged();
+ ViewModel = BindingContext as TViewModel;
+ }
+
+ private static void OnViewModelChanged(BindableObject bindableObject, object oldValue, object newValue)
+ {
+ bindableObject.BindingContext = newValue;
+ }
+}
diff --git a/Stellar.Maui/Views/ViewCellBase.cs b/Stellar.Maui/Views/ViewCellBase.cs
index 9a2e2b3..fc8f29d 100644
--- a/Stellar.Maui/Views/ViewCellBase.cs
+++ b/Stellar.Maui/Views/ViewCellBase.cs
@@ -5,6 +5,7 @@
#pragma warning disable CS0618 // Type or member is obsolete
using System.ComponentModel;
+using ReactiveUI.Maui.Views;
namespace Stellar.Maui.Views;
diff --git a/Stellar.MauiBlazorHybridSample/GlobalUsings.cs b/Stellar.MauiBlazorHybridSample/GlobalUsings.cs
index adfb3db..bffb3e3 100644
--- a/Stellar.MauiBlazorHybridSample/GlobalUsings.cs
+++ b/Stellar.MauiBlazorHybridSample/GlobalUsings.cs
@@ -2,6 +2,7 @@
global using System.Reactive.Disposables;
global using System.Reactive.Linq;
global using ReactiveUI;
+global using ReactiveUI.Reactive;
global using ReactiveUI.SourceGenerators;
global using Stellar.Maui;
global using Stellar.Maui.Pages;
diff --git a/Stellar.MauiSample/GlobalUsings.cs b/Stellar.MauiSample/GlobalUsings.cs
index 97d8ff8..8ae01f2 100644
--- a/Stellar.MauiSample/GlobalUsings.cs
+++ b/Stellar.MauiSample/GlobalUsings.cs
@@ -3,6 +3,7 @@
global using System.Reactive.Linq;
global using CommunityToolkit.Maui.Markup;
global using ReactiveUI;
+global using ReactiveUI.Reactive;
global using ReactiveUI.SourceGenerators;
global using Stellar.Maui;
global using Stellar.Maui.Pages;
diff --git a/Stellar.MauiSample/Stellar.MauiSample.csproj b/Stellar.MauiSample/Stellar.MauiSample.csproj
index 7ec06a2..1170d58 100644
--- a/Stellar.MauiSample/Stellar.MauiSample.csproj
+++ b/Stellar.MauiSample/Stellar.MauiSample.csproj
@@ -72,7 +72,7 @@
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/Stellar.UnitTests/Usings.cs b/Stellar.UnitTests/Usings.cs
index 9df1d42..28a1f4e 100644
--- a/Stellar.UnitTests/Usings.cs
+++ b/Stellar.UnitTests/Usings.cs
@@ -1 +1,2 @@
-global using Xunit;
+global using ReactiveUI.Reactive;
+global using Xunit;
diff --git a/Stellar.Uno/Extensions/BuilderExtensions.cs b/Stellar.Uno/Extensions/BuilderExtensions.cs
new file mode 100644
index 0000000..2b58068
--- /dev/null
+++ b/Stellar.Uno/Extensions/BuilderExtensions.cs
@@ -0,0 +1,51 @@
+using System.Reflection;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.UI.Dispatching;
+using ReactiveUI.Reactive.Builder;
+
+namespace Stellar.Uno;
+
+public static class BuilderExtensions
+{
+ public static IServiceCollection UseStellarComponents(this IServiceCollection services)
+ {
+ UseStellarComponents(services);
+
+ services.ConfigureStellarComponents(typeof(TStellarAssembly).GetTypeInfo().Assembly);
+
+ return services;
+ }
+
+ ///
+ /// Registers Stellar's Uno Platform services. Must be called from the UI thread
+ /// (e.g. in Application.OnLaunched) so the UI thread's
+ /// can be captured for the main-thread scheduler.
+ ///
+ /// The service collection.
+ /// The same service collection, for chaining.
+ public static IServiceCollection UseStellarComponents(this IServiceCollection services)
+ {
+ var dispatcherQueue = DispatcherQueue.GetForCurrentThread()
+ ?? throw new InvalidOperationException(
+ "UseStellarComponents must be called from the UI thread so the DispatcherQueue can be captured.");
+
+ // There is no ReactiveUI 24-compatible Uno platform package, so the activation
+ // fetcher and schedulers are registered here directly, mirroring Stellar.Avalonia.
+ RxAppBuilder
+ .CreateReactiveUIBuilder()
+ .WithRegistration(
+ static resolver => resolver.RegisterConstant(new UnoActivationForViewFetcher()))
+ .WithMainThreadScheduler(new UnoScheduler(dispatcherQueue))
+ .WithTaskPoolScheduler(Schedulers.ShortTermThreadPoolScheduler)
+ .Build();
+
+ return services;
+ }
+
+ public static IServiceCollection EnableHotReload(this IServiceCollection services)
+ {
+ HotReloadService.HotReloadAware = true;
+
+ return services;
+ }
+}
diff --git a/Stellar.Uno/GlobalUsings.cs b/Stellar.Uno/GlobalUsings.cs
new file mode 100644
index 0000000..bb463e0
--- /dev/null
+++ b/Stellar.Uno/GlobalUsings.cs
@@ -0,0 +1,8 @@
+global using System;
+global using System.Reactive;
+global using System.Reactive.Concurrency;
+global using System.Reactive.Disposables;
+global using System.Reactive.Linq;
+global using System.Reactive.Subjects;
+global using ReactiveUI;
+global using ReactiveUI.Reactive;
diff --git a/Stellar.Uno/HotReloadService.cs b/Stellar.Uno/HotReloadService.cs
new file mode 100644
index 0000000..e7fc0a9
--- /dev/null
+++ b/Stellar.Uno/HotReloadService.cs
@@ -0,0 +1,21 @@
+#pragma warning disable CS8632 // The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
+[assembly: System.Reflection.Metadata.MetadataUpdateHandler(typeof(Stellar.Uno.HotReloadService))]
+
+namespace Stellar.Uno;
+
+public static class HotReloadService
+{
+ public static bool HotReloadAware { get; set; }
+
+ public static event Action? UpdateApplicationEvent;
+
+ internal static void ClearCache(Type[]? types)
+ {
+ }
+
+ internal static void UpdateApplication(Type[]? types)
+ {
+ UpdateApplicationEvent?.Invoke(types);
+ }
+}
+#pragma warning restore CS8632 // The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
diff --git a/Stellar.Uno/PageBase.cs b/Stellar.Uno/PageBase.cs
new file mode 100644
index 0000000..de27e92
--- /dev/null
+++ b/Stellar.Uno/PageBase.cs
@@ -0,0 +1,117 @@
+using System.ComponentModel;
+using Microsoft.UI.Xaml;
+using Microsoft.UI.Xaml.Controls;
+using Microsoft.UI.Xaml.Navigation;
+
+namespace Stellar.Uno;
+
+// Implements IViewFor directly: there is no ReactiveUI 24-compatible Uno platform
+// package to supply a ReactivePage base class, so Stellar provides the ViewModel
+// dependency property and DataContext syncing itself.
+public abstract class PageBase : Page, IStellarView
+ where TViewModel : class
+{
+ public static readonly DependencyProperty ViewModelProperty = DependencyProperty.Register(
+ nameof(ViewModel),
+ typeof(TViewModel),
+ typeof(PageBase),
+ new PropertyMetadata(null));
+
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public ViewManager ViewManager { get; } = new UnoViewManager();
+
+ public TViewModel? ViewModel
+ {
+ get => (TViewModel?)GetValue(ViewModelProperty);
+ set => SetValue(ViewModelProperty, value);
+ }
+
+ object? IViewFor.ViewModel
+ {
+ get => ViewModel;
+ set => ViewModel = value as TViewModel;
+ }
+
+ public IObservable PageInitialized => ViewManager.Initialized;
+
+ public IObservable Activated => ViewManager.Activated;
+
+ public IObservable Deactivated => ViewManager.Deactivated;
+
+ public IObservable IsAppearing => ViewManager.IsAppearing;
+
+ public IObservable IsDisappearing => ViewManager.IsDisappearing;
+
+ public IObservable NavigatedTo => ViewManager.NavigatedTo;
+
+ public IObservable NavigatedFrom => ViewManager.NavigatedFrom;
+
+ public IObservable Disposed => ViewManager.Disposed;
+
+ public IObservable LifecycleEvents => ViewManager.LifecycleEvents;
+
+ protected PageBase()
+ : this(manuallyInitialize: true)
+ {
+ }
+
+ protected PageBase(
+ TViewModel? viewModel = null,
+ bool maintain = false,
+ bool delayBindingRegistrationUntilAttached = false,
+ bool manuallyInitialize = true)
+ {
+ Loaded += HandleLoaded;
+ Unloaded += HandleUnloaded;
+ DataContextChanged += HandleDataContextChanged;
+ RegisterPropertyChangedCallback(
+ ViewModelProperty,
+ (_, _) => ViewManager.PropertyChanged(this, nameof(ViewModel)));
+
+ if (!manuallyInitialize)
+ {
+ this.InitializeStellarComponent(viewModel, maintain, delayBindingRegistrationUntilAttached);
+ }
+ }
+
+ public virtual void Initialize()
+ {
+ }
+
+ public abstract void SetupUserInterface();
+
+ public abstract void Bind(WeakCompositeDisposable disposables);
+
+ protected override void OnNavigatedTo(NavigationEventArgs e)
+ {
+ base.OnNavigatedTo(e);
+
+ ViewManager.OnNavigating(this, NavigationEvent.NavigatedTo);
+ }
+
+ protected override void OnNavigatedFrom(NavigationEventArgs e)
+ {
+ ViewManager.OnNavigating(this, NavigationEvent.NavigatedFrom);
+
+ base.OnNavigatedFrom(e);
+ }
+
+ private void HandleLoaded(object sender, RoutedEventArgs e)
+ {
+ ViewManager.HandleActivated(this);
+
+ ViewManager.OnLifecycle(this, LifecycleEvent.IsAppearing);
+ }
+
+ private void HandleUnloaded(object sender, RoutedEventArgs e)
+ {
+ ViewManager.OnLifecycle(this, LifecycleEvent.IsDisappearing);
+
+ ViewManager.HandleDeactivated(this);
+ }
+
+ private void HandleDataContextChanged(FrameworkElement sender, DataContextChangedEventArgs args)
+ {
+ ViewModel = args.NewValue as TViewModel;
+ }
+}
diff --git a/Stellar.Uno/Stellar.Uno.csproj b/Stellar.Uno/Stellar.Uno.csproj
new file mode 100644
index 0000000..9a79e24
--- /dev/null
+++ b/Stellar.Uno/Stellar.Uno.csproj
@@ -0,0 +1,27 @@
+
+
+ net10.0
+ true
+ enable
+ enable
+ StellarUI.Uno
+ Stellar UI - Uno Platform
+ Eight-Bot
+ Eight-Bot 2026
+ https://github.com/TheEightBot/StellarUI/blob/main/LICENSE
+ https://github.com/TheEightBot/StellarUI
+ Uno Platform;MVVM;Reactive UI;Reactive Extensions;Eight-Bot
+
+
+
+
+
+
+
+
+
+
diff --git a/Stellar.Uno/UnoActivationForViewFetcher.cs b/Stellar.Uno/UnoActivationForViewFetcher.cs
new file mode 100644
index 0000000..e8916e3
--- /dev/null
+++ b/Stellar.Uno/UnoActivationForViewFetcher.cs
@@ -0,0 +1,37 @@
+using Microsoft.UI.Xaml;
+
+namespace Stellar.Uno;
+
+///
+/// Determines when Uno Platform views are activated and deactivated so that
+/// WhenActivated works for them. There is no ReactiveUI 24-compatible Uno
+/// platform package, so Stellar provides this itself. Views follow
+/// / .
+///
+public sealed class UnoActivationForViewFetcher : IActivationForViewFetcher
+{
+ public int GetAffinityForView(Type view) =>
+ typeof(FrameworkElement).IsAssignableFrom(view) ? 10 : 0;
+
+ public IObservable GetActivationForView(IActivatableView view)
+ {
+ if (view is not FrameworkElement element)
+ {
+ return Observable.Return(false);
+ }
+
+ var loaded = Observable
+ .FromEventPattern(
+ handler => element.Loaded += handler,
+ handler => element.Loaded -= handler)
+ .Select(static _ => true);
+
+ var unloaded = Observable
+ .FromEventPattern(
+ handler => element.Unloaded += handler,
+ handler => element.Unloaded -= handler)
+ .Select(static _ => false);
+
+ return loaded.Merge(unloaded).DistinctUntilChanged();
+ }
+}
diff --git a/Stellar.Uno/UnoScheduler.cs b/Stellar.Uno/UnoScheduler.cs
new file mode 100644
index 0000000..72d362f
--- /dev/null
+++ b/Stellar.Uno/UnoScheduler.cs
@@ -0,0 +1,80 @@
+using Microsoft.UI.Dispatching;
+
+namespace Stellar.Uno;
+
+///
+/// An that schedules work onto an Uno Platform UI thread via its
+/// . There is no ReactiveUI 24-compatible Uno platform
+/// package, so Stellar provides this itself. Work scheduled with no due time from the UI
+/// thread runs inline.
+///
+public sealed class UnoScheduler : IScheduler
+{
+ private readonly DispatcherQueue _dispatcherQueue;
+
+ public UnoScheduler(DispatcherQueue dispatcherQueue)
+ {
+ ArgumentNullException.ThrowIfNull(dispatcherQueue);
+
+ _dispatcherQueue = dispatcherQueue;
+ }
+
+ public DateTimeOffset Now => DateTimeOffset.Now;
+
+ public IDisposable Schedule(TState state, Func action)
+ {
+ var innerDisp = new SingleAssignmentDisposable();
+
+ if (_dispatcherQueue.HasThreadAccess)
+ {
+ innerDisp.Disposable = action(this, state);
+
+ return innerDisp;
+ }
+
+ _dispatcherQueue.TryEnqueue(
+ () =>
+ {
+ if (!innerDisp.IsDisposed)
+ {
+ innerDisp.Disposable = action(this, state);
+ }
+ });
+
+ return innerDisp;
+ }
+
+ public IDisposable Schedule(TState state, TimeSpan dueTime, Func action)
+ {
+ if (dueTime <= TimeSpan.Zero)
+ {
+ return Schedule(state, action);
+ }
+
+ var innerDisp = new SingleAssignmentDisposable();
+ var timer = _dispatcherQueue.CreateTimer();
+ timer.Interval = dueTime;
+ timer.IsRepeating = false;
+ timer.Tick += (_, _) =>
+ {
+ if (!innerDisp.IsDisposed)
+ {
+ innerDisp.Disposable = action(this, state);
+ }
+ };
+ timer.Start();
+
+ return new CompositeDisposable(
+ Disposable.Create(timer, static t => t.Stop()),
+ innerDisp);
+ }
+
+ public IDisposable Schedule(TState state, DateTimeOffset dueTime, Func action)
+ {
+ var relative = dueTime - Now;
+
+ return relative <= TimeSpan.Zero
+ ? Schedule(state, action)
+ : Schedule(state, relative, action);
+ }
+}
diff --git a/Stellar.Uno/UnoViewManager.cs b/Stellar.Uno/UnoViewManager.cs
new file mode 100644
index 0000000..c182378
--- /dev/null
+++ b/Stellar.Uno/UnoViewManager.cs
@@ -0,0 +1,66 @@
+using Microsoft.UI.Xaml;
+
+namespace Stellar.Uno;
+
+public class UnoViewManager : ViewManager
+ where TViewModel : class
+{
+ // Null whenever no view is activated, which HandleDeactivated relies on.
+ private IStellarView? _view;
+
+ public override void HandleActivated(IStellarView view)
+ {
+ base.HandleActivated(view);
+
+ if (HotReloadService.HotReloadAware)
+ {
+ _view = view;
+ HotReloadService.UpdateApplicationEvent -= HandleHotReload;
+ HotReloadService.UpdateApplicationEvent += HandleHotReload;
+ }
+ }
+
+ public override void HandleDeactivated(IStellarView view)
+ {
+ if (HotReloadService.HotReloadAware)
+ {
+ _view = null;
+ HotReloadService.UpdateApplicationEvent -= HandleHotReload;
+ }
+
+ base.HandleDeactivated(view);
+ }
+
+#pragma warning disable CS8632 // The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
+ private void HandleHotReload(Type[]? updatedTypes)
+#pragma warning restore CS8632 // The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
+ {
+ if (_view is not FrameworkElement fe)
+ {
+ return;
+ }
+
+ fe.DispatcherQueue.TryEnqueue(
+ () =>
+ {
+ var view = _view;
+
+ if (view is null)
+ {
+ return;
+ }
+
+ var maintainStatus = view.ViewManager.Maintain;
+
+ view.ViewManager.Maintain = false;
+
+ view.ViewManager.UnregisterBindings(view);
+
+ view.ViewManager.Maintain = maintainStatus;
+
+ view.SetupUserInterface();
+
+ view.ViewManager.RegisterBindings(view);
+ });
+ }
+}
diff --git a/Stellar.Uno/UserControlBase.cs b/Stellar.Uno/UserControlBase.cs
new file mode 100644
index 0000000..0a02f52
--- /dev/null
+++ b/Stellar.Uno/UserControlBase.cs
@@ -0,0 +1,94 @@
+using System.ComponentModel;
+using Microsoft.UI.Xaml;
+using Microsoft.UI.Xaml.Controls;
+
+namespace Stellar.Uno;
+
+// Implements IViewFor directly: there is no ReactiveUI 24-compatible Uno platform
+// package to supply a ReactiveUserControl base class, so Stellar provides the ViewModel
+// dependency property and DataContext syncing itself.
+public abstract class UserControlBase : UserControl, IStellarView
+ where TViewModel : class
+{
+ public static readonly DependencyProperty ViewModelProperty = DependencyProperty.Register(
+ nameof(ViewModel),
+ typeof(TViewModel),
+ typeof(UserControlBase),
+ new PropertyMetadata(null));
+
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public ViewManager ViewManager { get; } = new UnoViewManager();
+
+ public TViewModel? ViewModel
+ {
+ get => (TViewModel?)GetValue(ViewModelProperty);
+ set => SetValue(ViewModelProperty, value);
+ }
+
+ object? IViewFor.ViewModel
+ {
+ get => ViewModel;
+ set => ViewModel = value as TViewModel;
+ }
+
+ public IObservable UserControlInitialized => ViewManager.Initialized;
+
+ public IObservable Activated => ViewManager.Activated;
+
+ public IObservable Deactivated => ViewManager.Deactivated;
+
+ public IObservable Disposed => ViewManager.Disposed;
+
+ public IObservable LifecycleEvents => ViewManager.LifecycleEvents;
+
+ protected UserControlBase()
+ : this(manuallyInitialize: true)
+ {
+ }
+
+ protected UserControlBase(
+ TViewModel? viewModel = null,
+ bool maintain = false,
+ bool delayBindingRegistrationUntilAttached = false,
+ bool manuallyInitialize = true)
+ {
+ Loaded += HandleLoaded;
+ Unloaded += HandleUnloaded;
+ DataContextChanged += HandleDataContextChanged;
+ RegisterPropertyChangedCallback(
+ ViewModelProperty,
+ (_, _) => ViewManager.PropertyChanged(this, nameof(ViewModel)));
+
+ if (!manuallyInitialize)
+ {
+ this.InitializeStellarComponent(viewModel, maintain, delayBindingRegistrationUntilAttached);
+ }
+ }
+
+ public virtual void Initialize()
+ {
+ }
+
+ public abstract void SetupUserInterface();
+
+ public abstract void Bind(WeakCompositeDisposable disposables);
+
+ private void HandleLoaded(object sender, RoutedEventArgs e)
+ {
+ ViewManager.HandleActivated(this);
+
+ ViewManager.OnLifecycle(this, LifecycleEvent.IsAppearing);
+ }
+
+ private void HandleUnloaded(object sender, RoutedEventArgs e)
+ {
+ ViewManager.OnLifecycle(this, LifecycleEvent.IsDisappearing);
+
+ ViewManager.HandleDeactivated(this);
+ }
+
+ private void HandleDataContextChanged(FrameworkElement sender, DataContextChangedEventArgs args)
+ {
+ ViewModel = args.NewValue as TViewModel;
+ }
+}
diff --git a/Stellar.WinUI/Extensions/BuilderExtensions.cs b/Stellar.WinUI/Extensions/BuilderExtensions.cs
new file mode 100644
index 0000000..2157fb8
--- /dev/null
+++ b/Stellar.WinUI/Extensions/BuilderExtensions.cs
@@ -0,0 +1,39 @@
+using System.Reflection;
+using Microsoft.Extensions.DependencyInjection;
+using ReactiveUI.Builder;
+using ReactiveUI.Reactive.Builder;
+
+namespace Stellar.WinUI;
+
+public static class BuilderExtensions
+{
+ public static IServiceCollection UseStellarComponents(this IServiceCollection services)
+ {
+ UseStellarComponents(services);
+
+ services.ConfigureStellarComponents(typeof(TStellarAssembly).GetTypeInfo().Assembly);
+
+ return services;
+ }
+
+ public static IServiceCollection UseStellarComponents(this IServiceCollection services)
+ {
+ // WithWinUI registers the WinUI platform services (activation fetcher,
+ // main-thread scheduler, binding hooks) that ReactiveUI's old
+ // InitializeReactiveUI selected.
+ RxAppBuilder
+ .CreateReactiveUIBuilder()
+ .WithWinUI()
+ .WithTaskPoolScheduler(Schedulers.ShortTermThreadPoolScheduler)
+ .Build();
+
+ return services;
+ }
+
+ public static IServiceCollection EnableHotReload(this IServiceCollection services)
+ {
+ HotReloadService.HotReloadAware = true;
+
+ return services;
+ }
+}
diff --git a/Stellar.WinUI/GlobalUsings.cs b/Stellar.WinUI/GlobalUsings.cs
new file mode 100644
index 0000000..bb463e0
--- /dev/null
+++ b/Stellar.WinUI/GlobalUsings.cs
@@ -0,0 +1,8 @@
+global using System;
+global using System.Reactive;
+global using System.Reactive.Concurrency;
+global using System.Reactive.Disposables;
+global using System.Reactive.Linq;
+global using System.Reactive.Subjects;
+global using ReactiveUI;
+global using ReactiveUI.Reactive;
diff --git a/Stellar.WinUI/HotReloadService.cs b/Stellar.WinUI/HotReloadService.cs
new file mode 100644
index 0000000..11c0a0c
--- /dev/null
+++ b/Stellar.WinUI/HotReloadService.cs
@@ -0,0 +1,21 @@
+#pragma warning disable CS8632 // The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
+[assembly: System.Reflection.Metadata.MetadataUpdateHandler(typeof(Stellar.WinUI.HotReloadService))]
+
+namespace Stellar.WinUI;
+
+public static class HotReloadService
+{
+ public static bool HotReloadAware { get; set; }
+
+ public static event Action? UpdateApplicationEvent;
+
+ internal static void ClearCache(Type[]? types)
+ {
+ }
+
+ internal static void UpdateApplication(Type[]? types)
+ {
+ UpdateApplicationEvent?.Invoke(types);
+ }
+}
+#pragma warning restore CS8632 // The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
diff --git a/Stellar.WinUI/PageBase.cs b/Stellar.WinUI/PageBase.cs
new file mode 100644
index 0000000..44616a9
--- /dev/null
+++ b/Stellar.WinUI/PageBase.cs
@@ -0,0 +1,92 @@
+using System.ComponentModel;
+using Microsoft.UI.Xaml;
+using Microsoft.UI.Xaml.Navigation;
+
+namespace Stellar.WinUI;
+
+// ReactivePage supplies the ViewModel dependency property and IViewFor plumbing; Stellar
+// layers its ViewManager lifecycle on top. WinUI has no all-property change virtual, so
+// ViewModel changes are forwarded through RegisterPropertyChangedCallback instead.
+public abstract class PageBase : ReactivePage, IStellarView
+ where TViewModel : class
+{
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public ViewManager ViewManager { get; } = new WinUIViewManager();
+
+ public IObservable PageInitialized => ViewManager.Initialized;
+
+ public IObservable Activated => ViewManager.Activated;
+
+ public IObservable Deactivated => ViewManager.Deactivated;
+
+ public IObservable IsAppearing => ViewManager.IsAppearing;
+
+ public IObservable IsDisappearing => ViewManager.IsDisappearing;
+
+ public IObservable NavigatedTo => ViewManager.NavigatedTo;
+
+ public IObservable NavigatedFrom => ViewManager.NavigatedFrom;
+
+ public IObservable Disposed => ViewManager.Disposed;
+
+ public IObservable LifecycleEvents => ViewManager.LifecycleEvents;
+
+ protected PageBase()
+ : this(manuallyInitialize: true)
+ {
+ }
+
+ protected PageBase(
+ TViewModel? viewModel = null,
+ bool maintain = false,
+ bool delayBindingRegistrationUntilAttached = false,
+ bool manuallyInitialize = true)
+ {
+ Loaded += HandleLoaded;
+ Unloaded += HandleUnloaded;
+ RegisterPropertyChangedCallback(
+ ViewModelProperty,
+ (_, _) => ViewManager.PropertyChanged(this, nameof(ViewModel)));
+
+ if (!manuallyInitialize)
+ {
+ this.InitializeStellarComponent(viewModel, maintain, delayBindingRegistrationUntilAttached);
+ }
+ }
+
+ public virtual void Initialize()
+ {
+ }
+
+ public abstract void SetupUserInterface();
+
+ public abstract void Bind(WeakCompositeDisposable disposables);
+
+ protected override void OnNavigatedTo(NavigationEventArgs e)
+ {
+ base.OnNavigatedTo(e);
+
+ ViewManager.OnNavigating(this, NavigationEvent.NavigatedTo);
+ }
+
+ protected override void OnNavigatedFrom(NavigationEventArgs e)
+ {
+ ViewManager.OnNavigating(this, NavigationEvent.NavigatedFrom);
+
+ base.OnNavigatedFrom(e);
+ }
+
+ private void HandleLoaded(object sender, RoutedEventArgs e)
+ {
+ ViewManager.HandleActivated(this);
+
+ ViewManager.OnLifecycle(this, LifecycleEvent.IsAppearing);
+ }
+
+ private void HandleUnloaded(object sender, RoutedEventArgs e)
+ {
+ ViewManager.OnLifecycle(this, LifecycleEvent.IsDisappearing);
+
+ ViewManager.HandleDeactivated(this);
+ }
+}
diff --git a/Stellar.WinUI/Stellar.WinUI.csproj b/Stellar.WinUI/Stellar.WinUI.csproj
new file mode 100644
index 0000000..5635d79
--- /dev/null
+++ b/Stellar.WinUI/Stellar.WinUI.csproj
@@ -0,0 +1,32 @@
+
+
+ net10.0-windows10.0.19041.0
+ true
+
+ true
+ false
+ false
+ false
+ true
+ enable
+ enable
+ StellarUI.WinUI
+ Stellar UI - WinUI 3
+ Eight-Bot
+ Eight-Bot 2026
+ https://github.com/TheEightBot/StellarUI/blob/main/LICENSE
+ https://github.com/TheEightBot/StellarUI
+ WinUI;Windows App SDK;MVVM;Reactive UI;Reactive Extensions;Eight-Bot
+
+
+
+
+
+
+
+
+
diff --git a/Stellar.WinUI/UserControlBase.cs b/Stellar.WinUI/UserControlBase.cs
new file mode 100644
index 0000000..f9a4cb1
--- /dev/null
+++ b/Stellar.WinUI/UserControlBase.cs
@@ -0,0 +1,69 @@
+using System.ComponentModel;
+using Microsoft.UI.Xaml;
+
+namespace Stellar.WinUI;
+
+// ReactiveUserControl supplies the ViewModel dependency property and IViewFor plumbing;
+// Stellar layers its ViewManager lifecycle on top. WinUI has no all-property change
+// virtual, so ViewModel changes are forwarded through RegisterPropertyChangedCallback.
+public abstract class UserControlBase : ReactiveUserControl, IStellarView
+ where TViewModel : class
+{
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public ViewManager ViewManager { get; } = new WinUIViewManager();
+
+ public IObservable UserControlInitialized => ViewManager.Initialized;
+
+ public IObservable Activated => ViewManager.Activated;
+
+ public IObservable Deactivated => ViewManager.Deactivated;
+
+ public IObservable Disposed => ViewManager.Disposed;
+
+ public IObservable LifecycleEvents => ViewManager.LifecycleEvents;
+
+ protected UserControlBase()
+ : this(manuallyInitialize: true)
+ {
+ }
+
+ protected UserControlBase(
+ TViewModel? viewModel = null,
+ bool maintain = false,
+ bool delayBindingRegistrationUntilAttached = false,
+ bool manuallyInitialize = true)
+ {
+ Loaded += HandleLoaded;
+ Unloaded += HandleUnloaded;
+ RegisterPropertyChangedCallback(
+ ViewModelProperty,
+ (_, _) => ViewManager.PropertyChanged(this, nameof(ViewModel)));
+
+ if (!manuallyInitialize)
+ {
+ this.InitializeStellarComponent(viewModel, maintain, delayBindingRegistrationUntilAttached);
+ }
+ }
+
+ public virtual void Initialize()
+ {
+ }
+
+ public abstract void SetupUserInterface();
+
+ public abstract void Bind(WeakCompositeDisposable disposables);
+
+ private void HandleLoaded(object sender, RoutedEventArgs e)
+ {
+ ViewManager.HandleActivated(this);
+
+ ViewManager.OnLifecycle(this, LifecycleEvent.IsAppearing);
+ }
+
+ private void HandleUnloaded(object sender, RoutedEventArgs e)
+ {
+ ViewManager.OnLifecycle(this, LifecycleEvent.IsDisappearing);
+
+ ViewManager.HandleDeactivated(this);
+ }
+}
diff --git a/Stellar.WinUI/WinUIViewManager.cs b/Stellar.WinUI/WinUIViewManager.cs
new file mode 100644
index 0000000..863ee8e
--- /dev/null
+++ b/Stellar.WinUI/WinUIViewManager.cs
@@ -0,0 +1,66 @@
+using Microsoft.UI.Xaml;
+
+namespace Stellar.WinUI;
+
+public class WinUIViewManager : ViewManager
+ where TViewModel : class
+{
+ // Null whenever no view is activated, which HandleDeactivated relies on.
+ private IStellarView? _view;
+
+ public override void HandleActivated(IStellarView view)
+ {
+ base.HandleActivated(view);
+
+ if (HotReloadService.HotReloadAware)
+ {
+ _view = view;
+ HotReloadService.UpdateApplicationEvent -= HandleHotReload;
+ HotReloadService.UpdateApplicationEvent += HandleHotReload;
+ }
+ }
+
+ public override void HandleDeactivated(IStellarView view)
+ {
+ if (HotReloadService.HotReloadAware)
+ {
+ _view = null;
+ HotReloadService.UpdateApplicationEvent -= HandleHotReload;
+ }
+
+ base.HandleDeactivated(view);
+ }
+
+#pragma warning disable CS8632 // The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
+ private void HandleHotReload(Type[]? updatedTypes)
+#pragma warning restore CS8632 // The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
+ {
+ if (_view is not FrameworkElement fe)
+ {
+ return;
+ }
+
+ fe.DispatcherQueue.TryEnqueue(
+ () =>
+ {
+ var view = _view;
+
+ if (view is null)
+ {
+ return;
+ }
+
+ var maintainStatus = view.ViewManager.Maintain;
+
+ view.ViewManager.Maintain = false;
+
+ view.ViewManager.UnregisterBindings(view);
+
+ view.ViewManager.Maintain = maintainStatus;
+
+ view.SetupUserInterface();
+
+ view.ViewManager.RegisterBindings(view);
+ });
+ }
+}
diff --git a/Stellar.Wpf/Extensions/BuilderExtensions.cs b/Stellar.Wpf/Extensions/BuilderExtensions.cs
new file mode 100644
index 0000000..6ad227c
--- /dev/null
+++ b/Stellar.Wpf/Extensions/BuilderExtensions.cs
@@ -0,0 +1,37 @@
+using System.Reflection;
+using Microsoft.Extensions.DependencyInjection;
+using ReactiveUI.Reactive.Builder;
+
+namespace Stellar.Wpf;
+
+public static class BuilderExtensions
+{
+ public static IServiceCollection UseStellarComponents(this IServiceCollection services)
+ {
+ UseStellarComponents(services);
+
+ services.ConfigureStellarComponents(typeof(TStellarAssembly).GetTypeInfo().Assembly);
+
+ return services;
+ }
+
+ public static IServiceCollection UseStellarComponents(this IServiceCollection services)
+ {
+ // WithWpf registers the WPF platform services (activation fetcher, main-thread
+ // scheduler, binding hooks) that ReactiveUI's old InitializeReactiveUI selected.
+ RxAppBuilder
+ .CreateReactiveUIBuilder()
+ .WithWpf()
+ .WithTaskPoolScheduler(Schedulers.ShortTermThreadPoolScheduler)
+ .Build();
+
+ return services;
+ }
+
+ public static IServiceCollection EnableHotReload(this IServiceCollection services)
+ {
+ HotReloadService.HotReloadAware = true;
+
+ return services;
+ }
+}
diff --git a/Stellar.Wpf/GlobalUsings.cs b/Stellar.Wpf/GlobalUsings.cs
new file mode 100644
index 0000000..bb463e0
--- /dev/null
+++ b/Stellar.Wpf/GlobalUsings.cs
@@ -0,0 +1,8 @@
+global using System;
+global using System.Reactive;
+global using System.Reactive.Concurrency;
+global using System.Reactive.Disposables;
+global using System.Reactive.Linq;
+global using System.Reactive.Subjects;
+global using ReactiveUI;
+global using ReactiveUI.Reactive;
diff --git a/Stellar.Wpf/HotReloadService.cs b/Stellar.Wpf/HotReloadService.cs
new file mode 100644
index 0000000..40771f2
--- /dev/null
+++ b/Stellar.Wpf/HotReloadService.cs
@@ -0,0 +1,21 @@
+#pragma warning disable CS8632 // The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
+[assembly: System.Reflection.Metadata.MetadataUpdateHandler(typeof(Stellar.Wpf.HotReloadService))]
+
+namespace Stellar.Wpf;
+
+public static class HotReloadService
+{
+ public static bool HotReloadAware { get; set; }
+
+ public static event Action? UpdateApplicationEvent;
+
+ internal static void ClearCache(Type[]? types)
+ {
+ }
+
+ internal static void UpdateApplication(Type[]? types)
+ {
+ UpdateApplicationEvent?.Invoke(types);
+ }
+}
+#pragma warning restore CS8632 // The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
diff --git a/Stellar.Wpf/Stellar.Wpf.csproj b/Stellar.Wpf/Stellar.Wpf.csproj
new file mode 100644
index 0000000..19bfeb8
--- /dev/null
+++ b/Stellar.Wpf/Stellar.Wpf.csproj
@@ -0,0 +1,28 @@
+
+
+
+ net10.0-windows10.0.19041.0
+ true
+
+ true
+ true
+ enable
+ enable
+ StellarUI.Wpf
+ Stellar UI - WPF
+ Eight-Bot
+ Eight-Bot 2026
+ https://github.com/TheEightBot/StellarUI/blob/main/LICENSE
+ https://github.com/TheEightBot/StellarUI
+ WPF;MVVM;Reactive UI;Reactive Extensions;Eight-Bot
+
+
+
+
+
+
+
+
diff --git a/Stellar.Wpf/UserControlBase.cs b/Stellar.Wpf/UserControlBase.cs
new file mode 100644
index 0000000..17e825d
--- /dev/null
+++ b/Stellar.Wpf/UserControlBase.cs
@@ -0,0 +1,137 @@
+using System.ComponentModel;
+using System.Windows;
+using ReactiveUI.Reactive;
+
+namespace Stellar.Wpf;
+
+// ReactiveUserControl supplies the ViewModel dependency property and IViewFor plumbing;
+// Stellar layers its ViewManager lifecycle on top. WPF has no virtual for Loaded/Unloaded,
+// so the constructor subscribes to the routed events instead.
+public abstract class UserControlBase : ReactiveUserControl, IStellarView
+ where TViewModel : class
+{
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public ViewManager ViewManager { get; } = new WpfViewManager();
+
+ public IObservable UserControlInitialized => ViewManager.Initialized;
+
+ public IObservable Activated => ViewManager.Activated;
+
+ public IObservable Deactivated => ViewManager.Deactivated;
+
+ public IObservable Disposed => ViewManager.Disposed;
+
+ public IObservable LifecycleEvents => ViewManager.LifecycleEvents;
+
+ protected UserControlBase()
+ : this(manuallyInitialize: true)
+ {
+ }
+
+ protected UserControlBase(
+ TViewModel? viewModel = null,
+ bool maintain = false,
+ bool delayBindingRegistrationUntilAttached = false,
+ bool manuallyInitialize = true)
+ {
+ Loaded += HandleLoaded;
+ Unloaded += HandleUnloaded;
+
+ if (!manuallyInitialize)
+ {
+ this.InitializeStellarComponent(viewModel, maintain, delayBindingRegistrationUntilAttached);
+ }
+ }
+
+ public virtual void Initialize()
+ {
+ }
+
+ public abstract void SetupUserInterface();
+
+ public abstract void Bind(WeakCompositeDisposable disposables);
+
+ protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
+ {
+ ViewManager.PropertyChanged(this, e.Property.Name);
+
+ base.OnPropertyChanged(e);
+ }
+
+ private void HandleLoaded(object sender, RoutedEventArgs e)
+ {
+ ViewManager.HandleActivated(this);
+
+ ViewManager.OnLifecycle(this, LifecycleEvent.IsAppearing);
+ }
+
+ private void HandleUnloaded(object sender, RoutedEventArgs e)
+ {
+ ViewManager.OnLifecycle(this, LifecycleEvent.IsDisappearing);
+
+ ViewManager.HandleDeactivated(this);
+ }
+}
+
+public abstract class UserControlBase : ReactiveUserControl, IStellarView
+ where TViewModel : class
+{
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public ViewManager ViewManager { get; } = new WpfViewManager();
+
+ public IObservable UserControlInitialized => ViewManager.Initialized;
+
+ public IObservable Activated => ViewManager.Activated;
+
+ public IObservable Deactivated => ViewManager.Deactivated;
+
+ public IObservable Disposed => ViewManager.Disposed;
+
+ public IObservable LifecycleEvents => ViewManager.LifecycleEvents;
+
+ protected UserControlBase()
+ {
+ Loaded += HandleLoaded;
+ Unloaded += HandleUnloaded;
+ DataContextChanged += HandleDataContextChanged;
+ }
+
+ public virtual void Initialize()
+ {
+ }
+
+ public abstract void SetupUserInterface();
+
+ public abstract void Bind(WeakCompositeDisposable disposables);
+
+ protected abstract void MapDataModelToViewModel(TViewModel viewModel, TDataModel dataModel);
+
+ protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
+ {
+ ViewManager.PropertyChanged(this, e.Property.Name);
+
+ base.OnPropertyChanged(e);
+ }
+
+ private void HandleLoaded(object sender, RoutedEventArgs e)
+ {
+ ViewManager.HandleActivated(this);
+
+ ViewManager.OnLifecycle(this, LifecycleEvent.IsAppearing);
+ }
+
+ private void HandleUnloaded(object sender, RoutedEventArgs e)
+ {
+ ViewManager.OnLifecycle(this, LifecycleEvent.IsDisappearing);
+
+ ViewManager.HandleDeactivated(this);
+ }
+
+ private void HandleDataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
+ {
+ if (ViewModel is not null && e.NewValue is TDataModel dataModel)
+ {
+ MapDataModelToViewModel(ViewModel, dataModel);
+ }
+ }
+}
diff --git a/Stellar.Wpf/WindowBase.cs b/Stellar.Wpf/WindowBase.cs
new file mode 100644
index 0000000..d830653
--- /dev/null
+++ b/Stellar.Wpf/WindowBase.cs
@@ -0,0 +1,83 @@
+using System.ComponentModel;
+using System.Windows;
+using ReactiveUI.Reactive;
+
+namespace Stellar.Wpf;
+
+// ReactiveWindow supplies the ViewModel dependency property and IViewFor plumbing;
+// Stellar layers its ViewManager lifecycle on top, mirroring Stellar.Avalonia's WindowBase.
+public abstract class WindowBase : ReactiveWindow, IStellarView
+ where TViewModel : class
+{
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public ViewManager ViewManager { get; } = new WpfViewManager();
+
+ public IObservable WindowInitialized => ViewManager.Initialized;
+
+ public IObservable WindowActivated => ViewManager.Activated;
+
+ public IObservable WindowDeactivated => ViewManager.Deactivated;
+
+ public IObservable IsAppearing => ViewManager.IsAppearing;
+
+ public IObservable IsDisappearing => ViewManager.IsDisappearing;
+
+ public IObservable Disposed => ViewManager.Disposed;
+
+ public IObservable LifecycleEvents => ViewManager.LifecycleEvents;
+
+ protected WindowBase()
+ : this(manuallyInitialize: true)
+ {
+ }
+
+ protected WindowBase(
+ TViewModel? viewModel = null,
+ bool maintain = false,
+ bool delayBindingRegistrationUntilAttached = false,
+ bool manuallyInitialize = true)
+ {
+ if (!manuallyInitialize)
+ {
+ this.InitializeStellarComponent(viewModel, maintain, delayBindingRegistrationUntilAttached);
+ }
+ }
+
+ public virtual void Initialize()
+ {
+ }
+
+ public abstract void SetupUserInterface();
+
+ public abstract void Bind(WeakCompositeDisposable disposables);
+
+ protected override void OnInitialized(EventArgs e)
+ {
+ base.OnInitialized(e);
+
+ ViewManager.HandleActivated(this);
+ }
+
+ protected override void OnContentRendered(EventArgs e)
+ {
+ base.OnContentRendered(e);
+
+ ViewManager.OnLifecycle(this, LifecycleEvent.IsAppearing);
+ }
+
+ protected override void OnClosing(CancelEventArgs e)
+ {
+ ViewManager.OnLifecycle(this, LifecycleEvent.IsDisappearing);
+
+ ViewManager.HandleDeactivated(this);
+
+ base.OnClosing(e);
+ }
+
+ protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
+ {
+ ViewManager.PropertyChanged(this, e.Property.Name);
+
+ base.OnPropertyChanged(e);
+ }
+}
diff --git a/Stellar.Wpf/WpfViewManager.cs b/Stellar.Wpf/WpfViewManager.cs
new file mode 100644
index 0000000..4748c39
--- /dev/null
+++ b/Stellar.Wpf/WpfViewManager.cs
@@ -0,0 +1,60 @@
+using System.Windows;
+
+namespace Stellar.Wpf;
+
+public class WpfViewManager : ViewManager
+ where TViewModel : class
+{
+ // Null whenever no view is activated, which HandleDeactivated relies on.
+ private IStellarView? _view;
+
+ public override void HandleActivated(IStellarView view)
+ {
+ base.HandleActivated(view);
+
+ if (HotReloadService.HotReloadAware)
+ {
+ _view = view;
+ HotReloadService.UpdateApplicationEvent -= HandleHotReload;
+ HotReloadService.UpdateApplicationEvent += HandleHotReload;
+ }
+ }
+
+ public override void HandleDeactivated(IStellarView view)
+ {
+ if (HotReloadService.HotReloadAware)
+ {
+ _view = null;
+ HotReloadService.UpdateApplicationEvent -= HandleHotReload;
+ }
+
+ base.HandleDeactivated(view);
+ }
+
+#pragma warning disable CS8632 // The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
+ private void HandleHotReload(Type[]? updatedTypes)
+#pragma warning restore CS8632 // The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
+ {
+ if (_view is null)
+ {
+ return;
+ }
+
+ Application.Current?.Dispatcher
+ .Invoke(
+ () =>
+ {
+ var maintainStatus = _view.ViewManager.Maintain;
+
+ _view.ViewManager.Maintain = false;
+
+ _view.ViewManager.UnregisterBindings(_view);
+
+ _view.ViewManager.Maintain = maintainStatus;
+
+ _view.SetupUserInterface();
+
+ _view.ViewManager.RegisterBindings(_view);
+ });
+ }
+}
diff --git a/Stellar.sln b/Stellar.sln
index 6ae7901..2035b2e 100644
--- a/Stellar.sln
+++ b/Stellar.sln
@@ -49,6 +49,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Stellar.DiskDataCache", "St
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Stellar.SourceGenerators", "Stellar.SourceGenerators\Stellar.SourceGenerators.csproj", "{037D57FD-AB7C-4F69-8841-1A655FC86145}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Stellar.Wpf", "Stellar.Wpf\Stellar.Wpf.csproj", "{D3E861EB-BBD7-405F-AB85-04F928E39E38}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Stellar.WinUI", "Stellar.WinUI\Stellar.WinUI.csproj", "{B51B97EC-F16B-4092-950D-260D71CF7A65}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Stellar.Uno", "Stellar.Uno\Stellar.Uno.csproj", "{51D9C2E2-D338-45BF-B904-5C8B1DDF68FF}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -216,6 +222,42 @@ Global
{037D57FD-AB7C-4F69-8841-1A655FC86145}.Release|x64.Build.0 = Release|Any CPU
{037D57FD-AB7C-4F69-8841-1A655FC86145}.Release|x86.ActiveCfg = Release|Any CPU
{037D57FD-AB7C-4F69-8841-1A655FC86145}.Release|x86.Build.0 = Release|Any CPU
+ {D3E861EB-BBD7-405F-AB85-04F928E39E38}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D3E861EB-BBD7-405F-AB85-04F928E39E38}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D3E861EB-BBD7-405F-AB85-04F928E39E38}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {D3E861EB-BBD7-405F-AB85-04F928E39E38}.Debug|x64.Build.0 = Debug|Any CPU
+ {D3E861EB-BBD7-405F-AB85-04F928E39E38}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {D3E861EB-BBD7-405F-AB85-04F928E39E38}.Debug|x86.Build.0 = Debug|Any CPU
+ {D3E861EB-BBD7-405F-AB85-04F928E39E38}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D3E861EB-BBD7-405F-AB85-04F928E39E38}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D3E861EB-BBD7-405F-AB85-04F928E39E38}.Release|x64.ActiveCfg = Release|Any CPU
+ {D3E861EB-BBD7-405F-AB85-04F928E39E38}.Release|x64.Build.0 = Release|Any CPU
+ {D3E861EB-BBD7-405F-AB85-04F928E39E38}.Release|x86.ActiveCfg = Release|Any CPU
+ {D3E861EB-BBD7-405F-AB85-04F928E39E38}.Release|x86.Build.0 = Release|Any CPU
+ {B51B97EC-F16B-4092-950D-260D71CF7A65}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B51B97EC-F16B-4092-950D-260D71CF7A65}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B51B97EC-F16B-4092-950D-260D71CF7A65}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {B51B97EC-F16B-4092-950D-260D71CF7A65}.Debug|x64.Build.0 = Debug|Any CPU
+ {B51B97EC-F16B-4092-950D-260D71CF7A65}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {B51B97EC-F16B-4092-950D-260D71CF7A65}.Debug|x86.Build.0 = Debug|Any CPU
+ {B51B97EC-F16B-4092-950D-260D71CF7A65}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B51B97EC-F16B-4092-950D-260D71CF7A65}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B51B97EC-F16B-4092-950D-260D71CF7A65}.Release|x64.ActiveCfg = Release|Any CPU
+ {B51B97EC-F16B-4092-950D-260D71CF7A65}.Release|x64.Build.0 = Release|Any CPU
+ {B51B97EC-F16B-4092-950D-260D71CF7A65}.Release|x86.ActiveCfg = Release|Any CPU
+ {B51B97EC-F16B-4092-950D-260D71CF7A65}.Release|x86.Build.0 = Release|Any CPU
+ {51D9C2E2-D338-45BF-B904-5C8B1DDF68FF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {51D9C2E2-D338-45BF-B904-5C8B1DDF68FF}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {51D9C2E2-D338-45BF-B904-5C8B1DDF68FF}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {51D9C2E2-D338-45BF-B904-5C8B1DDF68FF}.Debug|x64.Build.0 = Debug|Any CPU
+ {51D9C2E2-D338-45BF-B904-5C8B1DDF68FF}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {51D9C2E2-D338-45BF-B904-5C8B1DDF68FF}.Debug|x86.Build.0 = Debug|Any CPU
+ {51D9C2E2-D338-45BF-B904-5C8B1DDF68FF}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {51D9C2E2-D338-45BF-B904-5C8B1DDF68FF}.Release|Any CPU.Build.0 = Release|Any CPU
+ {51D9C2E2-D338-45BF-B904-5C8B1DDF68FF}.Release|x64.ActiveCfg = Release|Any CPU
+ {51D9C2E2-D338-45BF-B904-5C8B1DDF68FF}.Release|x64.Build.0 = Release|Any CPU
+ {51D9C2E2-D338-45BF-B904-5C8B1DDF68FF}.Release|x86.ActiveCfg = Release|Any CPU
+ {51D9C2E2-D338-45BF-B904-5C8B1DDF68FF}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/Stellar.slnf b/Stellar.slnf
index 7ab2dc8..2417a5b 100644
--- a/Stellar.slnf
+++ b/Stellar.slnf
@@ -10,7 +10,10 @@
"Stellar.DiskDataCache\\Stellar.DiskDataCache.csproj",
"Stellar.FluentValidation\\Stellar.FluentValidation.csproj",
"Stellar.SourceGenerators\\Stellar.SourceGenerators.csproj",
- "Stellar.UnitTests\\Stellar.UnitTests.csproj"
+ "Stellar.UnitTests\\Stellar.UnitTests.csproj",
+ "Stellar.Uno\\Stellar.Uno.csproj",
+ "Stellar.WinUI\\Stellar.WinUI.csproj",
+ "Stellar.Wpf\\Stellar.Wpf.csproj"
]
}
}
diff --git a/Stellar/GlobalUsings.cs b/Stellar/GlobalUsings.cs
index b2683af..c41d875 100644
--- a/Stellar/GlobalUsings.cs
+++ b/Stellar/GlobalUsings.cs
@@ -6,3 +6,4 @@
global using System.Reactive.Linq;
global using ReactiveGenerator;
global using ReactiveUI;
+global using ReactiveUI.Reactive;
diff --git a/Stellar/Stellar.csproj b/Stellar/Stellar.csproj
index 7a47009..eda9faf 100644
--- a/Stellar/Stellar.csproj
+++ b/Stellar/Stellar.csproj
@@ -18,7 +18,8 @@
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
+