Home > View Post

The Awesome Power of IoC Part III

In this somewhat delayed (sorry) part of the series on IoC (see Part I and Part II) I want to try something a little different.

We're going to use Castle's Windsor Container (an open source IoC provider) to help us build a loosely coupled and extensible forms based application. First though, I'm going to need to set the scene so you'll have to bear with me. It'll be fun though, so don't go away just yet. Also, the full source of the example will be available for download at the end of the post.

Setting the scene

We're going to build a very simple WinForm UI to manage a very simple database. Yup, some good old CRUD stuff.

Let's start by looking at our very simple database schema:

TeachersAndStudents database schema

Jolly good. We have Student records that are linked to a Teacher record - what could be easier?

Therefore, in our UI, we're going to need:

  • an Add Teacher form
  • an Add Student form
  • an Edit Teacher form
  • an Edit Student form, with the ability to choose a teacher
  • a Search for Teachers form
  • a Search for Students form
We're also going to need some navigation and a way of linking a teacher to a student. The Add and Edit versions of each form are going to be almost identical so we'll reuse them and just change a few things dynamically.

We also need to support the ability to reference one record in another. Specifically, to associate a Teacher with the a Student (remember our schema?). Most systems use a kind of lookup control and that's exactly what we'll do too.

The Lookup User Control

