diff --git a/.ncrunch/ReactiveUIDemo.v3.ncrunchproject b/.ncrunch/ReactiveUIDemo.v3.ncrunchproject
new file mode 100644
index 0000000000..319cd523ce
--- /dev/null
+++ b/.ncrunch/ReactiveUIDemo.v3.ncrunchproject
@@ -0,0 +1,5 @@
+
+
+ True
+
+
\ No newline at end of file
diff --git a/Avalonia.Desktop.slnf b/Avalonia.Desktop.slnf
index 6ba05332be..3fa8e969c8 100644
--- a/Avalonia.Desktop.slnf
+++ b/Avalonia.Desktop.slnf
@@ -9,6 +9,7 @@
"samples\\MiniMvvm\\MiniMvvm.csproj",
"samples\\SampleControls\\ControlSamples.csproj",
"samples\\Sandbox\\Sandbox.csproj",
+ "samples\\ReactiveUIDemo\\ReactiveUIDemo.csproj",
"src\\Avalonia.Base\\Avalonia.Base.csproj",
"src\\Avalonia.Build.Tasks\\Avalonia.Build.Tasks.csproj",
"src\\Avalonia.Controls.ColorPicker\\Avalonia.Controls.ColorPicker.csproj",
diff --git a/Avalonia.sln b/Avalonia.sln
index 34b5596119..63b331ec08 100644
--- a/Avalonia.sln
+++ b/Avalonia.sln
@@ -230,6 +230,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ControlCatalog.Browser", "s
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ControlCatalog.Browser.Blazor", "samples\ControlCatalog.Browser.Blazor\ControlCatalog.Browser.Blazor.csproj", "{90B08091-9BBD-4362-B712-E9F2CC62B218}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ReactiveUIDemo", "samples\ReactiveUIDemo\ReactiveUIDemo.csproj", "{75C47156-C5D8-44BC-A5A7-E8657C2248D6}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -541,6 +543,10 @@ Global
{90B08091-9BBD-4362-B712-E9F2CC62B218}.Debug|Any CPU.Build.0 = Debug|Any CPU
{90B08091-9BBD-4362-B712-E9F2CC62B218}.Release|Any CPU.ActiveCfg = Release|Any CPU
{90B08091-9BBD-4362-B712-E9F2CC62B218}.Release|Any CPU.Build.0 = Release|Any CPU
+ {75C47156-C5D8-44BC-A5A7-E8657C2248D6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {75C47156-C5D8-44BC-A5A7-E8657C2248D6}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {75C47156-C5D8-44BC-A5A7-E8657C2248D6}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {75C47156-C5D8-44BC-A5A7-E8657C2248D6}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -605,6 +611,7 @@ Global
{47F8530C-F19B-4B1A-B4D6-EB231522AE5D} = {86A3F706-DC3C-43C6-BE1B-B98F5BAAA268}
{15B93A4C-1B46-43F6-B534-7B25B6E99932} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{90B08091-9BBD-4362-B712-E9F2CC62B218} = {9B9E3891-2366-4253-A952-D08BCEB71098}
+ {75C47156-C5D8-44BC-A5A7-E8657C2248D6} = {9B9E3891-2366-4253-A952-D08BCEB71098}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {87366D66-1391-4D90-8999-95A620AD786A}
diff --git a/samples/ReactiveUIDemo/App.axaml b/samples/ReactiveUIDemo/App.axaml
new file mode 100644
index 0000000000..dd3a39f6ac
--- /dev/null
+++ b/samples/ReactiveUIDemo/App.axaml
@@ -0,0 +1,8 @@
+
+
+
+
+
diff --git a/samples/ReactiveUIDemo/App.axaml.cs b/samples/ReactiveUIDemo/App.axaml.cs
new file mode 100644
index 0000000000..4578566427
--- /dev/null
+++ b/samples/ReactiveUIDemo/App.axaml.cs
@@ -0,0 +1,37 @@
+using Avalonia;
+using Avalonia.Controls.ApplicationLifetimes;
+using Avalonia.Markup.Xaml;
+using Avalonia.ReactiveUI;
+using ReactiveUI;
+using ReactiveUIDemo.ViewModels;
+using ReactiveUIDemo.Views;
+using Splat;
+
+namespace ReactiveUIDemo
+{
+ public class App : Application
+ {
+ public override void Initialize()
+ {
+ AvaloniaXamlLoader.Load(this);
+ Locator.CurrentMutable.Register(() => new FooView(), typeof(IViewFor));
+ Locator.CurrentMutable.Register(() => new BarView(), typeof(IViewFor));
+ }
+
+ public override void OnFrameworkInitializationCompleted()
+ {
+ if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
+ desktop.MainWindow = new MainWindow();
+ base.OnFrameworkInitializationCompleted();
+ }
+
+ public static int Main(string[] args)
+ => BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
+
+ public static AppBuilder BuildAvaloniaApp()
+ => AppBuilder.Configure()
+ .UsePlatformDetect()
+ .UseReactiveUI()
+ .LogToTrace();
+ }
+}
diff --git a/samples/ReactiveUIDemo/MainWindow.axaml b/samples/ReactiveUIDemo/MainWindow.axaml
new file mode 100644
index 0000000000..7775fc5a79
--- /dev/null
+++ b/samples/ReactiveUIDemo/MainWindow.axaml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/ReactiveUIDemo/MainWindow.axaml.cs b/samples/ReactiveUIDemo/MainWindow.axaml.cs
new file mode 100644
index 0000000000..5bf2d476fd
--- /dev/null
+++ b/samples/ReactiveUIDemo/MainWindow.axaml.cs
@@ -0,0 +1,22 @@
+using ReactiveUIDemo.ViewModels;
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+
+namespace ReactiveUIDemo
+{
+ public class MainWindow : Window
+ {
+ public MainWindow()
+ {
+ this.InitializeComponent();
+ this.DataContext = new MainWindowViewModel();
+ this.AttachDevTools();
+ }
+
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+ }
+}
diff --git a/samples/ReactiveUIDemo/ReactiveUIDemo.csproj b/samples/ReactiveUIDemo/ReactiveUIDemo.csproj
new file mode 100644
index 0000000000..94ca4ee809
--- /dev/null
+++ b/samples/ReactiveUIDemo/ReactiveUIDemo.csproj
@@ -0,0 +1,28 @@
+
+
+ Exe
+ net6.0
+ enable
+
+
+
+
+
+
+
+
+
+
+ BarView.axaml
+
+
+ FooView.axaml
+
+
+
+
+
+
+
+
+
diff --git a/samples/ReactiveUIDemo/ViewModels/BarViewModel.cs b/samples/ReactiveUIDemo/ViewModels/BarViewModel.cs
new file mode 100644
index 0000000000..3448453d81
--- /dev/null
+++ b/samples/ReactiveUIDemo/ViewModels/BarViewModel.cs
@@ -0,0 +1,11 @@
+using ReactiveUI;
+
+namespace ReactiveUIDemo.ViewModels
+{
+ internal class BarViewModel : ReactiveObject, IRoutableViewModel
+ {
+ public BarViewModel(IScreen screen) => HostScreen = screen;
+ public string UrlPathSegment => "Bar";
+ public IScreen HostScreen { get; }
+ }
+}
diff --git a/samples/ReactiveUIDemo/ViewModels/FooViewModel.cs b/samples/ReactiveUIDemo/ViewModels/FooViewModel.cs
new file mode 100644
index 0000000000..1a363e18dc
--- /dev/null
+++ b/samples/ReactiveUIDemo/ViewModels/FooViewModel.cs
@@ -0,0 +1,11 @@
+using ReactiveUI;
+
+namespace ReactiveUIDemo.ViewModels
+{
+ internal class FooViewModel : ReactiveObject, IRoutableViewModel
+ {
+ public FooViewModel(IScreen screen) => HostScreen = screen;
+ public string UrlPathSegment => "Foo";
+ public IScreen HostScreen { get; }
+ }
+}
diff --git a/samples/ReactiveUIDemo/ViewModels/MainWindowViewModel.cs b/samples/ReactiveUIDemo/ViewModels/MainWindowViewModel.cs
new file mode 100644
index 0000000000..2222137d38
--- /dev/null
+++ b/samples/ReactiveUIDemo/ViewModels/MainWindowViewModel.cs
@@ -0,0 +1,9 @@
+using ReactiveUI;
+
+namespace ReactiveUIDemo.ViewModels
+{
+ internal class MainWindowViewModel : ReactiveObject
+ {
+ public RoutedViewHostPageViewModel RoutedViewHost { get; } = new();
+ }
+}
diff --git a/samples/ReactiveUIDemo/ViewModels/RoutedViewHostPageViewModel.cs b/samples/ReactiveUIDemo/ViewModels/RoutedViewHostPageViewModel.cs
new file mode 100644
index 0000000000..701447cfe8
--- /dev/null
+++ b/samples/ReactiveUIDemo/ViewModels/RoutedViewHostPageViewModel.cs
@@ -0,0 +1,21 @@
+using ReactiveUI;
+
+namespace ReactiveUIDemo.ViewModels
+{
+ internal class RoutedViewHostPageViewModel : ReactiveObject, IScreen
+ {
+ public RoutedViewHostPageViewModel()
+ {
+ Foo = new(this);
+ Bar = new(this);
+ Router.Navigate.Execute(Foo);
+ }
+
+ public RoutingState Router { get; } = new();
+ public FooViewModel Foo { get; }
+ public BarViewModel Bar { get; }
+
+ public void ShowFoo() => Router.Navigate.Execute(Foo);
+ public void ShowBar() => Router.Navigate.Execute(Bar);
+ }
+}
diff --git a/samples/ReactiveUIDemo/Views/BarView.axaml b/samples/ReactiveUIDemo/Views/BarView.axaml
new file mode 100644
index 0000000000..2622245997
--- /dev/null
+++ b/samples/ReactiveUIDemo/Views/BarView.axaml
@@ -0,0 +1,16 @@
+
+
+
+ Bar!
+
+
+
diff --git a/samples/ReactiveUIDemo/Views/BarView.axaml.cs b/samples/ReactiveUIDemo/Views/BarView.axaml.cs
new file mode 100644
index 0000000000..2fbea6de91
--- /dev/null
+++ b/samples/ReactiveUIDemo/Views/BarView.axaml.cs
@@ -0,0 +1,28 @@
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+using ReactiveUI;
+using ReactiveUIDemo.ViewModels;
+
+namespace ReactiveUIDemo.Views
+{
+ internal partial class BarView : UserControl, IViewFor
+ {
+ public BarView()
+ {
+ InitializeComponent();
+ }
+
+ public BarViewModel? ViewModel { get; set; }
+
+ object? IViewFor.ViewModel
+ {
+ get => ViewModel;
+ set => ViewModel = (BarViewModel?)value;
+ }
+
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+ }
+}
diff --git a/samples/ReactiveUIDemo/Views/FooView.axaml b/samples/ReactiveUIDemo/Views/FooView.axaml
new file mode 100644
index 0000000000..8f73250d3b
--- /dev/null
+++ b/samples/ReactiveUIDemo/Views/FooView.axaml
@@ -0,0 +1,16 @@
+
+
+
+ Foo!
+
+
+
diff --git a/samples/ReactiveUIDemo/Views/FooView.axaml.cs b/samples/ReactiveUIDemo/Views/FooView.axaml.cs
new file mode 100644
index 0000000000..313a71044c
--- /dev/null
+++ b/samples/ReactiveUIDemo/Views/FooView.axaml.cs
@@ -0,0 +1,28 @@
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+using ReactiveUI;
+using ReactiveUIDemo.ViewModels;
+
+namespace ReactiveUIDemo.Views
+{
+ internal partial class FooView : UserControl, IViewFor
+ {
+ public FooView()
+ {
+ InitializeComponent();
+ }
+
+ public FooViewModel? ViewModel { get; set; }
+
+ object? IViewFor.ViewModel
+ {
+ get => ViewModel;
+ set => ViewModel = (FooViewModel?)value;
+ }
+
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+ }
+}