Introduction
Let me start by introducing IoC – Inversion of Control. Software is a combination of fixed parts and moving parts. The way I see it IoC is used to manage the moving parts of your code by providing the fixed parts of your code the choice and the freedom to choose the moving parts dynamically through an external framework rather than tightly coupling fixed parts with the moving parts making code difficult to one thing constant in software world ‘CHANGE’. IoC can be implemented in multiple ways, we are going to use dependency injection and factory pattern, the external framework in this case is our DI container castle windsor. We are going to use DI to facilitate loose coupling among views, view models and business logic. That’s not all; we are also going to see how Ioc actually makes life easier in using tree view and wizard in a WPF application.
To understand this article you need to have at least basic knowledge of:
- C# language
- WPF
- dependency injection and factory design pattern
Background
The scenario was that we had built a windows service with an architecture using DI and CQRS pattern. Later we decided to build a WPF application that shared the common repositories with the service. The UI team came up with a WPF project that typically did not use DI. I was bestowed upon the task to transform the WPF UI project to use DI to reuse the existing repositories created. As I overcame the challenge I realized the code transformation did not just adhere to our architecture using DI but also become more maintainable, readable, clean as well solved few design problems.
Using the code
I am going to explain the code necessary to wire castle windsor container in your project.
Step 1
Define corresponding abstractions (Interfaces) for your concrete views and view models. The role of interfaces is to facilitate loose coupling amongst other views and view models. A class diagram for my sample employee management WPF application is as follows
Step 2
Create ViewSelector
that overrides the DefaultTypedFactoryComponentSelector
of windsor container as follows. The role of ViewSelector
is to return the abstract type of the view when the view name is passed as argument.
class ViewSelector : DefaultTypedFactoryComponentSelector
{
private const string DotSeperator = ".";
private const string InterfacePrefix = "I";
private const string ViewDirectory = "Interfaces.Views";
private const string ViewSuffix = "View";
protected override string GetComponentName(MethodInfo method, object[] arguments)
{
return null;
}
protected override Type GetComponentType(MethodInfo method, object[] arguments)
{
StringBuilder typeName = new StringBuilder();
typeName.Append(Assembly.GetExecutingAssembly().GetName().Name);
typeName.Append(DotSeperator);
typeName.Append(ViewDirectory);
typeName.Append(DotSeperator);
typeName.Append(InterfacePrefix);
typeName.Append(arguments[0]);
typeName.Append(ViewSuffix);
return Type.GetType(typeName.ToString());
}
}
Step 3
Create an interface IViewFactory
. The role of IViewFactory is to return the concrete view instance when view name is passed as argument.
public interface IViewFactory
{
IView GetView(string viewName);
}
Step 4
Create ViewsInstaller
class that implements IWindsorInstaller
of castle windsor. The role of ViewsInstaller
is to register the concrete implementation of views and viewmodels with abstract Interfaces. In other words this is letting DI container know the concrete implementations to inject at runtime for abstract interfaces.
public class ViewsInstaller : IWindsorInstaller
{
private IWindsorContainer _container;
private string _assemblyName = Assembly.GetExecutingAssembly().FullName;
public void Install(IWindsorContainer container, IConfigurationStore store)
{
this._container = container;
this.RegisterViews();
this.RegisterViewModels();
}
private void RegisterViews()
{
this._container.Register(
Component.For<itypedfactorycomponentselector>().ImplementedBylt;viewselector>(),
Castle.MicroKernel.Registration.Classes.FromAssemblyNamed(this._assemblyName)
.BasedOn(typeof(IView))
.WithService.FromInterface()
.Configure(c => c.LifeStyle.Is(LifestyleType.Transient)),
Component.For<iviewfactory>().AsFactory(c => c.SelectedWith<viewselector>()).LifestyleSingleton()
);
}
private void RegisterViewModels()
{
this._container.Register(
Castle.MicroKernel.Registration.Classes.FromAssemblyNamed(this._assemblyName)
.BasedOn(typeof(IViewModel))
.WithService.FromInterface()
.Configure(c => c.LifeStyle.Is(LifestyleType.Transient)));
}
}
Step 5
Create a Bootstrapper
that creates an instance of the DI container.
internal static class Bootstrapper
{
private static IWindsorContainer container;
internal static IWindsorContainer InitializeContainer()
{
container = new WindsorContainer();
container.AddFacility<typedfactoryfacility>(); container.Install(FromAssembly.This());
return container;
}
}
As you will see that we have not provided explicit implementation for our IViewFactory
rather we have added typedfactoryfacility
to our windsor container there by making sure that castle windsor provides the implementation for IViewFactory
. We have used factory pattern to dynamically generate views when view name is passed.
OK! Now we are all set to put into use our DI container in our application.Let us dive into the use case. Here is a wireframe showcasing the UI design of my WPF application.
And a GIF image to give yourself an idea of what we are set to achieve.
Pardon me for the UI. I’m a beginner in xaml
First thing first I need to call the Bootstrapper
in application start and instantiate the DI container for my application. Then explicitly resolve the IMainView to get the concrete MainView
.
public partial class App : Application
{
private IWindsorContainer _container;
private IMainView _mainView;
public App()
{
this._container = Bootstrapper.InitializeContainer();
}
private void Application_Startup(object sender, StartupEventArgs e)
{
this._mainView = this._container.Resolve<imainview>();
this._mainView.ShowDialog();
}
}
In MainView
we need the instance of ShellView
. So in constructor we set up ShellView
to be injected.
public MainView(IShellView shellView)
{
InitializeComponent();
this.grdContainer.Children.Clear();
this.grdContainer.Children.Add((UIElement)shellView);
}
And in ShellView
we need the instance of ViewFactory
and ShellViewModel
.
public ShellView(IShellViewModel shellViewModel, IViewFactory viewFactory)
{
InitializeComponent();
this._viewFactory = viewFactory;
this.DataContext = shellViewModel;
}
As you can see in wireframe the ShellView
contains the TreeView
control. Now let’s see how to leverage DI in using TreeView
control. To bind data to TreeView
create a class ViewMenu
.
public class ViewMenu
{
private string menuName;
private string itemName;
private string toolTip;
public bool IsRoot { get; set; }
public bool IsExpand { get; set; }
public List<viewmenu> SubMenuItems { get; set; }
public string MenuName
{
get { return menuName; }
set { menuName = value; }
}
public string ItemName
{
get { return itemName; }
set { itemName = value; }
}
public string ToolTip
{
get { return toolTip; }
set { toolTip = value; }
}
}
And in shell view populate the viewmenu collection.
List<viewmenu> subMenus = new List<viewmenu>();
subMenus.Add(new ViewMenu() { MenuName = "Employee Profile", ItemName = "EmployeeProfile", ToolTip = "Employee Profile" });
subMenus.Add(new ViewMenu() { MenuName = "Employee Contact", ItemName = "EmployeeContact", ToolTip = "Employee Contact" });
subMenus.Add(new ViewMenu() { MenuName = "Employee Registration", ItemName = "EmployeeRegistration", ToolTip = "Employee Registration" });
this.ViewMenuItems = new ObservableCollection<viewmenu>();
this.ViewMenuItems.Add(new ViewMenu() { MenuName = "Employee", ItemName = "EmployeeProfile", ToolTip = "Settings", SubMenuItems = subMenus });
Here is the xaml code for TreeView
<TreeView x:Name="MytreeView" ItemsSource="{Binding ViewMenuItems}" helper:TreeViewExtension.SelectedItem="{Binding ViewSelected, Mode=TwoWay}" ScrollViewer.HorizontalScrollBarVisibility="Hidden" ScrollViewer.VerticalScrollBarVisibility="Hidden" Margin="0" ItemTemplate="{DynamicResource Navigation_DataTemplate}" />
When a menu item is selected, the ViewSelected
property is set.
public ViewMenu ViewSelected
{
get
{
return this._viewSelected;
}
set
{
if (this._viewSelected != value)
{
this._viewSelected = value;
this.ChildViewSelected(this._viewSelected.ItemName);
}
}
}
Here is what code to load the corresponding view based on ViewMenu
selected without using DI looks like
public IView ChildViewSelected(string viewName)
{
switch (viewName)
{
case "EmployeeProfile":
EmployeeProfileView employeeProfileView = new EmployeeProfileView();
employeeProfileView.DataContext = new EmployeeProfileViewModel();
break;
case "EmployeeContact":
EmployeeContactView employeeContactView = new EmployeeContactView();
employeeContactView.DataContext = new EmployeeContactViewModel();
break;
}
}
Looks simple right! Wait… What happens when you add a submenu to your ViewMenuItems
collection? You will have to remember to add a case for each submenu added. Doesn’t it become cumbersome to maintain. Also you are tightly coupling your ShellView
with the other view objects. It gets simpler when using DI. Let’s see
private void OnChildViewSelected(string itemName)
{
IView viewSelected = this._viewFactory.GetView(itemName);
this.pnlWorkArea.Children.Clear();
this.pnlWorkArea.Children.Add((UIElement)viewSelected);
}
We call the ViewFactory
to get the view instance for the corresponding view name. The Windsor gets the view type using ViewSelector
and instantiates using the ViewFactory
implementation the windsor generates.
As you can see the code is clean and readable. We have removed the dependency of other concrete view implementation in shell view thereby achieving loose coupling. Also, we have shifted the responsibility of view selection and instantiation to ViewSelector
and ViewFactory
respectively. The code is more maintainable as you do not have to worry about adding a case for every submenu added. All one has to do is to create a view and implement IView interface.
Is that all ? No, the major advantage of using Dependency Injection which is loose coupling can come handy when you have to customize your application for specific audience. Say you have multiple representation of EmployeeContactView
. You can simply set the required view name in the ViewMenuItems
collection and the ViewFactory
will take care of the rest.
Now as everything works seamlessly for tree view control, let’s see if there is any way we could improve our DI container set up. When you recall you would have noticed that in ShellView
constructor we would have explicitly assigned DataContext
to shell view model. The same would be the case for other views using DI as well. We could skip this step by creating a generic solution that assigns the DataContext
to the view model injected. To achieve it we need to create a ViewActivator
that overrides the DefaultComponentActivator
of windsor container.
protected override object CreateInstance(Castle.MicroKernel.Context.CreationContext context, ConstructorCandidate c, object[] arguments)
{
var component = base.CreateInstance(context, c, arguments);
this.AssignViewModel(component, arguments);
return component;
}
private void AssignViewModel(object component, object[] arguments)
{
var frameworkElement = component as FrameworkElement;
if (frameworkElement == null || arguments == null)
{
return;
}
var vm = arguments.Where(a => a is IViewModel).FirstOrDefault();
if (vm != null)
{
frameworkElement.DataContext = vm;
}
}
Also make windsor use ViewActivator
by explicitly specifying the activator type in views installer.
Castle.MicroKernel.Registration.Classes.FromAssemblyNamed(this._assemblyName)
.BasedOn(typeof(IView))
.WithService.FromInterface()
.Configure(c => c.LifeStyle.Is(LifestyleType.Transient)
.Activator<viewactivator>());
Alright we saved us some time by tweaking the DI container a bit. Now let’s jump to the second part of our use case, Wizards.Here is a GIF image of the wizard in my sample application.
The idea is pretty much the same as in TreeView
control, to use our ViewFactory
to switch between views. But there is one additional thing to accomplish, to use same view model instance as DataContext
for all the views of the wizard in each scope.
Wait why use the same view model instance for the entire wizard ?
The wizard was created only for presentation purpose in first place. The view model is independent of UI and should be designed based on responsibilities of binding data to View. In this case a single view model for employee registration suffices. It reduces additional overhead when mapping data and saving to db. Also, makes it easier to validate the entire wizard.
So how do we achieve it ? We need to first become aware of one additional feature of DI containers, LifeStyle. Lifestyle controls in what scope instances are reused and when to release them.
So let’s analyze which life style suits our use case.
We have used the transient lifestyle for our view models. Each time an instance of a view model is needed, container will produce a new one, never reusing them. So it doesn’t fit the use case.
Using singleton lifestyle will create the view model first time someone requests it, and subsequently reuse every time it's needed. Sounds good right. But there is a caveat. Our use case needs the same view model instance only per employee registration scope not per application.
Fortunately windsor container offers a lifestyle suitable for our use case – LifestyleBoundToNearest
but unfortunately the lifestyle doesn’t work when factory pattern is used. I had to use Lifestyle scoped which windsor added to specify arbitrary scope of component instance lifetime/reuse. I had to take the help of Guid to define my scope.
We need to provide an implementation for IScopeAccessor
.
class CustomScopeAccessor : IScopeAccessor
{
private static readonly ConcurrentDictionary<guid, ilifetimescope=""> Collection = new ConcurrentDictionary<guid, ilifetimescope="">();
public ILifetimeScope GetScope(Castle.MicroKernel.Context.CreationContext context)
{
return Collection.GetOrAdd((Guid)Application.Current.Resources["ScopeId"], id => new DefaultLifetimeScope());
}
}
Also, we have to explicitly specify the lifestyle for employee registration view model in our views installer.
this._container.Register(
Component.For<iemployeeregistrationviewmodel>()
.ImplementedBy(typeof(EmployeeRegistrationViewModel)).LifestyleScoped<customscopeaccessor>());
Before we complete, I would like to stress upon the need to release any explicitly resolved instances and dispose the container when application ends to avoid memory leaks !
private void Application_Exit(object sender, ExitEventArgs e)
{
this._container.Release(this._mainView);
this._container.Dispose();
}
Conclusion
As you can see leveraging Dependency Injection in WPF can come handy in design apart from facilitating loose coupling and increasing maintainability and readability of code. Any comments are appreciated!
References
https://visualstudiomagazine.com/articles/2011/10/01/wpf-and-inversion-of-control.aspx
https://github.com/castleproject/Windsor/blob/master/docs/lifestyles.md
https://github.com/castleproject/Windsor/blob/master/docs/implementing-custom-scope.md
Revision History
23-05-2016
Original Article