Monday, March 16, 2009

On the Separation of Form and Data.

Source code for this article can be downloaded here: http://www.mediafire.com/?mdqmymgrwky

As a professional developer I spend a lot of time maintaining other peoples code. One application that I spend a lot of time with is a C# application written by COBOL programmers. This makes for some very interesting contrasts between what Object Oriented programming is, and how it is simulated by developers without practical experience in OOP.
The forms in this application are a strange mixture of form elements, control logic, static data and business logic. Pieces of the process consists of forms being populated, stored in memory, then passed to other forms for other parts of the process. Maintenance is very difficult, it is hard to determine exactly where values are collected, which form to collect values from and whether data resides in a form or in a large object in memory. There has to be a better way to do this.

Lets lay out the problem formally. We have a process, lets call it OrderALetter. In order to complete one iteration of this process, we must;
1 - Establish a letter recipient.
2- Offer the user a chance to modify any of the information.
4 - Offer the user a form to type the letter into.
5 - Store the letter order.

Based on these abbreviated requirements, we can see that we need 2 forms to collect information, lets call them LetterRecipient and LetterText.
The process we will implement will follow the same order as the problem statement.

Since we are going to be using reflection and iteration to bind our object to our form, this frees us to focus our design cycles on the types we will use. This methodology also forces us to consider a part of OO design that we normally reserve for developing our interfaces; public attributes.

The utility class we will use will reflect on the object and seek properties that match the id's of controls on the form, so we really need to make good decisions regarding the public interface of our object. We need to be careful not to expose any properties that we are not prepared to validate, and we must insure that changes made to the type's attributes cascade properly to keep the type's internals synchronized. If you are smart about your design, this will also help you keep the private members and public methods in your types to the absolute minimum necessary to do the job. This will keep you from racing through objectDesign Mart, filling your object up with all kinds of useless functionality. You will also see very clearly where operations and data stop and where presentation and event handling start. With the types properties you can handle input validation and throw argument errors where validation rules indicate errant data. (This will be handled in a later post).

So lets get started. We need a container class for our customer information, lets call it 'Customers'. The class consist only of the necessary members, associated properties and a validation method or two. Here are the relevant members.
Note: The full source is included, properties, methods etc.

namespace Customers {
public class Customer {

private string _userFirstName;
private string _userLastName;
private string _userMiddleName;
private string _userAddress1;
private string _userAddress2;
private string _userCity;
private string _userState;
private string _userZipCode;
private string _paragraph1;
private string _paragraph2;
}}



Now, lets test this concept. I have created a form consisting of labels and text boxes. There is also a button to bind the control back to the object to update its values, and a button to go to the next step in the process. The text boxes on the form will hold the data from the object and they MUST be named exactly as the associated property in the Customers class is named. Notice what is missing, lots of assignment statements!

Here is the code behind for the form.

namespace Customers{
public partial class LetterOrder : Form{

private Customer _ltrRecipient;
public Customer LetterRecipient
{
get { return this._ltrRecipient; }
set { this._ltrRecipient = value; }
}

public LetterOrder(Customer LetterRecip)
{
InitializeComponent();
this.LetterRecipient = LetterRecip;
FormBinding.BindObjectToControls(LetterRecipient, this);
}

private void button1_Click(object sender, EventArgs e)
{
FormBinding.BindControlsToObject(LetterRecipient, this);
this.Close();
}}}

Notice that we have defined a public property for the Customers object reference. This allows the forms in the application to share the static Customers object.

Here's how the form looks with the data included.

From Mad Bytes


Now we have defined a container class, a form with control id's that mirror property names in the container class, and we have defined operations that bind the object to the control and that bind the control to the object to update values.

Now lets look at the next step. We need to open the LetterText form, populate its values with what the user has already entered and persisted in the Customers object, and get the paragraph text from the user.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Utilities;

namespace Customers{
public partial class LetterOrder : Form{

private Customer _ltrRecipient;
public Customer LetterRecipient
{
get { return this._ltrRecipient; }
set { this._ltrRecipient = value; }
}

public LetterOrder(Customer LetterRecip)
{
InitializeComponent();
this.LetterRecipient = LetterRecip;
FormBinding.BindObjectToControls(LetterRecipient, this);
}

private void button1_Click(object sender, EventArgs e)
{
FormBinding.BindControlsToObject(LetterRecipient, this);
this.Close();
}}}

From Mad Bytes



From this point all we have to do is send the letter to the printing or storage mechanism, preferably as an ILetter.

I have shown you how to completely abstract form data from a form in C#. There are many benefits to this methodology. Implementation of design patterns paradigms allow for easy modifications to the data or the form in the future without breaking anything. Separation of development tasks is much easier as you can establish an interface (the fields available on the form for the object to bind to), and send your winform developers off to build eye popping forms while your data developers build your objects and worry about data validation, serialization, storage etc. Check back for further elocution on this interesting topic.



The helper class used to bind the form to the object and vice versa was taken from an excellent MSDN posting by John Dyer: aspformbinding

I took his version that was written for asp.net and modified it to work with C# winforms.