Our lookup user control will have three main parts - A (readonly) textbox showing the description of the associated record (e.g. the Teacher's name), a button to allow you to change the assigned record and an Integer property storing the Id of the associated record.

In more detail

The idea behind this article is to use IoC to implement a very simple mini-framework to create a very extensible and manageable little app. As always, the first thing we're going to need is some interfaces for our forms.

Our IEasyForm interface will have two methods - one to display a form in 'Edit' mode (which will need an Id so we can load up the appropriate record) and one to display a form in 'Add' mode (which won't need an Id because it will be empty).

namespace Tjoc.Ioc.EasyForms.Framework
{
    public interface IEasyForm
    {
        void ShowAdd();
        void Show(int id);
    }
}

Our IEasySeachFor interface will be implemented by all our search forms. They'll always be used in a Dialog fashion and therefore we simply use the ShowDialog method that we automatically inherit from the Form class. But we'll need to add two properties, one to get the Id of the selected record and one to get the description.

namespace Tjoc.Ioc.EasyForms.Framework
{
    public interface IEasySearchFor
    {
        DialogResult ShowDialog();
        int ResultId { get; }
        string ResultDescription { get; }
    }
}

Provided our forms obey (implement) these simple contracts (interfaces) then our mini-framework will allow them to play happily together. We'll be needing some implementations of these interfaces then and here's what our TeacherForm looks like (in Edit mode though it looks much the same in Add mode) which inherits from System.Windows.Forms.Control (as usual) and implements IEasyForm.

The Edit Teacher Form

... and the Teacher Search which implements IEasySearchFor ...

The Teacher Search Form

Finally, the Student Form and Student Search:

The Edit Student Form

The Student Search form

Note the use of our lookup control in the StudentForm too. Cool.

Building the navigation

The navigation in our mini-framework is going to be a matter of configuration, so let's invent some Xml (that can be deserialized into a suitably logical object model):

<easyFormsMenu xmlns="urn:thejoyofcode-com:easy-forms:menus:2006-10">
    <easyMenuItems>
        <easyMenuItem>
            <name>Teacher</name>
            <action type="noAction" />
            <easyMenuItems>
                <easyMenuItem>
                    <name>Add Teacher</name>
                    <action type="create" providerKey="TeacherForm"/>
                </easyMenuItem>
                <easyMenuItem>
                    <name>Edit Teacher</name>
                    <action type="rud" providerKey="TeacherForm" supportingProviderKey="TeacherSearch"/>
                </easyMenuItem>
            </easyMenuItems>
        </easyMenuItem>
        <!-- ... etc ... -->

Not the most exciting read in the world but I bet you've guessed what the menu looks like already. Well done.

You may have noticed that we give each easyMenuItem potence through its <action> element. Also, pay close attention to the providerKey and supportingProviderKey attributes, we'll be needing them shortly.

The first job of our winform application is to read our menu configuration file and dynamically create the menu:

The EasyMenu in action :)

At last, some IoC

This is where it gets a bit more interesting. When we dynamically create the menu, we stuff the details of the action element into the menuItem's Tag property for recall later. Now, when the user clicks the menu item, we can work out what should happen, based on the action type.
  • noAction - nothing!
  • add - display the Add form
  • rud - display the Edit form
But which 'form' class is to be displayed. You guessed it! The provider keys map to ids in the Castle Windsor Container's configuration file.

<configuration>
    <components>
        <component id="TeacherForm" service="Tjoc.Ioc.EasyForms.Framework.IEasyForm, Tjoc.Ioc.EasyForms"

type="Tjoc.Ioc.EasyForms.Forms.TeacherForm, Tjoc.Ioc.EasyForms" lifestyle="Transient"/>
        <component id="TeacherSearch" service="Tjoc.Ioc.EasyForms.Framework.IEasySearchFor, Tjoc.Ioc.EasyForms"

type="Tjoc.Ioc.EasyForms.Forms.TeacherSearch, Tjoc.Ioc.EasyForms" lifestyle="Transient"/>
    <!-- ... etc ... -->

So our menu click event simply fulfils the required IEasyForm or IEasySearchFor using the provider key and the WindsorContainer (Note: the code is a little different in the download as the container has been wrapped).

void menu_Click(object sender, EventArgs e)
{
    EasyMenuAction action = (EasyMenuAction)((ToolStripMenuItem)sender).Tag;
    
    IEasyForm form;
    IEasySearchFor search;
    
    switch (action.Type)
    {
        case EasyMenuType.NoAction:
            break;
        case EasyMenuType.Create:
            form = (IEasyForm) _container[action.ProviderKey];
            form.ShowAdd();
            break;
        case EasyMenuType.Rud:
            search = (IEasySearchFor) _container[action.SupportingProviderKey];
            form = (IEasyForm) _container[action.ProviderKey];
            if (search.ShowDialog() == DialogResult.OK)
            {
                form.Show(search.SelectedId);
            }
            break;
    }
}

And we're away! Sweet.

But it gets sweeter

All of our forms have no public default constructor - they all need to be passed a data access layer component: IStudentDal or ITeacherDal.

public TeacherForm(ITeacherDal dal)
{
    // etc
}

public StudentForm(IStudentDal dal)
{
    // etc
}

Once again, because these interfaces are configured, Castle takes care of the required dependency injection without me having to worry about it. Tidy. Very tidy.

You can imagine how easy it would have been to inject your controller/presenter (as I should have done but wanted to focus on one thing at a time in the examples) and how testable this would be.

Oh, and that lookup control

Finally, let's look at that lookup control we mentioned earlier. You'll remember that we used it to create the relationship between students and their teachers. The interaction should allow for a user to click the ellipsis ("...") and search for a teacher. It'll

be even more fun if we could right click on the (readonly) textbox and choose to view the full details of the currently selected Teacher.

There's a clear opportunity for some reuse here. We can use the TeacherSearch to search for the right teacher, and the TeacherForm to view their details. What we want to do now is use our IEasySearchFor and IEasyForm interfaces inside our new user control to make it as loosely coupled and extensible as the rest of the mini-framework.

The natural choice is to use a User Control (then we can use the designer to throw together the other controls). We add two string properties: "ViewProviderKey" and "SearchProviderKey" which can be used by the control to retrieve the IEasyForm and IEasySearchFor instances from the IoC container.

private void _btnLookup_Click(object sender, EventArgs e)
{
    IEasySearchFor search = (IEasySearchFor) _container[_searchProviderKey];
    if (search.ShowDialog() == DialogResult.OK)
    {
        _selectedId = search.SelectedId;
        _txtDescription.Text = search.SelectedDescription;
    }
}
    
So all we have to do is drag the user control onto a form and configure its provider keys at design time. Doddle.

The full source for this example is available for download.

Enjoy.

Download the source

For brevity, I missed out a lot of code from the post so if the post has piqued your interest at all - you should download the code and have a nosy.

Remember, you'll need to have installed the Castle MSI to build and run the project. Note that I used Bamboo Prevalence for data persistence because it comes with the MSI, is an interesting facility available through the Windsor Container and means you don't have to worry about having a database handy to get the code up and running. Prevalence is a really interesting idea and I hope to write a brief post about it shortly, so stay tuned. Note that I also used the PropertyListView control in the search forms.

Important - I want to point out that this 'mini-framework' is mostly playtime and doesn't begin to go far enough but I hope it demonstrates how IoC can help you think about contracts (interfaces) and loose coupling outside of the service space. Whilst we always appreciate feedback, I am aware that the current codeset isn't always following best practice - it's just for fun :)

Tags: WinForms.NET

 
Josh Post By Josh Twist
5:36 AM
16 Oct 2006

» Next Post: DataBinding made simple
« Previous Post: SSIS Compress File Task Fix

Comments are closed for this post.

© 2005 - 2017 Josh Twist - All Rights Reserved.