Click here to Skip to main content
14,926,168 members
Articles / Desktop Programming / WPF
Article
Posted 26 May 2016

Stats

15.5K views
326 downloads
10 bookmarked

Leveraging IoC In WPF

Rate me:
Please Sign up or sign in to vote.
5.00/5 (4 votes)
26 May 2016CPOL7 min read
Using Dependency Injection to manage navigation and assigning data context in MVVM design.

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

Image 1 Image 2

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.

C#
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.

C#
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.

C#
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.

C#
internal static class Bootstrapper
{
    private static IWindsorContainer container;

    internal static IWindsorContainer InitializeContainer()
    {
       container = new WindsorContainer();
       container.AddFacility<typedfactoryfacility>(); // Provides automatically generated factories
       container.Install(FromAssembly.This()); 
       // Instantiates the installers that implements IWindsorInstaller in this assembly and invokes the constructor
       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.

Image 3

And a GIF image to give yourself an idea of what we are set to achieve.

Image 4

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.

C#
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.

C#
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.

C#
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.

C#
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.

C#
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

XML
<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.

C#
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

C#
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

C#
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.

C#
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.

C#
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.

Image 5

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.

C#
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.

C#
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 !

C#
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

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Dilip Nandakumar
Software Developer Bally Technologies
India India
I am a full stack developer working in .net technologies.

Comments and Discussions

 
QuestionGeneral Pin
Ian Carr10-Jun-21 5:23
professionalIan Carr10-Jun-21 5:23 
QuestionMy vote of 5 for simplicity in explanation! Pin
Shabana Parveen7-May-18 1:15
professionalShabana Parveen7-May-18 1:15 
QuestionIt's a very primitive PRISM Pin
PureNsanity27-May-16 9:29
professionalPureNsanity27-May-16 9:29 
PraiseRe: It's a very primitive PRISM Pin
Dilip Nandakumar29-May-16 20:05
professionalDilip Nandakumar29-May-16 20:05 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.