diff --git a/README.adoc b/README.adoc
index 97b18a5..d4d3a48 100644
--- a/README.adoc
+++ b/README.adoc
@@ -89,6 +89,10 @@ Each sample is tagged with it's difficulty. The degree of difficulty describes h
| 🐥 Easy
| MVVM, Data-Validation, Exception, Error, Error-Message, Binding
+| link:src/Avalonia.Samples/MVVM/MsExtensionsHostingSample[Avalonia and .NET Generic Host]
+| 🐥 Easy
+| .NET Generic Host, DI, Configuration, Logging, MVVM
+
|===
=== ✒️ Drawing-Samples
diff --git a/src/Avalonia.Samples/Avalonia.Samples.sln b/src/Avalonia.Samples/Avalonia.Samples.sln
index 5b9f577..9597075 100644
--- a/src/Avalonia.Samples/Avalonia.Samples.sln
+++ b/src/Avalonia.Samples/Avalonia.Samples.sln
@@ -48,6 +48,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestableApp.Headless.XUnit"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestableApp.Appium", "Testing\TestableApp.Appium\TestableApp.Appium.csproj", "{F5CB3DA2-EB59-4792-A1B3-49F600F7C130}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MsExtensionsHostingSample", "MVVM\MsExtensionsHostingSample\MsExtensionsHostingSample.csproj", "{062E5397-CB68-4B3B-9707-4A9B59BDCC1D}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -110,6 +112,10 @@ Global
{F5CB3DA2-EB59-4792-A1B3-49F600F7C130}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F5CB3DA2-EB59-4792-A1B3-49F600F7C130}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F5CB3DA2-EB59-4792-A1B3-49F600F7C130}.Release|Any CPU.Build.0 = Release|Any CPU
+ {062E5397-CB68-4B3B-9707-4A9B59BDCC1D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {062E5397-CB68-4B3B-9707-4A9B59BDCC1D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {062E5397-CB68-4B3B-9707-4A9B59BDCC1D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {062E5397-CB68-4B3B-9707-4A9B59BDCC1D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -129,6 +135,7 @@ Global
{B8CB5C57-07ED-4BC6-ACE8-F05E428E3EB5} = {85B157B3-F701-4F75-B4F1-EC2287729480}
{BDA7536E-26FD-436F-AAC8-F8A2B500548E} = {85B157B3-F701-4F75-B4F1-EC2287729480}
{F5CB3DA2-EB59-4792-A1B3-49F600F7C130} = {85B157B3-F701-4F75-B4F1-EC2287729480}
+ {062E5397-CB68-4B3B-9707-4A9B59BDCC1D} = {932FD4A5-FCE7-4428-A0C1-C0392D90A21A}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C246CAB0-0837-4EE4-A22D-28B3C74930B4}
diff --git a/src/Avalonia.Samples/MVVM/MsExtensionsHostingSample/App.axaml b/src/Avalonia.Samples/MVVM/MsExtensionsHostingSample/App.axaml
new file mode 100644
index 0000000..05a6019
--- /dev/null
+++ b/src/Avalonia.Samples/MVVM/MsExtensionsHostingSample/App.axaml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Avalonia.Samples/MVVM/MsExtensionsHostingSample/App.axaml.cs b/src/Avalonia.Samples/MVVM/MsExtensionsHostingSample/App.axaml.cs
new file mode 100644
index 0000000..b11a203
--- /dev/null
+++ b/src/Avalonia.Samples/MVVM/MsExtensionsHostingSample/App.axaml.cs
@@ -0,0 +1,69 @@
+using System;
+using Avalonia;
+using Avalonia.Controls.ApplicationLifetimes;
+using Avalonia.Markup.Xaml;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using MsExtensionsHostingSample.Models;
+using MsExtensionsHostingSample.Services;
+using MsExtensionsHostingSample.Services.Interfaces;
+using MsExtensionsHostingSample.ViewModels;
+using MsExtensionsHostingSample.Views;
+
+namespace MsExtensionsHostingSample;
+
+public partial class App : Application
+{
+ public IHost? GlobalHost { get; private set; }
+
+ public override void Initialize()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+
+ public override async void OnFrameworkInitializationCompleted()
+ {
+ var hostBuilder = CreateHostBuilder();
+ var host = hostBuilder.Build();
+ GlobalHost = host;
+
+ if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
+ {
+ desktop.MainWindow = new MainWindow
+ {
+ DataContext = GlobalHost.Services.GetRequiredService()
+ };
+ desktop.Exit += (sender, args) =>
+ {
+ GlobalHost.StopAsync(TimeSpan.FromSeconds(5)).GetAwaiter().GetResult();
+ GlobalHost.Dispose();
+ GlobalHost = null;
+ };
+ }
+
+ DataTemplates.Add(GlobalHost.Services.GetRequiredService());
+
+ base.OnFrameworkInitializationCompleted();
+
+ // Usually, we don't want to block main UI thread.
+ // But if it's required to start async services before we create any window,
+ // then don't set any MainWindow, and simply call Show() on a new window later after async initialization.
+ await host.StartAsync();
+ }
+
+ private static HostApplicationBuilder CreateHostBuilder()
+ {
+ // Alternatively, we can use Host.CreateDefaultBuilder, but this sample focuses on HostApplicationBuilder.
+ var builder = Host.CreateApplicationBuilder(Environment.GetCommandLineArgs());
+
+ builder.Services.AddOptions().Bind(builder.Configuration.GetSection("Weather"));
+ builder.Services.AddHostedService();
+
+ builder.Services.AddTransient();
+ builder.Services.AddTransient();
+ builder.Services.AddTransient();
+
+ builder.Services.AddView();
+ return builder;
+ }
+}
\ No newline at end of file
diff --git a/src/Avalonia.Samples/MVVM/MsExtensionsHostingSample/Assets/avalonia-logo.ico b/src/Avalonia.Samples/MVVM/MsExtensionsHostingSample/Assets/avalonia-logo.ico
new file mode 100644
index 0000000..da8d49f
Binary files /dev/null and b/src/Avalonia.Samples/MVVM/MsExtensionsHostingSample/Assets/avalonia-logo.ico differ
diff --git a/src/Avalonia.Samples/MVVM/MsExtensionsHostingSample/Models/DayReport.cs b/src/Avalonia.Samples/MVVM/MsExtensionsHostingSample/Models/DayReport.cs
new file mode 100644
index 0000000..ef48dc5
--- /dev/null
+++ b/src/Avalonia.Samples/MVVM/MsExtensionsHostingSample/Models/DayReport.cs
@@ -0,0 +1,11 @@
+using System;
+
+namespace MsExtensionsHostingSample.Models;
+
+public record DayReport(
+ DateOnly Date,
+ string WeatherCondition,
+ double Temperature,
+ string TemperatureUnit,
+ double RelativeHumidity,
+ double WindSpeed);
\ No newline at end of file
diff --git a/src/Avalonia.Samples/MVVM/MsExtensionsHostingSample/Models/WeatherSettings.cs b/src/Avalonia.Samples/MVVM/MsExtensionsHostingSample/Models/WeatherSettings.cs
new file mode 100644
index 0000000..92dc62b
--- /dev/null
+++ b/src/Avalonia.Samples/MVVM/MsExtensionsHostingSample/Models/WeatherSettings.cs
@@ -0,0 +1,6 @@
+namespace MsExtensionsHostingSample.Models;
+
+public class WeatherSettings
+{
+ public string Unit { get; set; } = "C";
+}
\ No newline at end of file
diff --git a/src/Avalonia.Samples/MVVM/MsExtensionsHostingSample/MsExtensionsHostingSample.csproj b/src/Avalonia.Samples/MVVM/MsExtensionsHostingSample/MsExtensionsHostingSample.csproj
new file mode 100644
index 0000000..1cb1d9c
--- /dev/null
+++ b/src/Avalonia.Samples/MVVM/MsExtensionsHostingSample/MsExtensionsHostingSample.csproj
@@ -0,0 +1,30 @@
+
+
+ WinExe
+ net7.0
+ enable
+ true
+ app.manifest
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Avalonia.Samples/MVVM/MsExtensionsHostingSample/Program.cs b/src/Avalonia.Samples/MVVM/MsExtensionsHostingSample/Program.cs
new file mode 100644
index 0000000..acbe98b
--- /dev/null
+++ b/src/Avalonia.Samples/MVVM/MsExtensionsHostingSample/Program.cs
@@ -0,0 +1,26 @@
+using Avalonia;
+using Avalonia.ReactiveUI;
+using System;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using MsExtensionsHostingSample.Services;
+
+namespace MsExtensionsHostingSample;
+
+class Program
+{
+ // Initialization code. Don't use any Avalonia, third-party APIs or any
+ // SynchronizationContext-reliant code before AppMain is called: things aren't initialized
+ // yet and stuff might break.
+ [STAThread]
+ public static void Main(string[] args) => BuildAvaloniaApp()
+ .StartWithClassicDesktopLifetime(args);
+
+ // Avalonia configuration, don't remove; also used by visual designer.
+ public static AppBuilder BuildAvaloniaApp()
+ => AppBuilder.Configure()
+ .UsePlatformDetect()
+ .WithInterFont()
+ .LogToTrace()
+ .UseReactiveUI();
+}
\ No newline at end of file
diff --git a/src/Avalonia.Samples/MVVM/MsExtensionsHostingSample/README.adoc b/src/Avalonia.Samples/MVVM/MsExtensionsHostingSample/README.adoc
new file mode 100644
index 0000000..092db66
--- /dev/null
+++ b/src/Avalonia.Samples/MVVM/MsExtensionsHostingSample/README.adoc
@@ -0,0 +1,233 @@
+= Avalonia and .NET Generic Host
+// --- D O N ' T T O U C H T H I S S E C T I O N ---
+:toc:
+:toc-placement!:
+:tip-caption: :bulb:
+:note-caption: :information_source:
+:important-caption: :heavy_exclamation_mark:
+:caution-caption: :fire:
+:warning-caption: :warning:
+// ----------------------------------------------------------
+
+
+
+// Write a short summary here what this examples does
+This example will show you how to integrate https://learn.microsoft.com/en-us/dotnet/core/extensions/generic-host[.NET Generic Host (Microsoft.Extensions.Hosting)] into Avalonia application and use some of extensions features.
+
+
+// --- D O N ' T T O U C H T H I S S E C T I O N ---
+toc::[]
+// ---------------------------------------------------------
+
+
+=== Difficulty
+// Choose one of the below difficulties. You can just delete the ones you don't need.
+
+🐥 Easy 🐥
+
+
+=== Buzz-Words
+
+// Write some buzz-words here. You can separate them by ", "
+.NET Generic Host, DI, Configuration, Logging, MVVM
+
+
+== Before we start
+
+This example will work from `avalonia.mvvm` template. This document won't go into details of specific views and view models. However, C# project in this folder has a simple Weather Report app that uses all of mentioned below technics.
+
+== Basic configuration
+
+With typical console or web application developers expect that .NET Generic Host is built and run directly from the Main method. But it's not really the case with GUI applications, as we don't have much control over platform rules:
+
+- Avalonia already has its own AppBuilder, and Main method is not used at all with XAML Previewer.
+- macOS apps needs to have sync Main method, so we can't run async there.
+- Browser Main method has to be async in Avalonia and it exists without closing the app.
+- Android apps don't even have a Main method.
+- Full-featured .NET Generic Host expects a strict lifetime with events of application closing.
+
+As a result, it is recommended to keep program entry point as is, and instead setup .NET Generic Host integration from the Avalonia Application class.
+
+Let's start with updating `OnFrameworkInitializationCompleted` method in `App.xaml.cs` file:
+
+[source,c#]
+----
+ public override void OnFrameworkInitializationCompleted()
+ {
+ var builder = Host.CreateApplicationBuilder(Environment.GetCommandLineArgs());
+ builder.Services.AddTransient();
+ var host = builder.Build();
+
+ if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
+ {
+ desktop.MainWindow = new MainWindow
+ {
+ DataContext = host.Services.GetRequiredService()
+ };
+ desktop.Exit += (sender, args) =>
+ {
+ host.Dispose();
+ };
+ }
+
+ base.OnFrameworkInitializationCompleted();
+ }
+----
+
+Essentially, this is a minimal working integration of .NET Generic Host with Avalonia desktop app.
+With this, it's possible to register services in the host builder, use logger, read host configuration files and options.
+
+MainWindowViewModel is registered as a transient service, which means we now use Dependency Injection into our view models.
+
+But in order to get more features from the Generic Host, we will go into details below.
+
+== Managing Host lifetime
+
+In previous example we have not called Start or Stop methods on the Host object. It is enough for basic usage, but if application needs to run hosted services it is important to do some modifications.
+
+To start the host, we can run host.StartAsync() at some point after MainWindow was set. For example, in the end of the OnFrameworkInitializationCompleted method. And we can stop the host in Exit event handler right before disposing it:
+
+[source,c#]
+----
+ public override async void OnFrameworkInitializationCompleted()
+ {
+ var builder = Host.CreateApplicationBuilder(Environment.GetCommandLineArgs());
+ builder.Services.AddTransient();
+ builder.Services.AddHostedService();
+ var host = builder.Build();
+
+ if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
+ {
+ desktop.MainWindow = new MainWindow
+ {
+ DataContext = host.Services.GetRequiredService()
+ };
+ desktop.Exit += (sender, args) =>
+ {
+ host.StopAsync(TimeSpan.FromSeconds(5)).GetAwaiter().GetResult();
+ host.Dispose();
+ };
+ }
+
+ base.OnFrameworkInitializationCompleted();
+
+ await host.StartAsync();
+ }
+----
+
+New service was registered - `HostedBackgroundService`, that now can handle application start and stop events reported by the Generic Host.
+
+[source,c#]
+----
+public class HostedBackgroundService : IHostedService
+{
+ private readonly ILogger _logger;
+
+ public HostedBackgroundService(ILogger logger)
+ {
+ _logger = logger;
+ }
+
+ public async Task StartAsync(CancellationToken cancellationToken)
+ {
+ _logger.LogInformation("BackgroundService started.");
+ }
+
+ public async Task StopAsync(CancellationToken cancellationToken)
+ {
+ _logger.LogInformation("BackgroundService ended.");
+ }
+}
+----
+
+NOTE: It is important to NOT run sync `host.Run()` or `host.Start()` methods, as it will block UI thread. Keep in mind, that UI apps has its own dispatcher queue that needs to be processed freely.
+
+NOTE: If your application needs to do some async logic **before** showing main window, you can skip MainWindow initialization, and then show `MainWindow` using `Show()` method on it after your async code. Note, it's only possible on desktop platforms.
+
+== ViewLocator powered by the Microsoft DI
+
+As you might know from other samples, ViewLocator in Avalonia is a special IDataTemplate implementation that helps to glue
+
+Before starting, don't forget to delete `ViewLocator.cs` and `ViewLocator` usage from `App.axaml` files, if you have it from the Avalonia template.
+
+Let's create ViewLocator implementation that will accept list of registered views as a constructor argument:
+
+[source,c#]
+----
+public class ViewLocator : IDataTemplate
+{
+ private readonly Dictionary> _dic;
+
+ public ViewLocator(IEnumerable descriptors)
+ {
+ _dic = descriptors.ToDictionary(x => x.ViewModel, x => x.Factory);
+ }
+
+ public Control Build(object? param) => _dic[param!.GetType()]();
+
+ public bool Match(object? param) => param is not null && _dic.ContainsKey(param.GetType());
+
+ public record ViewLocationDescriptor(Type ViewModel, Func Factory);
+}
+----
+
+And we need to add a extenions method which we will use to register these views to the Microsoft DI:
+
+[source,c#]
+----
+public static class ViewLocatorHelpers
+{
+ public static IServiceCollection AddView(this IServiceCollection services)
+ where TView : Control, new()
+ where TViewModel : ViewModelBase
+ {
+ services.AddSingleton(new ViewLocator.ViewLocationDescriptor(typeof(TViewModel), () => new TView()));
+ return services;
+ }
+}
+----
+
+Now we can register our view locator and all views:
+
+[source,c#]
+----
+builder.Services.AddTransient();
+
+// Registering DayReportView as a view for DayReportViewModel view model.
+// For the source code of these classes, see project in the sample folder.
+builder.Services.AddView();
+----
+
+And finally, after host was built, we can use
+
+[source,c#]
+----
+var host = builder.Build();
+
+var viewLocator = host.Services.GetRequiredService();
+DataTemplates.Add(viewLocator);
+----
+
+== Integration with ReactiveUI
+
+There is no need for any special configuration of ReactiveUI and .NET Generic Host with this approach.
+You only need to remember to call `.UseReactiveUI()` in Avalonia AppBuilder, so ReactiveUI can setup proper MainThreadScheduler.
+
+== Integration with Asp.Net Core
+
+This example doesn't cover this scenario, but it can be achieved in a similar way. Except instead of
+
+== Using .NET Generic Host in mobile and browser apps
+
+Having .NET Generic Host initialization in Application class will ensure that it will be created on all supported platforms.
+
+However as you might have noticed, IClassicDesktopStyleApplicationLifetime was used to handle application Exit event, so we can stop and dispose .NET Generic Host.
+
+Unfortunately, there is no currently a way to trigger code on application closing on all platforms. Apps that rely on StopAsync or Host disposal need to perform clean-up logic themselves using native APIs where it's possible.
+
+== Related
+
+Below please find a collection with helpful links:
+
+* https://learn.microsoft.com/en-us/dotnet/core/extensions/generic-host[.NET Generic Host]
+* https://laurentkempe.com/2019/09/03/WPF-and-dotnet-Generic-Host-with-dotnet-Core-3-0/[WPF and .NET Generic Host with .NET Core 3.0]
\ No newline at end of file
diff --git a/src/Avalonia.Samples/MVVM/MsExtensionsHostingSample/Services/HostedBackgroundService.cs b/src/Avalonia.Samples/MVVM/MsExtensionsHostingSample/Services/HostedBackgroundService.cs
new file mode 100644
index 0000000..9c43c2c
--- /dev/null
+++ b/src/Avalonia.Samples/MVVM/MsExtensionsHostingSample/Services/HostedBackgroundService.cs
@@ -0,0 +1,26 @@
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
+
+namespace MsExtensionsHostingSample.Services;
+
+public class HostedBackgroundService : IHostedService
+{
+ private readonly ILogger _logger;
+
+ public HostedBackgroundService(ILogger logger)
+ {
+ _logger = logger;
+ }
+
+ public async Task StartAsync(CancellationToken cancellationToken)
+ {
+ _logger.LogInformation("BackgroundService started.");
+ }
+
+ public async Task StopAsync(CancellationToken cancellationToken)
+ {
+ _logger.LogInformation("BackgroundService ended.");
+ }
+}
\ No newline at end of file
diff --git a/src/Avalonia.Samples/MVVM/MsExtensionsHostingSample/Services/Interfaces/IWeatherService.cs b/src/Avalonia.Samples/MVVM/MsExtensionsHostingSample/Services/Interfaces/IWeatherService.cs
new file mode 100644
index 0000000..9f475e5
--- /dev/null
+++ b/src/Avalonia.Samples/MVVM/MsExtensionsHostingSample/Services/Interfaces/IWeatherService.cs
@@ -0,0 +1,10 @@
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using MsExtensionsHostingSample.Models;
+
+namespace MsExtensionsHostingSample.Services.Interfaces;
+
+public interface IWeatherService
+{
+ Task> GetFiveDayTemperaturesAsync();
+}
\ No newline at end of file
diff --git a/src/Avalonia.Samples/MVVM/MsExtensionsHostingSample/Services/WeatherService.cs b/src/Avalonia.Samples/MVVM/MsExtensionsHostingSample/Services/WeatherService.cs
new file mode 100644
index 0000000..5395d37
--- /dev/null
+++ b/src/Avalonia.Samples/MVVM/MsExtensionsHostingSample/Services/WeatherService.cs
@@ -0,0 +1,56 @@
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using Microsoft.Extensions.Options;
+using MsExtensionsHostingSample.Models;
+using MsExtensionsHostingSample.Services.Interfaces;
+
+namespace MsExtensionsHostingSample.Services;
+
+public class WeatherService : IWeatherService
+{
+ private readonly IOptions _weatherSettings;
+
+ public WeatherService(IOptions weatherSettings)
+ {
+ _weatherSettings = weatherSettings;
+ }
+
+ public async Task> GetFiveDayTemperaturesAsync()
+ {
+ await Task.Delay(100); // simulate async operation
+
+ var weatherDataList = new List();
+
+ for (int i = 0; i < 10; i++)
+ {
+ var weatherData = new DayReport(
+ DateOnly.FromDateTime(DateTime.Today.AddDays(i)),
+ GetRandomWeatherCondition(),
+ ConvertTemperature(Random.Shared.Next(65, 80)),
+ _weatherSettings.Value.Unit,
+ Random.Shared.Next(40, 70),
+ Random.Shared.Next(5, 20));
+
+ weatherDataList.Add(weatherData);
+ }
+
+ return weatherDataList;
+ }
+
+ private string GetRandomWeatherCondition()
+ {
+ var weatherConditions = new[] { "Sunny", "Partly Cloudy", "Cloudy", "Rainy", "Windy" };
+ return weatherConditions[Random.Shared.Next(weatherConditions.Length)];
+ }
+
+ private double ConvertTemperature(double baseInF)
+ {
+ if (_weatherSettings.Value.Unit.Equals("C", StringComparison.OrdinalIgnoreCase))
+ {
+ return (int)Math.Round((baseInF - 32) / 1.8);
+ }
+
+ return baseInF;
+ }
+}
\ No newline at end of file
diff --git a/src/Avalonia.Samples/MVVM/MsExtensionsHostingSample/ViewLocator.cs b/src/Avalonia.Samples/MVVM/MsExtensionsHostingSample/ViewLocator.cs
new file mode 100644
index 0000000..ae6b11d
--- /dev/null
+++ b/src/Avalonia.Samples/MVVM/MsExtensionsHostingSample/ViewLocator.cs
@@ -0,0 +1,36 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Avalonia.Controls;
+using Avalonia.Controls.Templates;
+using Microsoft.Extensions.DependencyInjection;
+using MsExtensionsHostingSample.ViewModels;
+
+namespace MsExtensionsHostingSample;
+
+public static class ViewLocatorHelpers
+{
+ public static IServiceCollection AddView(this IServiceCollection services)
+ where TView : Control, new()
+ where TViewModel : ViewModelBase
+ {
+ services.AddSingleton(new ViewLocator.ViewLocationDescriptor(typeof(TViewModel), () => new TView()));
+ return services;
+ }
+}
+
+public class ViewLocator : IDataTemplate
+{
+ private readonly Dictionary> _dic;
+
+ public ViewLocator(IEnumerable descriptors)
+ {
+ _dic = descriptors.ToDictionary(x => x.ViewModel, x => x.Factory);
+ }
+
+ public Control Build(object? param) => _dic[param!.GetType()]();
+
+ public bool Match(object? param) => param is not null && _dic.ContainsKey(param.GetType());
+
+ public record ViewLocationDescriptor(Type ViewModel, Func Factory);
+}
\ No newline at end of file
diff --git a/src/Avalonia.Samples/MVVM/MsExtensionsHostingSample/ViewModels/DayReportViewModel.cs b/src/Avalonia.Samples/MVVM/MsExtensionsHostingSample/ViewModels/DayReportViewModel.cs
new file mode 100644
index 0000000..c7a30be
--- /dev/null
+++ b/src/Avalonia.Samples/MVVM/MsExtensionsHostingSample/ViewModels/DayReportViewModel.cs
@@ -0,0 +1,24 @@
+using System;
+using MsExtensionsHostingSample.Models;
+
+namespace MsExtensionsHostingSample.ViewModels;
+
+public class DayReportViewModel : ViewModelBase
+{
+ public DayReportViewModel(DayReport dayReport)
+ {
+ Date = dayReport.Date;
+ Temperature = dayReport.Temperature;
+ TemperatureUnit = dayReport.TemperatureUnit;
+ RelativeHumidity = dayReport.RelativeHumidity;
+ WindSpeed = dayReport.WindSpeed;
+ WeatherDescription = dayReport.WeatherCondition;
+ }
+
+ public DateOnly Date { get; }
+ public double Temperature { get; }
+ public string TemperatureUnit { get; }
+ public double RelativeHumidity { get; }
+ public double WindSpeed { get; }
+ public object WeatherDescription { get; }
+}
\ No newline at end of file
diff --git a/src/Avalonia.Samples/MVVM/MsExtensionsHostingSample/ViewModels/DesignData.cs b/src/Avalonia.Samples/MVVM/MsExtensionsHostingSample/ViewModels/DesignData.cs
new file mode 100644
index 0000000..c8bcbf0
--- /dev/null
+++ b/src/Avalonia.Samples/MVVM/MsExtensionsHostingSample/ViewModels/DesignData.cs
@@ -0,0 +1,12 @@
+using Avalonia;
+using Microsoft.Extensions.DependencyInjection;
+using MsExtensionsHostingSample.ViewModels;
+
+namespace MsExtensionsHostingSample;
+
+// DesignData used for Previewer as a source of generated view models.
+public static class DesignData
+{
+ public static MainWindowViewModel MainWindowViewModel { get; } =
+ ((App)Application.Current!).GlobalHost!.Services.GetRequiredService();
+}
\ No newline at end of file
diff --git a/src/Avalonia.Samples/MVVM/MsExtensionsHostingSample/ViewModels/MainWindowViewModel.cs b/src/Avalonia.Samples/MVVM/MsExtensionsHostingSample/ViewModels/MainWindowViewModel.cs
new file mode 100644
index 0000000..979e050
--- /dev/null
+++ b/src/Avalonia.Samples/MVVM/MsExtensionsHostingSample/ViewModels/MainWindowViewModel.cs
@@ -0,0 +1,37 @@
+using System;
+using System.Collections.ObjectModel;
+using System.Reactive;
+using System.Reactive.Disposables;
+using System.Reactive.Linq;
+using MsExtensionsHostingSample.Services.Interfaces;
+using ReactiveUI;
+
+namespace MsExtensionsHostingSample.ViewModels;
+
+public class MainWindowViewModel : ViewModelBase, IActivatableViewModel
+{
+ public MainWindowViewModel(IWeatherService weatherService)
+ {
+ Reports = new ObservableCollection();
+ RefreshReport = ReactiveCommand.CreateFromTask(async _ =>
+ {
+ var days = await weatherService.GetFiveDayTemperaturesAsync();
+ Reports.Clear();
+ foreach (var dayReport in days)
+ {
+ Reports.Add(new DayReportViewModel(dayReport));
+ }
+ });
+
+ this.WhenActivated(disposable =>
+ {
+ RefreshReport.Execute().Subscribe().DisposeWith(disposable);
+ });
+ }
+
+ public ReactiveCommand RefreshReport { get; }
+
+ public ObservableCollection Reports { get; }
+
+ public ViewModelActivator Activator { get; } = new();
+}
\ No newline at end of file
diff --git a/src/Avalonia.Samples/MVVM/MsExtensionsHostingSample/ViewModels/ViewModelBase.cs b/src/Avalonia.Samples/MVVM/MsExtensionsHostingSample/ViewModels/ViewModelBase.cs
new file mode 100644
index 0000000..d009ed7
--- /dev/null
+++ b/src/Avalonia.Samples/MVVM/MsExtensionsHostingSample/ViewModels/ViewModelBase.cs
@@ -0,0 +1,7 @@
+using ReactiveUI;
+
+namespace MsExtensionsHostingSample.ViewModels;
+
+public class ViewModelBase : ReactiveObject
+{
+}
\ No newline at end of file
diff --git a/src/Avalonia.Samples/MVVM/MsExtensionsHostingSample/Views/DayReportView.axaml b/src/Avalonia.Samples/MVVM/MsExtensionsHostingSample/Views/DayReportView.axaml
new file mode 100644
index 0000000..9018ffa
--- /dev/null
+++ b/src/Avalonia.Samples/MVVM/MsExtensionsHostingSample/Views/DayReportView.axaml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Avalonia.Samples/MVVM/MsExtensionsHostingSample/Views/DayReportView.axaml.cs b/src/Avalonia.Samples/MVVM/MsExtensionsHostingSample/Views/DayReportView.axaml.cs
new file mode 100644
index 0000000..74756c0
--- /dev/null
+++ b/src/Avalonia.Samples/MVVM/MsExtensionsHostingSample/Views/DayReportView.axaml.cs
@@ -0,0 +1,12 @@
+using Avalonia.ReactiveUI;
+using MsExtensionsHostingSample.ViewModels;
+
+namespace MsExtensionsHostingSample.Views;
+
+public partial class DayReportView : ReactiveUserControl
+{
+ public DayReportView()
+ {
+ InitializeComponent();
+ }
+}
\ No newline at end of file
diff --git a/src/Avalonia.Samples/MVVM/MsExtensionsHostingSample/Views/MainWindow.axaml b/src/Avalonia.Samples/MVVM/MsExtensionsHostingSample/Views/MainWindow.axaml
new file mode 100644
index 0000000..3074c13
--- /dev/null
+++ b/src/Avalonia.Samples/MVVM/MsExtensionsHostingSample/Views/MainWindow.axaml
@@ -0,0 +1,15 @@
+
+
+
diff --git a/src/Avalonia.Samples/MVVM/MsExtensionsHostingSample/Views/MainWindow.axaml.cs b/src/Avalonia.Samples/MVVM/MsExtensionsHostingSample/Views/MainWindow.axaml.cs
new file mode 100644
index 0000000..894c9fe
--- /dev/null
+++ b/src/Avalonia.Samples/MVVM/MsExtensionsHostingSample/Views/MainWindow.axaml.cs
@@ -0,0 +1,13 @@
+using Avalonia.Controls;
+using Avalonia.ReactiveUI;
+using MsExtensionsHostingSample.ViewModels;
+
+namespace MsExtensionsHostingSample.Views;
+
+public partial class MainWindow : ReactiveWindow
+{
+ public MainWindow()
+ {
+ InitializeComponent();
+ }
+}
\ No newline at end of file
diff --git a/src/Avalonia.Samples/MVVM/MsExtensionsHostingSample/app.manifest b/src/Avalonia.Samples/MVVM/MsExtensionsHostingSample/app.manifest
new file mode 100644
index 0000000..47a2893
--- /dev/null
+++ b/src/Avalonia.Samples/MVVM/MsExtensionsHostingSample/app.manifest
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Avalonia.Samples/MVVM/MsExtensionsHostingSample/appsettings.json b/src/Avalonia.Samples/MVVM/MsExtensionsHostingSample/appsettings.json
new file mode 100644
index 0000000..e4ba8b1
--- /dev/null
+++ b/src/Avalonia.Samples/MVVM/MsExtensionsHostingSample/appsettings.json
@@ -0,0 +1,11 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.Hosting.Lifetime": "Warning"
+ }
+ },
+ "Weather": {
+ "Unit": "F"
+ }
+}