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?
In order to use the same Castle container throughout your entire application, create a static class like:
Then register/install all your components in the
Main()
method. You can also hook into the application shutdown event to callDispose()
(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.
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:
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.
Here you are doing something that are not very "Dependency Injection"...
The "new" is the problem... You have to call
the form of type fcls must be correctly configured via Fluent Registration API o
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:
Then, in a simplified case, this is the kind of code written everywhere else:
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