Introduction
This library will inject Spring dependencies in WinForms and UserControls with just an attribute.
Background
In ASP.NET, for example, the integration can be done in the configuration file, web.config. In WinForms, explicit calls to Context.GetObject()
are needed (we can use a factory, but you still have to have the code somewhere). Injecting a presenter to a UserControl
inside a Form
has the worst drawbacks, as the factory cannot be used inside the VS2005 designer.
If you want to configure you Forms and UserControls with spring XML and unobtrusive code, you can use the RichClient project.
Another approach is to use attributes that indicate which properties should be injected by Spring in Forms and UI components. This way of handling is used in Wicket (a Java-Web framework) with @SpringBean
. This article is inspired by this feature of Wicket, and it creates an annotation [SpringObject]
for .NET and WinForms.
Originally, this library was for using the MVP pattern, and the things that should be injected are basically Presenters.
Configuration of Spring
If you already have your WinForms application configured with Spring, skip to the next section.
First, you need to add Spring.Core as a reference for your project. Then, you need to add the App.Config file, with something like this:
="1.0"="utf-8"
<configuration>
<configSections>
<sectionGroup name="spring">
<section name="context"
type="Spring.Context.Support.ContextHandler, Spring.Core" />
</sectionGroup>
</configSections>
<spring>
<context>
<resource uri="application-context.xml" />
</context>
</spring>
</configuration>
This file makes a reference to an application-context.xml that has the definitions of the objects for Spring:
="1.0"="utf-8"
<objects xmlns="http://www.springframework.net">
<object id="objectDao"
type="TestSpringAttribute.TestObjectDao,TestSpringAttribute"
singleton="true">
</object>
</objects>
We just add a single object for testing purposes.
Using the code
Mostly, all you have to do is use a SpringForm
and a SpringUserControl
as the base for your Forms and UserControls, respectively, and insert the SpringObject
attribute to the properties or fields that are Presenters or other object from Spring. The Name
parameter must have the id
of the object in the Spring configuration. If the Name
parameter is missing, the injector will try to guess the name with the name of the property or the field.
An example of a SpringForm
with an objectDao
injected in _injectableObject
:
using Spring.WinForm;
namespace TestSpringAttribute
{
public partial class Form1 : SpringForm
{
public Form3()
{
InitializeComponent();
}
private TestObjectDao _injectableObject;
[SpringObject(Name = "objectDao")]
public TestObjectDao InjectableObject
{
get { return _injectableObject; }
set { _injectableObject = value; }
}
}
}
An example for a UserControl
:
using Spring.WinForm;
using System.ComponentModel;
namespace TestSpringAttribute
{
public partial class UserControl1 : SpringUserControl
{
public UserControl1()
{
InitializeComponent();
}
private TestObjectDao _objectDao;
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[SpringObject(Name = "testObjectDao")]
public TestObjectDao ObjectDao
{
get { return _objectDao; }
set { _objectDao = value; }
}
}
}
When putting the injected object in a UserControl
, be careful to add this attribute as well:
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
because when you insert the UserControl
in a Form
with the designer, it will set all the properties with default values (a null
object in our case).
You can also put the attribute SpringObject
to fields directly, like this:
using Spring.WinForm;
namespace TestSpringAttribute
{
public partial class UserControl2 : SpringUserControl
{
public UserControl2()
{
InitializeComponent();
}
[SpringObject(Name = "testObjectDao")]
private TestObjectDao _objectDao;
}
}
Implementation of the injector
The implementation of the injection is done using Reflection, looking at each field and property, and analyzing if it has the SpringObject
attribute:
using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using Spring.Context.Support;
using Spring.Context;
namespace Spring.WinForm
{
static public class InjectSpringAttribute
{
static public void InjectObject(object control)
{
foreach (PropertyInfo property in control.GetType().GetProperties())
{
foreach (object attribute in property.GetCustomAttributes(true))
{
if (attribute.GetType() == typeof(SpringObject))
{
SpringObject inject = (SpringObject)attribute;
if (inject.Name == null)
inject.Name = property.Name[0].ToString().ToLower() +
property.Name.Substring(1);
property.SetValue(control, GetObjectAttribute(inject.Name),
null);
}
}
}
foreach (FieldInfo field in control.GetType().GetFields(
BindingFlags.NonPublic | BindingFlags.Instance))
{
foreach (object attribute in field.GetCustomAttributes(true))
{
if (attribute.GetType() == typeof(SpringObject))
{
SpringObject inject = (SpringObject)attribute;
if (inject.Name == null)
{
inject.Name = field.Name[0].ToString().ToLower() +
field.Name.Substring(1);
if (inject.Name[0] == '_')
inject.Name = inject.Name.Substring(1);
}
field.SetValue(control, GetObjectAttribute(inject.Name));
}
}
}
}
private static object GetObjectAttribute(String nameObject)
{
IApplicationContext ctx = ContextRegistry.GetContext("spring.root");
if (ctx != null)
{
return ctx.GetObject(nameObject);
}
return null;
}
}
}
The code for SpringUserControl
and SpringForm
is almost the same:
using System.Windows.Forms;
namespace Spring.WinForm
{
public partial class SpringForm : Form
{
public SpringForm()
{
InitializeComponent();
InjectSpringAttribute.InjectObject(this);
}
}
}
using System.Windows.Forms;
namespace Spring.WinForm
{
public partial class SpringUserControl : UserControl
{
public SpringUserControl()
{
InitializeComponent();
InjectSpringAttribute.InjectObject(this);
}
}
}
Points of interest
Using Reflection to inject Spring was fairly easy, and you shouldn't mix this approach in your forms with definitions of forms in the Spring context (you may get in a infinite loop when initializing Spring). Internally, the Spring controls use Reflection for the properties and fields that the objects have in their definitions.
I had some problems when the controls and forms where loaded in the designer (as Spring was called to inject the properties inside Visual Studio and threw an exception). You should not reference the injected properties or fields in the constructor (as it is called by the Designer).