Click here to Skip to main content
15,353,841 members
Articles / Programming Languages / C#
Posted 13 Sep 2004


84 bookmarked

Looking for the missing link - Binding business objects to WinForms

Rate me:
Please Sign up or sign in to vote.
4.77/5 (29 votes)
27 Sep 20046 min read
Simplifying the creation of a user interface for business objects while providing a consistent mechanism for handling IsDirty and IsValid in the UI.

Sample Image - bindingmanager.gif



I guess most readers agree that a well designed application consists of some logical layers or tiers. Common sense is to at least separate the user interface from the underlying business objects and rules and the database access layer. Usually, business objects (also referred to as business entities - I use these terms here equally) are available in all layers and are the data source for Windows forms. Binding these business objects to forms can be a boring and error-prone task. With a new WinForm application coming up, I decided to finally tackle this inconvenience. I found myself writing code like this just too many times to tolerate this monotony and possible error source any longer:

private void initialize() 
    txtName.Text = _person.Name;
    dateOfBirth.Value = _person.DateOfBirth;
    chkActive.Checked = _person.Active;
private void save()
   _person.Name = txtName.Text;
   _person.DateOfBirth = dateOfBirth.Value;
   _person.Active = chkActive.Checked;

This might be a non-issue when there are only a few fields, everything is specified and will not change in the course of a project, and the project contains only a few forms. Facing, however, maybe up to hundred or more forms, I wanted to bind these objects in the designer, assigning each input control a property of the business entity with just a few clicks. A few months ago, I covered a way to bind XML documents - config files in particular - to WinForms, so I thought it should be an easy task to extend support of binding to any object.

Design goals

The proposed solution is supposed to:

  1. Simplify the binding of one business entity to a WinForm, in which this entity is created/edited.
  2. Provide a consistent method of handling the IsDirty state of the object (e.g., show an asterisk in the caption when the object is dirty and remove it when it is not dirty).
  3. Show up a consistent method of indicating the IsValid state of the object (e.g., using the ErrorProvider component).

How to read the code

To help you understand the code (especially the scope of methods and variables), here are the relevant coding conventions I follow:

  • private fields start with a "_" prefix followed by lowercase.
  • private methods start with lowercase.
  • public or protected methods and properties (there are no non-private fields) start with uppercase.

For clarity's sake, I omitted helper functions in this article where I thought that the implementation is rather trivial and the method name tells the story anyway (which a method name should always do ;-)). You can look them up in the source code though.

The ObjectBindingManager

The ObjectBindingManager is the key component of this package. It abstracts the bound object and provides design-time capabilities for assigning properties to controls. The idea behind is rather trivial. The component gets dropped on the WinForm. It is then assigned a System.Type to allow the assignment via a PropertyExplorer instead of typing a property name and thus avoid errors. At runtime, the ObjectBindingManager initializes all bound controls with the corresponding properties. It further provides a CommitChanges() and a RollbackChanges method to handle the updated values. It also listens to the Validated event of all bound controls, compares the new value with the old one, and raises an IsDirtyChanged event to notify a listener about the state of the object. By this, the form can listen to this event and behave accordingly (e.g., the mentioned asterisk, but it could also activate/deactivate the Save button).

The BoundType is assigned by means of a custom UITypeEditor called TypeBrowser. I incidentally found this component when I just wanted to write my own, so props here to Stephen Toub. I modified it slightly to fit my needs. It now takes a System.Type as parameter and shows only types of this Type or descendants. By this, you can modify my code and provide the base type of your business objects. If you don't have a base type, simply call it with a NULL argument. It then shows all types. The ObjectBindingManager provides extended properties, a very convenient technique to enhance other controls. All you have to do is to tag the component with a ProvideProperty attribute and follow a simple naming convention:

private bool ShouldSerializeBinding(Control extendee)
    Binding binding = _bindings[extendee];
    if (binding == null) return false;

    bool serialize = (binding.ControlProperty != null 
                     && binding.ControlProperty != "" && 
                     (binding.ObjectProperty != null && 
                     binding.ObjectProperty != "") || binding.MonitoredOnly);

    return serialize;

[Description("Define databinding on Object")]
public Binding GetBinding(Control extendee)
    if (_bindings.ContainsKey(extendee))
        return _bindings[extendee];
        Binding binding = new Binding(ObjectType, extendee);
        binding.ControlProperty = (extendee is CheckBox) ? "Checked" : "Text";
        _bindings[extendee] = binding;
        return binding;

public void SetBinding(Control extendee, object value)
    Binding binding = value as Binding;
    binding.Control = extendee;

    if (!binding.MonitoredOnly && 
                 (binding.ControlProperty == null || 
                 binding.ObjectProperty == null))
        if (_bindings.ContainsKey(extendee))
            _bindings[extendee] = binding;
            _bindings.Add(extendee, binding);

Here, the property binding will be provided to all controls. You can select a control in the designer, switch to the property window, and you find an entry "Binding on bindingManager1" under the DataBinding category. The ShouldSerializeBinding method here tells the designer if code for this property should be generated.

Supporting complex types

Depending on the controls you use and your business framework, you might still need to assign some values manually. In the sample application, you will find a

property which is displayed by two checkboxes. In case you can't use the auto-binding feature but still want to track the IsDirty-state, you can "monitor" a control. Select the control, find the "Binding on bindingManager1" entry, expand it, and set MonitoredOnly to
. Now, the initial state of the control is compared against the actual state when determining the IsDirty state. After manual initialization, call bindingManager.ResetMonitoredValues();.

