I am using Prism + Unity in a wpf application using MVVM. I am a beginner with Prism and Unity.
I want to be able to close the current view. The various solutions and articles I've read state that the best way to do this is from the view model. But the view model needs a region manager object in order to close the view. Ok, so let's set up constructor injection. Never tried this before but there are plenty of questions on SO that deal with this.
Let me start with explaining how things are wired together. I have a bootstrapper class that handles the registering of types and instances.
Here is how my view and view model is registered:
container.RegisterType<IViewModel, ViewAccountsViewModel>(new InjectionConstructor(new ResolvedParameter(typeof(RegionManager))));
container.RegisterType<ViewAccountsView>();
Here is the module for the view accounts view:
public class ViewAccountsModule : IModule
{
private readonly IRegionManager regionManager;
private readonly IUnityContainer container;
public ViewAccountsModule(IUnityContainer container, IRegionManager regionManager)
{
this.container = container;
this.regionManager = regionManager;
}
/// <summary>
///
/// </summary>
public void Initialize()
{
regionManager.RegisterViewWithRegion("MainRegion", () => this.container.Resolve<ViewAccountsView>());
}
}
In my ViewAccountsView.xaml, I am setting the data context like so:
<Grid.DataContext>
<vm:ViewAccountsViewModel/>
</Grid.DataContext>
And my view model constructor:
[InjectionConstructor]
public ViewAccountsViewModel(IRegionManager regionManager)
{
if (regionManager == null) throw new ArgumentNullException("regionManager");
this.regionManager = regionManager;
}
When I compile the solution, I receive an error that the type "ViewAccountsViewModel" does not include any accessible constructors. If I add a default constructor to my view model, the view displays but I cannot remove the view from the region. I get an argument null exception.
Here is the code for removing the view:
regionManager.Regions["MainRegion"].Remove(regionManager.Regions["MainRegion"].GetView("ViewAccountsView"));
I am still very much a beginner with IoC and DI. Is there something I have missed?
Unity will handle injecting all dependencies it knows about for you. By default, Unity will call the constructor with the most parameters. You usually use InjectionConstructor to either tell Unity to choose a different constructor when it creates the objects for you, or if you want to pass it custom parameters.
Registration:
container.RegisterType<IViewModel, ViewAccountsViewModel>();
// If you plan to have multiple IViewModels, it will need to have a name
// container.RegisterType<IViewModel, ViewAccountsViewModel>("ViewAccountsViewModelName");
container.RegisterType<ViewAccountsView>();
ViewModel:
// If you decide later you need other dependencies like IUnityContainer, you can just set
// it in your constructor and Unity will give it to you automagically through the power
// of Dependency Injection
// public ViewAccountsViewModel(IRegionManager regionManager, IUnityContainer unityContainer)
public ViewAccountsViewModel(IRegionManager regionManager)
{
this.regionManager = regionManager;
}
View Code Behind:
// If you have a named IViewModel
// public ViewAccountsView([Dependency("ViewAccountsViewModelName")]IViewModel viewModel)
public ViewAccountsView(IViewModel viewModel)
{
this.InitializeComponent();
this.DataContext = viewModel;
}
Related
I want to take advantage of dependency injection in my Xamarin project but can't get constructor injection to work in C# classes behind XAML views. Is there any way to do it ?
I've seen guides how to setup dependency injections in View Models, to later use them as repositories but that doesn't work for me.
So far I tried Ninject and Unity.
Code:
This is the service I want to use inside of my PCL project:
public class MyService : IMyService
{
public void Add(string myNote)
{
//Add Note logic
}
}
Interface:
public interface IMyService
{
void Add(string myNote);
}
Unity setup in App.Xaml:
public App ()
{
InitializeComponent();
var unityContainer = new UnityContainer();
unityContainer.RegisterType<IMyService, MyService>();
var unityServiceLocator = new UnityServiceLocator(unityContainer);
ServiceLocator.SetLocatorProvider(() => unityServiceLocator);
MainPage = new MainMasterMenu(); //<-- feel that I'm missing something here as I shouldn't be creating class instances with DI, right ?
}
Usage that I'd like to see. This is .CS file behind a XAML starting page:
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class MainMasterMenu : MasterDetailPage
{
private IMyService _myService;
public MainMasterMenu(IMyService myService)
{
_myService = myService
}
private void SomeFormControlClickEvent(object sender, ItemChangedEventArgs e)
{
_myService.Add("hi");
}
}
For that simple example creating the MainMasterMenu directly would be no issue, but you would have to pass the reference to your service
MainPage = new MainMasterMenu(unityContainer.Resolve<IMyService>());
But this would mean that you'll have to change that line every time the constructor of MainMasterMenu changes. You could circumvent this by registering the MainMasterMenu, too.
unityContainer.RegisterType<MainMasterMenu>();
...
MainPage = unityContainer.Resolve<MainMasterPage>();
Anyway, anytime you want to navigate to another page, which needs any dependency registered with unity, you'll have to make sure to resolve its dependencies properly, which requires (at least indirect) access to the unity container. You could pass a wrapper that encapsules the access to unity
interface IPageResolver
{
T ResolvePage<T>()
where T : Page;
}
and then implement that resolver with unity
public class UnityPageResolver
{
private IUnityContainer unityContainer;
public UnityPageResolver(IUnityContainer unityContainer)
{
this.unityContainer = unityContainer;
}
public T ResolvePage<T>()
where T : Page // do we need this restriction here?
{
return unityContainer.Resolve<T>();
}
}
This gets registered with unity
unityContainer.RegisterInstance<IUnityContainer>(this);
unityContainer.RegisterType<IPageResolver, UnityPageResolver>();
But you should have a look at the Prism library (see here) that solves many of the issues (e.g. it provides an INavigationService that lets you navigate to other pages without caring about the dependencies and it provides facilities to resolve viewmodels automatically, including dependencies).
I've been using MVVM for WPF quite a while now but I've always been doing it this way:
ExampleView.xaml.cs (namespace: Example.Views)
public partial class ExampleView
{
public ExampleView()
{
InitializeComponent();
var viewModel = new ExampleViewModel();
DataContext = viewModel;
}
}
The ExampleView.xaml has no code concerning the ExampleViewModel except for bindings to properties.
ExampleViewModel.cs (namespace: Example.ViewModels)
public ExampleViewModel()
{
// No important code in here concerning this topic. Code here is only used in this class.
}
Below is a simplified MainWindowView.xaml.
<Window ...
xmlns:views="clr-namespace:Example.Views">
<Grid>
<views:ExampleView />
</Grid>
</Window>
The MainWindowView.xaml.cs is similar to the ExampleView.xaml.cs. The MainWindowViewModel.cs has no important code concerning this topic.
Lastly, the App.xaml contains the StartupUri="Views/MainWindowView.xaml".
If this is a good pattern or not, I got my application to work. Since the application is not maintainable by me alone anymore, 2-3 people are now working on it creating some problems. One person is doing the majority of the coding (ViewModels basically), one person is doing the GUI (Views) and one person is doing the "framework" coding. (Using "" because this is not really a framework but I can't think of a better word for it.)
Now, I'm the guy that is doing the framework coding and I've been reading up on several subjects like dependency injection and the code below is what I came up with using UnityContainer from Windows.
ExampleView.xaml.cs (namespace: Example.Views)
public partial class ExampleView
{
public ExampleView()
{
InitializeComponent();
}
}
The ExampleView.xaml has no code concerning the ExampleViewModel except for bindings to properties.
ExampleViewModel.cs (namespace: Example.ViewModels)
public string MyText { get; set; }
public ExampleViewModel(ILocalizer localizer)
{
MyText = localizer.GetString("Title");
}
Below is a simplified MainWindowView.xaml.
<Window ...
xmlns:views="clr-namespace:Example.Views">
<Grid>
<views:ExampleView DataContext="{Binding ExampleViewModel}" />
</Grid>
</Window>
The MainWindowView.xaml.cs is similar to the ExampleView.xaml.cs.
MainWindowViewModel.cs
ExampleViewModel ExampleViewModel { get; set; }
private readonly ILocalizer _localizer;
private readonly IExceptionHandler _exHandler;
public MainWindowViewModel(ILocalizer localizer, IExceptionHandler exHandler)
{
_localizer = localizer;
_exHandler = exHandler;
ExampleViewModel = new ExampleViewModel(localizer);
}
Lastly, the App.xaml does not contains the StartupUri="..." anymore. It's now done in App.xaml.cs. It's also here where the `UnityContainer is initialized.
protected override void OnStartup(StartupEventArgs e)
{
// Base startup.
base.OnStartup(e);
// Initialize the container.
var container = new UnityContainer();
// Register types and instances with the container.
container.RegisterType<ILocalizer, Localizer>();
container.RegisterType<IExceptionHandler, ExceptionHandler>();
// For some reason I need to initialize this myself. See further in post what the constructor is of the Localizer and ExceptionHandler classes.
container.RegisterInstance<ILocalizer>(new Localizer());
container.RegisterInstance<IExceptionHandler>(new ExceptionHandler());
container.RegisterType<MainWindowViewModel>();
// Initialize the main window.
var mainWindowView = new MainWindowView { DataContext = container.Resolve<MainWindowViewModel>() };
// This is a self made alternative to the default MessageBox. This is a static class with a private constructor like the default MessageBox.
MyMessageBox.Initialize(mainWindowView, container.Resolve<ILocalizer>());
// Show the main window.
mainWindowView.Show();
}
For some reason I need to initialize the Localizer and ExceptionHandler classes myself. The Localizer and ExceptionHandler constructors are found below. Both have constructors with all arguments that have a default value. Adding constructors without arguments like
public ExceptionHandler() : this(Path.Combine(Directory.GetCurrentDirectory(), "ErrorLogs", DateTime.Now.ToString("dd-MM-yyyy") + ".log")) { }
doesn't change a thing.
public Localizer(ResourceDictionary appResDic = null, string projectName = null, string languagesDirectoryName = "Languages", string fileBaseName = "Language", string fallbackLanguage = "en")
{
_appResDic = appResDic ?? Application.Current.Resources;
_projectName = !string.IsNullOrEmpty(projectName) ? projectName : Application.Current.ToString().Split('.')[0];
_languagesDirectoryName = languagesDirectoryName.ThrowArgNullExIfNullOrEmpty("languagesFolder", "0X000000066::The languages directory name can't be null or an empty string.");
_fileBaseName = fileBaseName.ThrowArgNullExIfNullOrEmpty("fileBaseName", "0X000000067::The base name of the language files can't be null or an empty string.");
_fallbackLanguage = fallbackLanguage.ThrowArgNullExIfNullOrEmpty("fallbackLanguage", "0X000000068::The fallback language can't be null or an empty string.");
CurrentLanguage = _fallbackLanguage;
}
public ExceptionHandler(string logLocation = null, ILocalizer localizer = null)
{
// Check if the log location is not null or an empty string.
LogLocation = string.IsNullOrEmpty(logLocation) ? Path.Combine(Directory.GetCurrentDirectory(), "ErrorLogs", DateTime.Now.ToString("dd-MM-yyyy") + ".log") : logLocation;
_localizer = localizer;
}
My big question now is if I'm approaching dependency injection correctly and if having several static classes that I initialize once are bad. Several topics I've read state that static classes are a bad-practice because of bad testability and tightly coupled code, but right now the tradeoffs of dependency injection are bigger than having static classes.
Doing dependency injection correctly would be a first step in having less tightly coupled code though. I like the approach with the static MyMessageBox I can initialize once and that it's globally available in the application. This is mainly for "easy usage" I guess cause I can simply call MyMessageBox.Show(...) instead of injecting this all the way down to the smallest element. I have a similar opinion about the Localizer and ExceptionHandler because these will be used even more.
A last concern I have is the following. Lets say I have a class with several arguments and one of the arguments is the Localizer (because this will be used in nearly any class). Having to add ILocalizer localizer every time
var myClass = new MyClass(..., ILocalizer localizer);
feels very annoying. This would push me towards a static Localizer I initialize once and having never to care about it anymore. How would this problem be tackled?
If you have a bunch of "Services" which are used in many classes, you can create a facade class which encapsulates the required services and inject the facade into your classes.
Advantage of doing so is, you can easily add other services to that facade and they'd be available in all other injected classes, without modifying the constructor parameters.
public class CoreServicesFacade : ICoreServicesFacade
{
private readonly ILocalizer localizer;
private readonly IExceptionHandler excaptionHandler;
private readonly ILogger logger;
public ILocalizer Localizer { get { return localizer; } }
public IExceptionHandler ExcaptionHandler{ get { return exceptionHandler; } }
public ILogger Logger { get { return logger; } }
public CoreServices(ILocalizer localizer, IExceptionHandler exceptionHandler, ILogger logger)
{
if(localizer==null)
throw new ArgumentNullException("localizer");
if(exceptionHandler==null)
throw new ArgumentNullException("exceptionHandler");
if(logger==null)
throw new ArgumentNullException(logger);
this.localizer = localizer;
this.exceptionHandler = exceptionHandler;
this.logger = logger;
}
}
Then you can pass it to your classes:
var myClass = new MyClass(..., ICoreServicesFacade coreServices);
(which you shouldn't do anyway when using Dependency Injection, you shouldn't use new keyword, except for factories and models).
As for your ILocalizer and IExceptionHandler implementations... if your ExceptionHandler requires the Localizer and the localizer requires the string parameter, you have two options, depending on if the file name needs to be determined at a later point at run time or only once during the Application initialization.
Important
Don't use optional constructor parameters if you want to use dependency injection. For DI, constructor parameters should declare the dependencies in constructor and constructor dependencies are always considered as mandatory (don't use ILocalizer localizer = null within the constructor).
If you only create the logfile during the Applications initialization, it's quite easy
var logFilePath = Path.Combine(Directory.GetCurrentDirectory(), "ErrorLogs", DateTime.Now.ToString("dd-MM-yyyy") + ".log");
var localizer = new Localizer(...);
var exceptionHandler = new ExceptionHandler(logFilePath, localizer);
container.RegisterInstance<ILocalizer>(localizer);
container.RegisterInstance<IExceptionHandler>(exceptionHandler);
Basically in your bootstrapper you instantiate and configure your Localizer and ExceptionHandler, then register it as instance with the container.
If for some reason, you need to determine the name of log filename or language at a later point (after Bootstrapper configuration & initialization), you need to use a different approach: You need a factory class.
The factory will be injected into your classes rather than the instance of ILocalizer/IExceptionHandler and create the instance of it when the parameters are known.
public interface ILocalizerFactory
{
ILocalizer Create(ResourceDictionary appResDic, string projectName);
}
public class ILocalizerFactory
{
public ILocalizer Create(ResourceDictionary appResDic, string projectName)
{
var localizer = new Localizer(appResDic, projectName, "Languages", "Language", "en");
return localizer;
}
}
Using the facade Example from above:
public class CoreServicesFacade : ICoreServicesFacade
{
private readonly ILocalizer localizer;
public ILocalizer Localizer { get { return localizer; } }
public CoreServices(ILocalizerFactory localizerFactory, ...)
{
if(localizer==null)
throw new ArgumentNullException("localizerFactory");
this.localizer = localizerFactory.Create( Application.Current.Resources, Application.Current.ToString().Split('.')[0]);
}
}
Caveats & tips
Move Default configuration outside of the classes itself
Don't use such code inside your Localizer/ExceptionHandler classes.
_appResDic = appResDic ?? Application.Current.Resources;
_projectName = !string.IsNullOrEmpty(projectName) ? projectName : Application.Current.ToString().Split('.')[0];
_languagesDirectoryName = languagesDirectoryName.ThrowArgNullExIfNullOrEmpty("languagesFolder", "0X000000066::The languages directory name can't be null or an empty string.");
_fileBaseName = fileBaseName.ThrowArgNullExIfNullOrEmpty("fileBaseName", "0X000000067::The base name of the language files can't be null or an empty string.");
_fallbackLanguage = fallbackLanguage.ThrowArgNullExIfNullOrEmpty("fallbackLanguage", "0X000000068::The fallback language can't be null or an empty string.");
CurrentLanguage = _fallbackLanguage;
This pretty much makes it untestable and puts the configuration logic in the wrong place. You should only accept and validate the parameters passed into the constructor and determine the values and fall backs in either a) factory's create method or b) inside your bootstrapper (if runtime parameters aren't required).
Don't use View-related type inside your interfaces
Don't use ResourceDictionary in your public interfaces, this will leak View knowledge into your ViewModels and require you to have a reference to the assembly containing View/Application related code (I know I used it above, based on your Locator constructor).
If you need it, pass it as constructor Parameter and implement the class in Application/View assembly, while having your Interface in your ViewModel assembly). Constructors are implementation detail, and can be hidden (by implementing the class in a different assembly which allows reference to the class in question).
Static classes are evil
As you already realized, static classes are bad. Inject them is the way to go. Your Application will most likely need a navigation too. So you can put Navigation (Navigate to a certain View), MessageBoxes (display an information) and opening of new Windows (a kind of navigation too) into either one service or a navigation facade (similar to above one) and pass all services related to navigation as a single Dependency into your objects.
Passing parameters to ViewModel
Passing parameters can be a bit of a pain in "home-brew" frameworks and you shouldn't pass parameters via ViewModel constructors (prevents DI from resolving it or forcing you to use a factory). Instead consider writing a navigation service (or using exiting framework). Prims has it solved pretty nicely, you got a navigation service (which will do the navigation to a certain View and it's ViewModel and also offers INavigationAware interface with NavigateTo and NavigateFrom methods, which are called when one navigates to a new view (one of this methods parameters can be used to provide parameters to the ViewModel) and when navigating from a ViewModel (i.e. to determine if navigating from a view is viable or to cancel the navigation if necessary, for example: Asking the user to save or discard the data before navigating to the other ViewModel).
But that's bit off-topic.
Example:
public class ExampleViewModel : ViewModelBase
{
public ExampleViewModel(Example2ViewModel example2ViewModel)
{
}
}
public class Example2ViewModel : ViewModelBase
{
public Example2ViewModel(ICustomerRepository customerRepository)
{
}
}
public class MainWindowViewModel : ViewModelBase
{
public MainWindowViewModel(ExampleViewModel example2ViewModel)
{
}
}
// Unity Bootstrapper Configuration
container.RegisterType<ICustomerRepository, SqlCustomerRepository>();
// You don't need to register Example2ViewModel and ExampleViewModel unless
// you want change their container lifetime manager or use InjectionFactory
To get an resolve instance of your MainWindowViewModel simply do
MainWindowViewModel mainWindowViewModel = container.Resolve<MainWindowViewModel>();
and Unity will resolve all other dependencies (it will inject ICustomerRepository into Example2ViewModel, then inject Example2ViewModel into ExampleViewModel and finally inject ExampleViewModel into your MainWindowViewModel and return an instance of it.
The catch is: You can't use the container inside your ViewModels (though using it in View's code-behind is okay in your use case. However it's better to use navigation Service or a ViewModel Locator within your XAML (see Prism on how they did it)) .
So you need a navigation service of a kind, if you need to do it from ViewModels.
Here Caliburn.Micro was successfully combined with ModernUI.
But if we want to use multiple windows we also need to re-implement Caliburn's WindowManager to work properly with ModernUI. How can it be done?
UPDATE:
(Additional question about IoC-Container/Dependency Injection)
Ok, as I get it: I used a Constructor Injection here:
public class BuildingsViewModel : Conductor<IScreen>
{
public BuildingsViewModel(IWindowManager _windowManager)
{
windowManager = _windowManager;
}
}
As far as BuildingsViewModel resolved from IoC container,
container itself injected ModernWindowManager implementation of IWindowManager interface because of this line in Bootstrapper's Configure() method:
container.Singleton<IWindowManager, ModernWindowManager>();
If I resolving an object instance from container, it injects all needed dependencies. Like a tree.
1) So now I wonder how can I replace this line using an injection(with interface)?
_windowManager.ShowWindow(new PopupViewModel());
2) If I want my whole project match DI pattern, all objects instances must be injected into ModernWindowViewModel, that resolves from container first?
3) Is it okay to use Caliburn's SimpleContainer for whole project, or better use mature framework like Castle Windsor? Should I avoid mixing?
UPDATE2:
4) Integrating an IoC container into an existing application requires creating this container first(in Main() method of console app for example), and then all object instanses must grow from it with injected dependencies?
Simply create your own derived WindowManager and override EnsureWindow:
public class ModernWindowManager : WindowManager
{
protected override Window EnsureWindow(object rootModel, object view, bool isDialog)
{
var window = view as ModernWindow;
if (window == null)
{
window = new ModernWindow();
window.SetValue(View.IsGeneratedProperty, true);
}
return window;
}
}
Any views that you want to use as popups must be based on ModernWindow and must either use a LinkGroupCollection or you must set the ContentSource property of the window, otherwise there will be no content.
You could possibly make this View-First but it works ViewModel-First using the method above.
e.g. to popup my PopupView I did the following
PopupView.xaml
<mui:ModernWindow x:Class="TestModernUI.ViewModels.PopupView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mui="http://firstfloorsoftware.com/ModernUI"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300" ContentSource="/ViewModels/ChildView.xaml">
</mui:ModernWindow>
PopupViewModel.cs
public class PopupViewModel : Screen
{
// Blah
}
Code to popup the view from another ViewModel:
public void SomeMethod()
{
_windowManager.ShowWindow(new PopupViewModel()); // Or use injection etc
}
Don't forget to register ModernWindowManager in place of WindowManager in your container!
e.g. using CM's SimpleContainer
container.Singleton<IWindowManager, ModernWindowManager>();
Obviously the only downside I can see to the above is that you can't seem to put content directly in a ModernWindow, so you have to have two UserControls for every popup!
A workaround would be to change EnsureWindow in ModernWindowManager so that it created a UserControl based on ModernWindow and set the ContentSource to the URI of the view you want to load, this will trigger the content loader and wire up your ViewModel. I'll update if I get a minute to try it.
Update:
Ok, so at the moment it's very hacky, but this could be a starting point for something useful. Basically I'm generating a URI based on the namespace and name of the view.
I'm sure there is a more reliable way of doing this, but for my test project it works:
protected override Window EnsureWindow(object rootModel, object view, bool isDialog)
{
var window = view as ModernWindow;
if (window == null)
{
window = new ModernWindow();
// Get the namespace of the view control
var t = view.GetType();
var ns = t.Namespace;
// Subtract the project namespace from the start of the full namespace
ns = ns.Remove(0, 12);
// Replace the dots with slashes and add the view name and .xaml
ns = ns.Replace(".", "/") + "/" + t.Name + ".xaml";
// Set the content source to the Uri you've made
window.ContentSource = new Uri(ns, UriKind.Relative);
window.SetValue(View.IsGeneratedProperty, true);
}
return window;
}
My full namespace for my view was TestModernUI.ViewModels.PopupView and the URI generated was /ViewModels/PopupView.xaml which then was loaded and bound via the content loader automagically.
Update 2
FYI here is my Bootstrapper configure method:
protected override void Configure()
{
container = new SimpleContainer();
container.Singleton<IWindowManager, ModernWindowManager>();
container.Singleton<IEventAggregator, EventAggregator>();
container.PerRequest<ChildViewModel>();
container.PerRequest<ModernWindowViewModel>();
container.PerRequest<IShell, ModernWindowViewModel>();
}
Here I create the container, and register some types.
The CM services such as WindowManager and EventAggregator are both registered against their respective interfaces and as singletons so only 1 instance of each will be available at run time.
The view models are registered as PerRequest which creates a new instance every time you request one from the container - this way you can have the same window popup multiple times without strange behaviour!
These dependencies are injected into the constructor of any objects resolved at run time.
Update 3
In answer to your IoC questions:
1) So now I wonder how can I replace this line using an injection(with interface)? _windowManager.ShowWindow(new PopupViewModel());
Since your viewmodels will now usually need dependencies you need to have some way of injecting them into the instances. If PopupViewModel had several dependencies, you could inject them into the parent class but this would couple the parent viewmodel to PopupViewModel in some way.
There are a couple of other methods you can use to get an instance of PopupViewModel.
Inject it!
If you register PopupViewModel as PerRequest you will get a new instance of it every time you request it. If you only need one popup instance in your viewmodel you can just inject it:
public class MyViewModel
{
private PopupViewModel _popup;
private IWindowManager _windowManager;
public MyViewModel(PopupViewModel popup, IWindowManager windowManager)
{
_popup = popup;
_windowManager = windowManager;
}
public void ShowPopup()
{
_windowManager.ShowPopup(_popup);
}
}
The only downside is that the instance will be the same one if you need to use it multiple times in the same viewmodel, though you could inject multiple instances of PopupViewModel if you knew how many you needed at the same time
Use some form of on-demand injection
For dependencies which are required later on you can use on-demand injection such as a factory
I don't think Caliburn or SimpleContainer support factories out of the box, so the alternative is to use IoC.Get<T>. IoC is a static class which lets you access your DI container after instantiation
public void ShowPopup()
{
var popup = IoC.Get<PopupViewModel>();
_windowManager.ShowWindow(popup);
}
You need to make sure you have correctly registered the container in your bootstrapper and delegated any calls to CM's IoC methods to the container - IoC.Get<T> calls the bootstrapper's GetInstance and other methods:
Here's an example:
public class AppBootstrapper : BootstrapperBase {
SimpleContainer container;
public AppBootstrapper() {
Initialize();
}
protected override void Configure() {
container = new SimpleContainer();
container.Singleton<IWindowManager, ModernWindowManager>();
container.Singleton<IEventAggregator, EventAggregator>();
container.PerRequest<IShell, ModernWindowViewModel>();
// Register viewmodels etc here....
}
// IoC.Get<T> or IoC.GetInstance(Type type, string key) ....
protected override object GetInstance(Type service, string key) {
var instance = container.GetInstance(service, key);
if (instance != null)
return instance;
throw new InvalidOperationException("Could not locate any instances.");
}
// IoC.GetAll<T> or IoC.GetAllInstances(Type type) ....
protected override IEnumerable<object> GetAllInstances(Type service) {
return container.GetAllInstances(service);
}
// IoC.BuildUp(object obj) ....
protected override void BuildUp(object instance) {
container.BuildUp(instance);
}
protected override void OnStartup(object sender, System.Windows.StartupEventArgs e) {
DisplayRootViewFor<IShell>();
}
Castle.Windsor supports factories so that you can Resolve and Release your components and manage their lifetime more explicitly, but I won't go into that here
2) If I want my whole project match DI pattern, all objects instances must be injected into ModernWindowViewModel, that resolves from container first?
You only need to inject the dependencies that the ModernWindowViewModel needs. Anything that is required by children is automatically resolved and injected e.g.:
public class ParentViewModel
{
private ChildViewModel _child;
public ParentViewModel(ChildViewModel child)
{
_child = child;
}
}
public class ChildViewModel
{
private IWindowManager _windowManager;
private IEventAggregator _eventAggregator;
public ChildViewModel(IWindowManager windowManager, IEventAggregator eventAggregator)
{
_windowManager = windowManager;
_eventAggregator = eventAggregator;
}
}
In the above situation, if you resolve ParentViewModel from the container - the ChildViewModel will get all it's dependencies. You don't need to inject them into the parent.
3) Is it okay to use Caliburn's SimpleContainer for whole project, or better use mature framework like Castle Windsor? Should I avoid mixing?
You can mix, but it might be confusing as they won't work with each other (one container won't know about the other). Just stick with one container, and SimpleContainer is fine - Castle Windsor has a lot more features, but you might never need them (I've only used a few of the advanced features)
4) Integrating an IoC container into an existing application requires creating this container first(in Main() method of console app for example), and then all object instanses must grow from it with injected dependencies?
Yes, you create the container, then you resolve the root component (in 99.9% of applications there is one main component which is called the composition root), and this then builds the full tree.
Here is an example of a bootstrapper for a service based application. I'm using Castle Windsor and I wanted to be able to host the engine in a Windows service or in a WPF application or even in a Console Window (for testing/debug):
// The bootstrapper sets up the container/engine etc
public class Bootstrapper
{
// Castle Windsor Container
private readonly IWindsorContainer _container;
// Service for writing to logs
private readonly ILogService _logService;
// Bootstrap the service
public Bootstrapper()
{
_container = new WindsorContainer();
// Some Castle Windsor features:
// Add a subresolver for collections, we want all queues to be resolved generically
_container.Kernel.Resolver.AddSubResolver(new CollectionResolver(_container.Kernel));
// Add the typed factory facility and wcf facility
_container.AddFacility<TypedFactoryFacility>();
_container.AddFacility<WcfFacility>();
// Winsor uses Installers for registering components
// Install the core dependencies
_container.Install(FromAssembly.This());
// Windsor supports plugins by looking in directories for assemblies which is a nice feature - I use that here:
// Install any plugins from the plugins directory
_container.Install(FromAssembly.InDirectory(new AssemblyFilter("plugins", "*.dll")));
_logService = _container.Resolve<ILogService>();
}
/// <summary>
/// Gets the engine instance after initialisation or returns null if initialisation failed
/// </summary>
/// <returns>The active engine instance</returns>
public IIntegrationEngine GetEngine()
{
try
{
return _container.Resolve<IIntegrationEngine>();
}
catch (Exception ex)
{
_logService.Fatal(new Exception("The engine failed to initialise", ex));
}
return null;
}
// Get an instance of the container (for debugging)
public IWindsorContainer GetContainer()
{
return _container;
}
}
Once the bootstrapper is created, it sets up the container and registers all services and also plugin dlls. The call to GetEngine starts the application by resolving Engine from the container which creates the full dependency tree.
I did this so that it allows me to create a service or a console version of the application like this:
Service Code:
public partial class IntegrationService : ServiceBase
{
private readonly Bootstrapper _bootstrapper;
private IIntegrationEngine _engine;
public IntegrationService()
{
InitializeComponent();
_bootstrapper = new Bootstrapper();
}
protected override void OnStart(string[] args)
{
// Resolve the engine which resolves all dependencies
_engine = _bootstrapper.GetEngine();
if (_engine == null)
Stop();
else
_engine.Start();
}
protected override void OnStop()
{
if (_engine != null)
_engine.Stop();
}
}
Console App:
public class ConsoleAppExample
{
private readonly Bootstrapper _bootstrapper;
private IIntegrationEngine _engine;
public ConsoleAppExample()
{
_bootstrapper = new Bootstrapper();
// Resolve the engine which resolves all dependencies
_engine = _bootstrapper.GetEngine();
_engine.Start();
}
}
Here's part of the implementation of IIntegrationEngine
public class IntegrationEngine : IIntegrationEngine
{
private readonly IScheduler _scheduler;
private readonly ICommsService _commsService;
private readonly IEngineStateService _engineState;
private readonly IEnumerable<IEngineComponent> _components;
private readonly ConfigurationManager _configurationManager;
private readonly ILogService _logService;
public IntegrationEngine(ICommsService commsService, IEngineStateService engineState, IEnumerable<IEngineComponent> components,
ConfigurationManager configurationManager, ILogService logService)
{
_commsService = commsService;
_engineState = engineState;
_components = components;
_configurationManager = configurationManager;
_logService = logService;
// The comms service needs to be running all the time, so start that up
commsService.Start();
}
All of the other components have dependencies, but I don't inject those into the IntegrationEngine - they are handled by the container
Using Prism, I've got a INFRASTRUCTURE project, where I've got a view with two regions:
ContentRegion
SidebarRegion
There's no problem in SidebarRegion, because this is a especif view of this project. But ContentRegion is different, I've got another modules like ModuleA, ModuleB, and these one contain the view which must be show in ContentRegion.
I mean, the INFRASTRUCTURE PROJECT contains the generic view. And the others module contains a view which must be showed in ContentRegion
I'm using Prism and UNITY. Can you orient me about how can I do this? I guess I need to register the views but I'm lost.
You should be able to pass the Region Manager and Unity container to the modules through the constructor. Then you can use those to register your views in the Initialize method, like so:
public class ModuleA : IModule
{
private readonly IUnityContainer _container;
private readonly IRegionManager _regionManager;
public ModuleA(IUnityContainer container, IRegionManager regionManager)
{
_container = container;
_regionManager = regionManager;
}
public void Initialize()
{
_regionManager.RegisterViewWithRegion("ContentRegion",
() => _container.Resolve<NameOfYourView>());
}
}
And, of course, you have to make sure the modules are loaded properly, but I'm assuming that is done already.
Using an IOC-container like Unity,AutoFac or others you have to Register and Resolve the IInterface to get the instance. This you do in the app class the root of all.
After doing the Register/Resolve stuff I am creating my MainController and pass them ALL resolved Services like:
protected void Application_Start(object sender, EventArgs e)
{
var builder = new ContainerBuilder();
builder.Register<IUserService1, UserService1>();
builder.Register<IUserService2, UserService2>();
builder.Register<IUserService3, UserService3>();
builder.Register<IAnotherService, AnotherService>();
// And many more Services...
_container = builder.Build();
var userService1 = _container.Resolve<IUserService1>();
var userService2 = _container.Resolve<IUserService2>();
var userService3 = _container.Resolve<IUserService3>();
var anotherService = _container.Resolve<IAnotherService>();
var vm = new MainController(userService1,userService2,userService3,anotherService)
}
public class MainController
{
private UserController1 _userVM1;
private UserController2 _userVM2;
private UserController3 _userVM3;
public MainController(IUserService1 userService1,IUserService2 userService2,IUserService3 userService3,anotherService)
{
_userVM1 = new UserController1(userService1,anotherService);
_userVM2 = new UserController2(userService2,...,...);
_userVM3 = new UserController3(userService3,...,...,...);
}
}
// Such a Controller class needs to be created 10 times... and what I do here is typical for all Controllers driving the GUI
public class UserController1
{
private readonly IUserService1 _userService1;
public UserController1(IUserService1 userService1,IAnotherService anotherService)
{
_userService1 = userService1;
//Bind data to GUI
UserData1Collection = ConvertModelIntoViewModelCollection(userService1,anotherService);
}
public ObservableCollection<UserData1> UserData1Collection { get; set; }
private ObservableCollection<UserData1ViewModel> ConvertModelIntoViewModelCollection(IAnotherService anotherService)
{
var userData1ViewModelCollection = new ObservableCollection<UserData1ViewModel>();
_userService1.GetUserData1().ForEach(user =>
{
userData1ViewModelCollection.Add(new UserData1ViewModel(user, anotherService,...));
});
return userData1ViewModelCollection;
}
}
Now the question:
There is a lot of falling through/passing trough services because I have to call services when for example properties of viewmodels change via lost_focus on gui controls.
Is that all right what I do? Do you see any disadvantage? Or how would you do it?
Update
That DI stuff is a massiv attack on my vicious habits :P
Did you meant it that way Can?
Btw. why should I do that controller factory? Why then not a ServiceFactory too... then we are back to the ServiceLocator...
How do I get now that controller instances in my MainViewModel? via extending the Constructor of my MVM with many additional params? ending up with 30 params? ...
protected override void OnStartup(StartupEventArgs e)
{
IContainerBuilder builder = new ContainerBuilder();
// Firstly Register ALL existing Services
builder.Register<IAdminService, AdminService>();
builder.Register<IDocumentService, DocumentService>();
builder.Register<ILessonPlannerService, LessonPlannerService>();
builder.Register<IMediator, Mediator>();
builder.Register<IMainRepository, MainRepository>();
builder.Register<MainViewModel>();
IContainer _container = builder.Build();
// THEN Register ALL Controllers needing the previously registered Services
IControllerFactory factory = new ControllerFactory(builder);
IDailyPlanner controller1 = factory.Create<IDailyPlanner>();
IWeeklyPlanner controller2 = factory.Create<IWeeklyPlanner>();
SchoolclassAdministrationViewModel controller3 = factory.Create<SchoolclassAdministrationViewModel>();
// THEN Register the mainViewModel(MainController) which should take ALL Services and ALL Controller... WOW thats a massive Ctor param count... is that pure? Did you mean it that way???
MainViewModel mainViewModel = _container.Resolve<MainViewModel>();
//MainWindow mainWindow = _container.Resolve<MainWindow>();
//mainWindow.DataContext = mainViewModel;
//mainWindow.ShowDialog();
}
public class ControllerFactory : IControllerFactory
{
private readonly IContainerBuilder _builder;
private readonly IContainer _container;
/// <summary>
/// Takes the IOC container to register all Controllers
/// </summary>
public ControllerFactory(IContainerBuilder builder)
{
_builder = builder;
_builder.Register<SchoolclassAdministrationViewModel>();
_builder.Register<IDailyPlanner, LessonPlannerDailyViewModel>();
_builder.Register<IWeeklyPlanner, LessonPlannerWeeklyViewModel>();
_container = _builder.Build();
}
/// <summary>
/// Returns an Instance of a given Type
/// </summary>
public T Create<T>()
{
return _container.Resolve<T>();
}
}
Update2:
Now I changed my code that the MainViewModel accepts the IControllerFactory as Parameter and added these two lines of code to the App class:
builder.Register<IControllerFactory, ControllerFactory>();
builder.Register<IContainerBuilder, ContainerBuilder>();
That way I dont need to pass all controllers in the MainViewModel Ctor instead the MainViewModel gets the controller instances from the Factory.
Is there anything better I can do here? Or is that an acceptable good solution? I have no experience at all with DI so I ask :)
Update3
OK I did some code refactoring and made comments for others so they understand whats the final solution:
protected override void OnStartup(StartupEventArgs e)
{
IContainerBuilder builder = new ContainerBuilder();
// Firstly Register ALL existing Services
builder.Register<IAdminService, AdminService>();
builder.Register<IDocumentService, DocumentService>();
builder.Register<ILessonPlannerService, LessonPlannerService>();
builder.Register<IMediator, Mediator>();
builder.Register<IMainRepository, MainRepository>();
builder.Register<IControllerFactory, ControllerFactory>();
builder.Register<IDailyPlanner, LessonPlannerDailyViewModel>();
builder.Register<IWeeklyPlanner, LessonPlannerWeeklyViewModel>();
// Just for visual separation THEN register the MainController driving all other Controllers created via the IControllerFactory
builder.Register<MainViewModel>();
// Build the container
IContainer container = builder.Build();
// THEN Register the MainController which should take ALL IServices and the IFactory
MainViewModel mainViewModel = container.Resolve<MainViewModel>();
// LATER in the mainViewModel`s Ctor you can create all 10 Controller instances with the IControllerFactory like this
// _dailyPlannerController = controllerFactory.Create<IDailyPlanner>();
MainWindow mainWindow = new MainWindow();
mainWindow.DataContext = mainViewModel;
mainWindow.ShowDialog();
}
public class ControllerFactory : IControllerFactory
{
private readonly IContainer _container;
/// <summary>
/// Takes the IOC container to resolve all Controllers
/// </summary>
public ControllerFactory(IContainer container)
{
_container = container;
}
/// <summary>
/// Returns an Instance of a given Type
/// </summary>
public T Create<T>()
{
return _container.Resolve<T>();
}
}
Thank you very much for your time, #Can. I have learned a lot!
It seems to me that you have misunderstood how to use an IoC container. Instead of creating instances of your services and passing them as parameters, you need to ask the container to resolve them for you.
For example, you can refactor your code as follows to make use of IoC properly:
protected void Application_Start(object sender, EventArgs e)
{
var builder = new ContainerBuilder();
builder.Register<IUserService1, UserService1>();
builder.Register<IUserService2, UserService2>();
builder.Register<IUserService3, UserService3>();
builder.Register<IAnotherService, AnotherService>();
builder.Register<MainController, MainController>();
// And many more Services...
_container = builder.Build();
//let the container inject all the required dependencies into MainController..
var vm = _container.Resolve<MainController>();
}
The container in this case should control the lifecycle of your MainController object and make sure that all the dependencies (properties and constructor parameters that need to be initialized) are injected and populated.
What will happen is that the container will understand that to create an instance of MainController, it needs IUserService1, IUserService2 and so forth, and in turn will look if it can create any instances of those, by looking at the other types registered with the container. This will be done in a recursive manner to build up a dependency tree until all the dependencies of a class can be satisified. The resulting MainController you get will already have all the dependencies injected in it.
Ideally, you should call Resolve() in as little places as possible in order to structure your application in a way that there is only one root. For an in depth view into Dependency Injection, I strongly recommend the book Dependency Injection in .NET by Mark Seeman, which is in my opinion one of the best introduction to DI one can have.
UPDATE:
The reason why I suggested to use a ControllerFactory was because you had a lot of UserController classes in your MainController, and passing all those as a dependency you would end up with 10+ constructor parameters, not to mention that having to add more when you create new controllers. If your viewmodels only have dependency on one controller, then it doesn't make sense to use the factory in that way and you can have a direct dependency on the required controller.
As for ServiceFactory, it is not needed, because each of your classes are not likely to require ALL the service classes available, just some of them. In that case it is better to specify them explicitly for each service in the constructor.
You should also register all your instances in one place (or in small installer classes) instead of within constructor for different classes.
Here's a question that is more specific to MVVM that should get you going on how to structure your classes and dependencies:
How can I combine MVVM and Dependency Injection in a WPF app?