2

Up until this point, I have been learning IoC/DI with Castle.Windsor using ASP.NET MVC, but I have a side project that is being done in Windows Forms, and I was wondering if there is an effective way to use it for that.

My problem is in the creation of forms, services, etc. In ASP.NET MVC, there is a sort of 'Activator' that does this under the hood, but this isn't the case in Windows Forms. I have to create a new Form like var form = new fclsMain();, so a Form like ..

class fclsMain : System.Windows.Forms.Form
{
 private readonly ISomeRepository<SomeClass> someRepository;
 fclsMain(ISomeRepository<SomeClass> someRepository)
 {
  this.someRepository = someRepository;
 }
}

Falls kind of short. I would basically have to do ...

var form = new fclsMain(IoC.Resolve<ISomeRepository<SomeClass>);

Which as I have had pointed out in at least three of my questions isn't smart, because it's supposedly not the 'correct' usage of IoC.

So how do I work with Castle.Windsor and Windows Forms? Is there some way to design a Form Activator or something? I'm really lost, if I can't make a static IoC container that I can resolve from, what can I do?

Phuc Thai
  • 718
  • 7
  • 17
Ciel
  • 17,312
  • 21
  • 104
  • 199

3 Answers3

1

Here you are doing something that are not very "Dependency Injection"...

var form = new fclsMain(IoC.Resolve<ISomeRepository<SomeClass>);

The "new" is the problem... You have to call

var form = IoC.Resolve<fcls>();

the form of type fcls must be correctly configured via Fluent Registration API o

abrfra
  • 634
  • 2
  • 6
  • 24
0

You don't "have to" new-up a form, as you've said. I use WinForms and never call "new FormName()". It's always a dependency itself. Otherwise I'd have to stuff the constructor full of service locator calls.

I might use a ServiceLocator (as in another answer) BUT only at the very top level. For example I have a Command pattern implemented to intercept toolbar buttons. Looks something like this:

public void Handle(string commandName)
{
    var command = IoC.Resolve<ICommand>(RegisteredCommands[commandName]);
    command.Execute();
}

Then, in a simplified case, this is the kind of code written everywhere else:

public class ShowOptionsCommand : Command, ICommand
{
    private readonly IOptionsView _optionsView;

    public ShowOptionsCommand(IOptionsView optionsView)
    {
        _optionsView = optionsView;
    }

    public void Execute()
    {
        _optionsView.Show();
    }
}

Yes, I use a "service locator" but you will hardly ever see it. That's important to me, because having service locator calls all throughout the code (eg in every class) defeats some of the point of using dependency inversion of control & needs extra work to be testable etc

PandaWood
  • 8,086
  • 9
  • 49
  • 54
0

In order to use the same Castle container throughout your entire application, create a static class like:

public static class CastleContainer {
    private static IWindsorContainer container;

    public static IWindsorContainer Instance {
        get {
            if (container == null) {
                container = new WindsorContainer();
            }
            return container;
        }
        // exposing a setter alleviates some common component testing problems
        set { container = value; }
    }

    // shortcut to make your life easier :)
    public static T Resolve<T>() {
        return Instance.Resolve<T>();
    }

    public static void Dispose() {
        if (container != null) 
            container.Dispose();
        container = null;
    }
}

Then register/install all your components in the Main() method. You can also hook into the application shutdown event to call Dispose() (although this isn't critical).

Castle actually uses a Windows Forms app in their quick-start guide.

Edit:

The pattern I showed above is a variant of the service locator, which some people refer to as an anti-pattern. It has a bad reputation because, among other reasons, it liters your code base with references to Windsor. Ideally, you should only have a single call to container.Resolve<...>() to create your root form. All other services & forms are injected via constructors.

Realistically, you'll probably need a few more calls to Resolve, especially if you don't want to load every single corner of the application at startup. In the web world, the best practice is to hand off the container to the web framework. In the Windows Forms world you'll need to implement your own service locator, like above. (Yes, handing the container to the ASP.NET MVC framework is still a service locator pattern).

I've edited the above code example so that the static container is injectable; no resources are tied up in a static context. If you do end up creating your own service locator, you might also want to create a test utility like this one to make testing easier.

public static class TestUtilities 
{
    public static IContainer CreateContainer(Action<IContainer> extraConfig = null) 
    {
        var container = new WindsorContainer();
        // 1. Setup common mocks to override prod configuration
        // 2. Setup specific mocks, when provided
        if (extraConfig != null)
            extraConfig(container);
        // 3. Configure container with production installers
        CastleContainer.Instance = container;
        return container;
    }
}

This makes a shortcut for creating a new container that looks a lot like the production version, but with some services replaced with mocks. Some example tests might look like:

[Test]
public void SubComponentWorksGreat() 
{
    using (var container = TestUtilities.CreateContainer())
    {
        var subComponent = container.Resolve<SubComponent>();
        // test it...
    }
}

[Test]
public void SubComponentWorksGreatWithMocks() 
{
    var repoMock = new Mock<IRepository>();
    using (var container = TestUtilities.CreateContainer(c => 
            c.Register(Component.For<IRepository>().Instance(repoMock.Object))))
    {
        var subComponent = container.Resolve<SubComponent>();
        // test it with all IRepository instances mocked...
    }
}

One last note. Creating a full container for every test can get expensive. Another option is to create the full container but only using nested containers for the actual tests.

mqueirozcorreia
  • 905
  • 14
  • 33
kelloti
  • 8,705
  • 5
  • 46
  • 82
  • I don't understand. This was my first approach and I was told it was 'bad practice' by others here on Stack Overflow. – Ciel Jan 30 '11 at 03:11
  • Do you know why it's bad practice? I'm using that approach in an ASP.NET application and it works well. Can you give me a link to that question? thanks. – kelloti Jan 30 '11 at 03:13
  • http://stackoverflow.com/questions/4401244/castle-windsor-ioc-in-an-mvc-application – Ciel Jan 30 '11 at 03:18
  • I truthfully don't really know why it was considered so bad, but the more I dug the more I found people claiming it wasn't a good approach. Otherwise I would have just stuck with it. – Ciel Jan 30 '11 at 03:18
  • HA! I like you're comment on that SO answer. The accepted answer pointed out that `ContainerFactory.Current()`, the best practice, returns a static singleton WindosorContainer. My answer is exactly this, except that I'm doing the legwork instead of letting the MVC framework handle it. I think you can make arguments all day why this isn't a bad pattern to use. Trust your instincts on this one! – kelloti Jan 30 '11 at 03:25
  • I don't understand why this was down voted. It solved my problem, and it is a good example. – Ciel Feb 07 '11 at 14:15
  • 1
    @kelloti were you possibly confusing the best way to use IoC with "best practice" in using a service locator once you've already decided to resort to using it? Trusting your instincts can almost never be good advice in (software) engineering, I'm sorry, I'm not on board with that one. Always question you're own thinking. – PandaWood Aug 13 '13 at 23:58