Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / WinForms

Spring injection in WinForms with attributes

0.00/5 (No votes)
7 Apr 2009Apache3 min read 25K  
Library for injection of Spring objects in WinForms, without references to IApplicationContext or Spring.

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:

XML
<?xml version="1.0" encoding="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:

XML
<?xml version="1.0" encoding="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:

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

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

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

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

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

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

License

This article, along with any associated source code and files, is licensed under The Apache License, Version 2.0