diff --git a/.gitignore b/.gitignore
index e76a874..52701a6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,6 @@
-NuclexSupportConfig.cmake
+FoundationPackageConfig.cmake
+
+third-party
# Visual Studio Codium
.vscode/*
diff --git a/.gitmodules b/.gitmodules
index 755403c..5b94603 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -13,3 +13,8 @@
url = ../../nuclex-shared-dotnet/Nuclex.Windows.Forms.Ninject.git
branch = main
update = rebase
+[submodule "Nuclex.Windows.Forms.DependencyInjection"]
+ path = Nuclex.Windows.Forms.DependencyInjection
+ url = ../../nuclex-shared-dotnet/Nuclex.Windows.Forms.DependencyInjection.git
+ branch = main
+ update = rebase
diff --git a/Nuclex.Foundation.DependencyInjection.nuspec b/Nuclex.Foundation.DependencyInjection.nuspec
new file mode 100644
index 0000000..7da305d
--- /dev/null
+++ b/Nuclex.Foundation.DependencyInjection.nuspec
@@ -0,0 +1,50 @@
+
+
+
+ Nuclex.Foundation.DependencyInjection
+ 1.2.0
+ Dependency injection bindings for the Nuclex Foundation Libraries
+ Markus Ewald
+
+ false
+ Apache-2.0
+ ReadMe.md
+ https://github.com/orgs/nuclex-shared-dotnet/repositories
+ Bindings to use Microsoft's (and compatible) dependency injector in Microsoft.Extensions.DependencyInjection(.Asbtraction) for view models with Nuclex.Windows.Forms
+ en-US
+ mvvm,util,ninject,dependency-injection
+ nuclex-logo.png
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Nuclex.Foundation.Ninject.nuspec b/Nuclex.Foundation.Ninject.nuspec
new file mode 100644
index 0000000..4307be9
--- /dev/null
+++ b/Nuclex.Foundation.Ninject.nuspec
@@ -0,0 +1,50 @@
+
+
+
+ Nuclex.Foundation.Ninject
+ 1.2.0
+ Ninject bindings for the Nuclex Foundation Libraries
+ Markus Ewald
+
+ false
+ Apache-2.0
+ ReadMe.md
+ https://github.com/orgs/nuclex-shared-dotnet/repositories
+ Bindings to use Ninject for dependency injection in view model with Nuclex.Windows.Forms
+ en-US
+ mvvm,util,ninject,dependency-injection
+ nuclex-logo.png
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/foundation-package.nuspec b/Nuclex.Foundation.nuspec
similarity index 55%
rename from foundation-package.nuspec
rename to Nuclex.Foundation.nuspec
index daec482..15c40b0 100644
--- a/foundation-package.nuspec
+++ b/Nuclex.Foundation.nuspec
@@ -2,31 +2,29 @@
Nuclex.Foundation
- 1.1.0
+ 1.2.0
Nuclex Foundation Libraries
Markus Ewald
false
Apache-2.0
+ https://licenses.nuget.org/Apache-2.0
ReadMe.md
- https://github.com/nuclex-shared-dotnet/Nuclex.Support
+ https://github.com/orgs/nuclex-shared-dotnet/repositories
Set of basic libraries offering tools for MVVM-based UIs, storage of settings, license keys, collections and observer/subscriber pattern helpers
en-US
mvvm,util
nuclex-logo.png
-
+
-
-
-
-
-
-
+
+
+
-
+
@@ -45,17 +43,13 @@
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Nuclex.Support b/Nuclex.Support
index e818fae..5fb433a 160000
--- a/Nuclex.Support
+++ b/Nuclex.Support
@@ -1 +1 @@
-Subproject commit e818fae81012b0a819abcf2180b90eee1d998fb7
+Subproject commit 5fb433ad983e58725e0d25faa8968a75b3dfd178
diff --git a/Nuclex.Windows.Forms b/Nuclex.Windows.Forms
index 563d45f..d057f70 160000
--- a/Nuclex.Windows.Forms
+++ b/Nuclex.Windows.Forms
@@ -1 +1 @@
-Subproject commit 563d45f9f2f3b666586eb0862088ac3e00566389
+Subproject commit d057f704497b63faa5e57c6330afb436a0d9708c
diff --git a/Nuclex.Windows.Forms.DependencyInjection b/Nuclex.Windows.Forms.DependencyInjection
new file mode 160000
index 0000000..573b7ab
--- /dev/null
+++ b/Nuclex.Windows.Forms.DependencyInjection
@@ -0,0 +1 @@
+Subproject commit 573b7ab93d25034a649bdf401f5439ec15c165f5
diff --git a/Nuclex.Windows.Forms.Ninject b/Nuclex.Windows.Forms.Ninject
index cb4e398..d3e1a3e 160000
--- a/Nuclex.Windows.Forms.Ninject
+++ b/Nuclex.Windows.Forms.Ninject
@@ -1 +1 @@
-Subproject commit cb4e39895666e467dec8b3b92abdd73127687385
+Subproject commit d3e1a3e24e7ff6615996ec9236495b6d1275de5c
diff --git a/ReadMe.md b/ReadMe.md
index eae9126..af68c28 100644
--- a/ReadMe.md
+++ b/ReadMe.md
@@ -35,7 +35,7 @@ void saveSettingsToIni() {
var iniStore = new ConfigurationFileStore();
saveSettings(iniStore);
- using(var writer = new StreamWriteR("awesome-app.ini")) {
+ using(var writer = new StreamWriter("awesome-app.ini")) {
iniStore.Save(writer);
writer.Flush()
}
@@ -78,9 +78,8 @@ static void Main() {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
- using(var windowManager = new WindowManager()) {
- Application.Run(windowManager.OpenRoot());
- }
+ var windowManager = new WindowManager();
+ Application.Run(windowManager.OpenRoot());
}
```
@@ -99,8 +98,8 @@ will be assigned to its `DataContext` property, establishing
the View/ViewModel relationship.
-Adding an IoC Container (Ninject) to the MVVM Example
------------------------------------------------------
+Adding Dependency Injection to the MVVM Example
+-----------------------------------------------
In the previous example, the view and its view model were constructed using
`Activator.CreateInstance()` - a method provided by .NET that creates a new
@@ -111,30 +110,55 @@ to provide the ViewModel with the data it is supposed to be an adapter for.
You can achieve that by constructing the ViewModel yourself and passing it
to the `WindowManager.OpenRoot()` or `WindowManager.ShowModal()` methods.
-A much better approach is to use a dependency injector - an IoC container with
-automatic constructor parameter injection. My favorite one is Ninject (due to
-its neat setup with a fluent interface), but you can use any container you
-wish, simply by inheriting your own `WindowManager` class:
+A much better approach is to use a dependency injector that automatically
+wires up your view models to app-internal services, repositories and managers
+to which the view models and mediate.
+
+Let's do the full program with the host application builder from Microsoft's
+`Microsoft.Extensions.Hosting` assembly which would also give you logging and
+hosted services in addition its dependency injector:
```csharp
-public class NinjectWindowManager : WindowManager {
+[STAThread]
+static void Main() {
+ Application.EnableVisualStyles();
+ Application.SetCompatibleTextRenderingDefault(false);
- public NinjectWindowManager(IKernel kernel, IAutoBinder autoBinder = null) :
- base(autoBinder) {
- this.kernel = kernel;
+ HostApplicationBuilder builder = Host.CreateApplicationBuilder(
+ new HostApplicationBuilderSettings() {
+ DisableDefaults = true,
+ ContentRootPath = "",
+ ApplicationName = "Example"
+ }
+ );
+
+ // These are extension methods in Nuclex.Windows.Forms.DependencyInjection
+ builder.Services.AddMvvm(); // Window Manager
+ builder.Services.AddCommonDialogs(); // Message boxes, file dialogs, etc.
+
+ // View models and dialogs must be explicitly registered by default.
+ // This requirement can be relaxed.
+ builder.Services.AddTransient();
+ builder.Services.AddTransient();
+
+ // Create the host (in which all application services and scopes will
+ // be managed) and run the main window.
+ using(IHost host = builder.Build()) {
+ var windowManager = host.Services.GetRequiredService();
+ Application.Run(windowManager.OpenRoot());
}
-
- protected override object CreateInstance(Type type) {
- return this.kernel.Get(type);
- }
-
- private IKernel kernel;
}
```
-Your `NinjectWindowManager` will now use `IKernel.Get()` to construct its
-ViewModels, allowing their constructors to require any services and instances
-you have set up in your Ninject kernel.
+Now view models can access any registered services. By default, each view and
+view model pair lives inside of an implicit 'scope', meaning that services
+added via `builder.Services.AddScoped()` will have unique instances per open
+dialog or window.
+
+This is very useful if your dialogs perform background processing via threads
+(i.e. using `ThreadedViewModel`) or tasks where a shared database connection,
+for example, would lead to trouble when it is asked to run another query while
+a data reader is still open.
```csharp
class MainViewModel {
diff --git a/foundation-package.sln b/foundation-package.sln
index 463c9d7..fa89d38 100644
--- a/foundation-package.sln
+++ b/foundation-package.sln
@@ -15,6 +15,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nuclex.Windows.Forms.Ninjec
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Unit Tests", "Unit Tests", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nuclex.Windows.Forms.DependencyInjection (net-4.6)(net-8.0)", "Nuclex.Windows.Forms.DependencyInjection\Nuclex.Windows.Forms.DependencyInjection (net-4.6)(net-8.0).csproj", "{3D7D6CA0-A130-FD54-B772-B4B48847E682}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -41,6 +43,10 @@ Global
{98B1CB8B-9202-4008-9254-D0BE0B2D38AF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{98B1CB8B-9202-4008-9254-D0BE0B2D38AF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{98B1CB8B-9202-4008-9254-D0BE0B2D38AF}.Release|Any CPU.Build.0 = Release|Any CPU
+ {3D7D6CA0-A130-FD54-B772-B4B48847E682}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {3D7D6CA0-A130-FD54-B772-B4B48847E682}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {3D7D6CA0-A130-FD54-B772-B4B48847E682}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {3D7D6CA0-A130-FD54-B772-B4B48847E682}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/nuget-pack.cmd b/nuget-pack.cmd
new file mode 100644
index 0000000..24d6c49
--- /dev/null
+++ b/nuget-pack.cmd
@@ -0,0 +1,5 @@
+@ECHO OFF
+
+nuget pack Nuclex.Foundation.nuspec -Version 1.2.0 -Properties Configuration=Release
+nuget pack Nuclex.Foundation.Ninject.nuspec -Version 1.2.0 -Properties Configuration=Release
+nuget pack Nuclex.Foundation.DependencyInjection.nuspec -Version 1.2.0 -Properties Configuration=Release