public MainForm()

    Person person = Service.GetPerson();
    bindingManager.BoundObject = person;

    chkUser.Checked = (person.Flags & GroupFlags.User) 
                              == GroupFlags.User ? true : false;
    chkAdmin.Checked = (person.Flags & GroupFlags.Admin) 
                              == GroupFlags.Admin ? true : false;


How to use the code

Compile the code, add the ObjectBindingManager to your toolbox, and drop an instance on the form. Assign a BoundType, select a control to bind, find 'Binding on bindingManager' in the Data category of the property window, and assign the ObjectProperty.

Now, where do I go from here?

Because business object layers and needs differ greatly from each other, this is most likely not a Plug&Play component. I assume you will modify the argument to TypeBrowser, so only your business objects are displayed. Furthermore, you might enjoy the simplicity of handling the IsDirty state and think of enhancing the BindingManager to provide also the IsValid state - well, at least I did ... If you have a strong and descriptive validation on your business objects (like, for example, DataObjects.NET or CSLA.NET does), you might reduce greatly the need of user interface validation code. The component has an ErrorProvider property - just drag one on the form, switch to the properties of the ObjectBindingManager, and assign it - which I use to display the validation errors.

You can switch the ObjectBindingManager to a different mode by disabling CacheValues. Instead of caching the values in an object array, the properties are directly bound using the DataBindings collection of the controls. I decided to enable it by default simply because I needed to. It might suit your needs better by disabling it.

Finally, I can only recommend using a kind of EditorBaseForm where you put the ObjectBindingManager, and handle its events and provide a template Save method ... it saved me a great deal of time and headaches!

Revision Notes

  • 27/09/2004: Fixed an issue concerning readonly properties and non-string properties.


This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


About the Author

Comments and Discussions

Questionformat end parse event Pin
manzyinter26-Dec-06 21:35
Membermanzyinter26-Dec-06 21:35 
QuestionIsValid Pin
Spink Bickle31-Jul-06 4:42
MemberSpink Bickle31-Jul-06 4:42 
AnswerRe: IsValid [modified] Pin
Eduard Liebenberger31-Jul-06 19:15
MemberEduard Liebenberger31-Jul-06 19:15 
GeneralOut of memory Pin
Steven Campbell6-Jan-05 5:53
MemberSteven Campbell6-Jan-05 5:53 
If you have recursive relationships within your object tree, then the property explorere will fail with an "out of memory" error. This is easily fixed:
Private Sub parseType(ByVal type As Type, ByVal node As TreeNode)
    parseType(type, node, 0)
End Sub

Private Sub parseType(ByVal type As Type, ByVal node As TreeNode, ByVal pDepth As Integer)
    If pDepth > 5 Then Exit Sub

    Dim infoArray2 As PropertyInfo() = type.GetProperties
    Dim num1 As Integer = 0
    Do While (num1 < infoArray2.Length)
        Dim info1 As PropertyInfo = infoArray2(num1)
        Dim node1 As New TreeNode(info1.Name)
        Dim chArray1 As Char() = New Char() {CChar(".")}
        node1.Tag = String.Format("{0}.{1}", node.Tag, info1.Name).Trim(chArray1)
        If (Not info1.PropertyType Is type) Then
            Me.parseType(info1.PropertyType, node1, pDepth + 1)
        End If
        num1 += 1
End Sub

(Replace the existing parseType sub in the PropertyExplorer user control with the above code). I used a max nesting depth of 5, but you could increase that according to your particular needs.

my blog
GeneralBinding to Two or More Combo Boxes Pin
leonleslie27-Nov-04 17:09
Memberleonleslie27-Nov-04 17:09 
GeneralRe: Binding to Two or More Combo Boxes Pin
Eduard Liebenberger14-Dec-04 23:45
MemberEduard Liebenberger14-Dec-04 23:45 
GeneralReflectionTypeException Error on Object Binder Manager -&gt; ObjectType Pin
Anonymous18-Nov-04 14:43
MemberAnonymous18-Nov-04 14:43 
GeneralRe: ReflectionTypeException Error on Object Binder Manager -&gt; ObjectType Pin
Eduard Liebenberger19-Nov-04 0:51
MemberEduard Liebenberger19-Nov-04 0:51 
GeneralRe: ReflectionTypeException Error on Object Binder Manager -&gt; ObjectType Pin
Anonymous24-Nov-04 14:57
MemberAnonymous24-Nov-04 14:57 
GeneralRe: ReflectionTypeException Error on Object Binder Manager -&gt; ObjectType Pin
Eduard Liebenberger24-Nov-04 20:05
MemberEduard Liebenberger24-Nov-04 20:05 
GeneralMaybe a little bug Pin
EPO26-Sep-04 22:40
MemberEPO26-Sep-04 22:40 
GeneralRe: Maybe a little bug Pin
Eduard Liebenberger27-Sep-04 0:13
MemberEduard Liebenberger27-Sep-04 0:13 
GeneralRe: Maybe a little bug Pin
EPO28-Sep-04 4:30
MemberEPO28-Sep-04 4:30 
GeneralRe: Maybe a little bug Pin
Eduard Liebenberger28-Sep-04 4:35
MemberEduard Liebenberger28-Sep-04 4:35 
GeneralWonderful job ! Pin
PatrickVD13-Sep-04 11:24
MemberPatrickVD13-Sep-04 11:24 
GeneralGood Job! Pin
alper13-Sep-04 10:58
Memberalper13-Sep-04 10:58 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.