From 708f87357e56ec8dec4d61bb4ac7a6d7ba0bd877 Mon Sep 17 00:00:00 2001 From: Markus Ewald Date: Wed, 12 Feb 2025 21:44:38 +0100 Subject: [PATCH] Wrote a small helper class with extension methods to register the Nuclex.Avalonia MVVM services to a Microsoft.Extensions.DependencyInjection IoC container --- ...endencyInjection (net-4.6)(net-8.0).csproj | 31 +++++ Properties/AssemblyInfo.cs | 48 ++++++++ Source/DependencyInjectedWindowManager.cs | 116 ++++++++++++++++++ Source/MvvmExtensions.cs | 83 +++++++++++++ 4 files changed, 278 insertions(+) create mode 100644 Nuclex.Avalonia.DependencyInjection (net-4.6)(net-8.0).csproj create mode 100644 Properties/AssemblyInfo.cs create mode 100644 Source/DependencyInjectedWindowManager.cs create mode 100644 Source/MvvmExtensions.cs diff --git a/Nuclex.Avalonia.DependencyInjection (net-4.6)(net-8.0).csproj b/Nuclex.Avalonia.DependencyInjection (net-4.6)(net-8.0).csproj new file mode 100644 index 0000000..4184461 --- /dev/null +++ b/Nuclex.Avalonia.DependencyInjection (net-4.6)(net-8.0).csproj @@ -0,0 +1,31 @@ + + + + net462;net8.0 + Apache-2.0 + false + True + Nuclex.Windows.Forms.DependencyInjection + Nuclex.Windows.Forms.DependencyInjection + obj\source + + + + + + + + + + + + + + + + + + + + + diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..d8411bb --- /dev/null +++ b/Properties/AssemblyInfo.cs @@ -0,0 +1,48 @@ +#region Apache License 2.0 +/* +Nuclex .NET Framework +Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +#endregion // Apache License 2.0 + +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Nuclex.Avalonia.DependencyInjection")] +[assembly: AssemblyProduct("Nuclex.Avalonia.DependencyInjection")] +[assembly: AssemblyDescription("Add-on module to use Nuclex.Avalonia MVVM with Microsoft dependency injection interfaces")] +[assembly: AssemblyCompany("Nuclex Development Labs")] +[assembly: AssemblyCopyright("Copyright © Markus Ewald / Nuclex Development Labs 2002-2025")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("4b185e46-672f-4629-98e8-82db07aa8147")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +[assembly: AssemblyVersion("1.3.0")] diff --git a/Source/DependencyInjectedWindowManager.cs b/Source/DependencyInjectedWindowManager.cs new file mode 100644 index 0000000..35dd566 --- /dev/null +++ b/Source/DependencyInjectedWindowManager.cs @@ -0,0 +1,116 @@ +#region Apache License 2.0 +/* +Nuclex .NET Framework +Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +#endregion // Apache License 2.0 + +using System; +#if NET6_0_OR_GREATER +using System.Runtime.Versioning; +#endif + +using Microsoft.Extensions.DependencyInjection; + +using Nuclex.Avalonia.AutoBinding; + +namespace Nuclex.Avalonia.DependencyInjection { + + /// + /// Window manager that is using Microsoft's dependency injection interfaces + /// +#if NET6_0_OR_GREATER + [SupportedOSPlatform("windows")] +#endif + public class DependencyInjectedWindowManager : WindowManager { + + #region class WindowScope + + /// Manages a window-specific service scope + private class WindowScope : IWindowScope, IDisposable { + + /// Initializes a new service scope for the window + /// + /// Service provider in which to create a scope + /// + public WindowScope(IServiceProvider serviceProvider) { + this.serviceScope = serviceProvider.CreateScope(); + } + + /// Creates an instance of the specified type in the scope + /// Type an instance will be created of + /// The created instance + /// + /// Use this to wire up your dependency injection container. By default, + /// the Activator class will be used to create instances which only works + /// if all of your view models are concrete classes. + /// + public object CreateInstance(Type type) { + return this.serviceScope.ServiceProvider.GetRequiredService(type); + } + + /// Immediately destroys all services owned by the scope + public void Dispose() { + if(this.serviceScope != null) { + this.serviceScope.Dispose(); + this.serviceScope = null; + } + } + + /// Service scope that will be used to create instances + private IServiceScope serviceScope; + + } + + #endregion // class WindowScope + + /// Initializes a new window manager + /// + /// Dependency injector the window manager uses to construct view models + /// + /// + /// View model binder that will be used to bind all created views to their models + /// + public DependencyInjectedWindowManager( + IServiceProvider serviceProvider, IAutoBinder autoBinder = null + ) : + base(autoBinder) { + this.serviceProvider = serviceProvider; + } + + /// Creates an instance of the specified type + /// Type an instance will be created of + /// The created instance + /// + /// Use this to wire up your dependency injection container. By default, + /// the Activator class will be used to create instances which only works + /// if all of your view models are concrete classes. + /// + protected override object CreateInstance(Type type) { + return this.serviceProvider.GetRequiredService(type); + } + + /// Creates a new scope in which window-specific services will live + /// The new scope managing the lifetime of window-specific services + protected override IWindowScope CreateWindowScope() { + return new WindowScope(this.serviceProvider); + } + + /// The service provider used to create new instances + private readonly IServiceProvider serviceProvider; + + } + +} // namespace Nuclex.Avalonia.DependencyInjection diff --git a/Source/MvvmExtensions.cs b/Source/MvvmExtensions.cs new file mode 100644 index 0000000..fe21bab --- /dev/null +++ b/Source/MvvmExtensions.cs @@ -0,0 +1,83 @@ +#region Apache License 2.0 +/* +Nuclex .NET Framework +Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +#endregion // Apache License 2.0 + +using System; + +using Microsoft.Extensions.DependencyInjection; + +using Nuclex.Avalonia.AutoBinding; +using Nuclex.Avalonia.Messages; + +namespace Nuclex.Avalonia.DependencyInjection { + + /// Sets up the service bindings for an MVVM-based application + public static class MvvmExtensions { + + /// Registers all MVVM supporting services for a WinForms application + /// Service collection the services will be registered to + /// The service collection for method chaining + public static IServiceCollection AddMvvm(this IServiceCollection services) { + + // Allow displaying message boxes via the AvaloniaMessagePresenter which wraps + // the MessageBox.Avalonia library + services.AddSingleton(); + + // The window manager keeps track of which Window is in the foreground + // and handles opening modal or modeless windows for which it either + // binds provided view models or requests new instances. + services.AddSingleton(); + + // The IWindowManager is the main interface that should be used to + // create new windows and dialogs + services.AddSingleton( + sp => sp.GetRequiredService() + ); + + // The IActiveWindowTracker is a very simple interface to let Windows Forms + // extensions that need to display message boxes or other things query for + // the currently active top-level window. + services.AddSingleton( + sp => sp.GetRequiredService() + ); + + // The auto binder uses convention-over-configuration to automatically + // establish data bindings or call view model methods that share their name + // with button controls. + services.AddSingleton(); + + return services; + + } + + /// Registers all MVVM supporting services for a WinForms application + /// Service collection the services will be registered to + /// The service collection for method chaining + public static IServiceCollection AddCommonDialogs(this IServiceCollection services) { + + // Implementation of the message service that uses plain MessageBoxes + // from the Win32 API to display messages to the user + services.AddSingleton(); + + return services; + + } + + } + +} // namespace Nuclex.Avalonia.DependencyInjection