Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Coding in Tiers - Part I

0.00/5 (No votes)
14 Sep 2003 1  
Automate UI Form population

Introduction

This article is the first in the two part series, and demonstrates a technique to auto-populate Windows Forms using an Automator class. The articles presented in this series will be as follows.

  • Coding in Tiers - Part I (Automate UI Form population)
  • Coding in Tiers - Part II (Reuse code in Windows and Web forms)

The downloadable source and executable code provided includes the example application. I am using Visual Studio .NET 2003 with Framework SDK 1.1 to create this example.

Patterns, Levels, Layers and Tiers

Enterprise solutions should not only be highly available and scalable, but also flexible and reusable to adapt to rapidly changing business requirements. Designing reusable software is not an easy task. It is relatively easy to write not so reusable software than designing and writing software which consists of reusable components. While the former may be a good idea for prototyping, it does not serve us well in the long run. Software designed with reuse in mind is very powerful since it saves a lot of precious time (and thus cost) in developing and debugging code when it can be readily "plugged-in".

Design Patterns occupy center stage when it comes to software component reusability. A Pattern describes a recurring problem that occurs in a given context, and based on a set of guiding forces recommends a solution. Patterns make software flexible, elegant and thus reusable. Patterns exist at many different levels of abstraction. Each of these levels can be thought of as being a cluster of design patterns.

Each level could consist of multiple layers e.g. Presentation, Services, Business, Data Access etc. A layer consists of elements at roughly the same level of abstraction. The dependencies in each layer have to be identified to formulate a layering strategy. Building applications without a good strategy for dependency management leads to brittle and fragile components, which are difficult and expensive to maintain, extend, and substitute. If the levels can be thought of as being laid out vertically, the layers would be laid out horizontally. This results in a matrix consisting of cells that define and use proven software design patterns. Each layer from the infrastructure viewpoint has a corresponding tier which in a multi-tier architectural model could be classified as follows...

  • Client Tier - All devices or system clients accessing the application e.g. Windows Application, Web Browser etc.
  • Presentation Tier - Components that provide an user interface (UI) for performing business logic.
  • Services Tier - Abstraction of services provided by the application which communicate with the business tier.
  • Business Tier - Components that perform business logic and transactions.
  • Integration Tier - Components that communicate with an external resource such as a data store.
  • Resource Tier - Contains business data which is accessed by the integration tier.

Best Practices

Separating disparate code is the key to successful implementation of a well structured application. Keep the code closer to the tier which it needs to interact with. Components that create a user interface and perform user interaction should be factored into the Presentation tier while components that perform database inserts, updates and queries should be a part of the Integration tier. Tight coupling between tiers result in code which is difficult to maintain and reuse. For e.g. consider a windows application which presents a user interface for registering a user in the system. If the code in the Business tier were to use a component from the Presentation tier, a problem could arise when the user interface was changed from a windows form to a web form.

using System.Windows.Forms; // Windows Forms


// Windows Application

namespace Win
{
    // Windows Form

    public class AutoForm : System.Windows.Forms.Form
    {
        // Members


        // Store the state of the form

        private void SaveButton_Click(object sender, System.EventArgs e)
        {
            // Register a new user

            AccountManager.Register(this);
        }
    }

    // A class to manage user accounts

    public sealed class AccountManager
    {
      // Register a new user

      public static void Register(AutoForm form) { // Process Registration }

    }
}

The code for AccountManager above will not compile without a reference to System.Windows.Forms.dll. This problem can be solved by extracting the reusable code out into a class library and creating a new class State that holds all the information entered into the user interface via the use of properties as shown in the code snippet below.

// Windows Application

using System.Windows.Forms; // Windows Forms


using Lib; // For the AccountManager and Registration class


// Presentation Tier (Windows)

namespace Win
{
    // Windows Form

    public class AutoForm : System.Windows.Forms.Form
    {
        // The registration information

        private State state = new State();

        // Members


        // Store the state of the form

        private void SaveButton_Click(object sender, System.EventArgs e)
        {
            // Populate state with values from the UI controls

            AccountManager.Register(this.state);
        }
    }
}

// ASP.NET Web Application

using System.Web.UI; // Web Forms


using Lib; // For the AccountManager and Registration class


// Presentation Tier (Web)

namespace Web
{
    // Web Form

    public class AutoForm : System.Web.UI.Page
    {
        // The registration information

        private State state = new State();

        // Members


        // Store the state of the form

        private void SaveButton_Click(object sender, System.EventArgs e)
        {
            // Populate state with values from the UI

            AccountManager.Register(this.state);
        }
    }
}

// Class Library

using System;

// Business/Domain Tier

namespace Lib
{
    // A class to hold registration information

    public class State
    {
        // Properties corresponding to the controls on the UI

    }

    // A class to manage user accounts

    public sealed class AccountManager
    {
        // Register a new user

        public static void Register(State state) { // Process Registration }

    }
}

The dependency on the AccountManager can be eliminated by providing an additional service tier as we will see in the follow up article. Every property defined in the State class depends on the type of control used on the UI i.e. A TextBox would be represented as a string where as a CheckBox should be represented as a bool. However, there is one more problem. The instance of State needs to be populated with values from the UI in the SaveButton_Click() method of the AutoForm class (Windows Application or ASP.NET Web Application). We will limit our discussion to the Windows Application for this article.

Introducing Automator

The SaveButton_Click() method would look something similar to the code snippet shown below.

// Store the state of the form

private void SaveButton_Click(object sender, System.EventArgs e)
{
    // Store TextBox value

    this.state.Username = usernameTextBox.Text;

    // Store CheckBox value

    this.state.Subscribe = subscribeCheckBox.Checked;

    AccountManager.Register(state);
}

Assuming that there is a LoadButton_Click() method for loading the last stored state of the form; The LoadButton_Click() method would look something similar to the code snippet shown below.

// Restore the state of the form

private void LoadButton_Click(object sender, System.EventArgs e)
{
    // Restore TextBox value

    usernameTextBox.Text = this.state.Username;

    // Restore CheckBox value

    subscribeCheckBox.Checked = this.state.Subscribe;
}

This could be cumbersome and error-prone especially when using complex forms which house a lot of controls. What we need is a utility class "Automator" which automatically stores and restores the state of the form. A skeletal code snippet describing its usage is shown below.

namespace Win
{
    // Windows Form

    public class AutoForm : System.Windows.Forms.Form
    {
        // Members


        // The form automator

        private Automator automator;

        public AutoForm
        {
            automator = new Automator(this, this.state);
        }

        // Store the state of the form

        private void SaveButton_Click(object sender, System.EventArgs e)
        {
            // Store the state using the automator

            automator.Store();
            // Register the user

            AccountManager.Register(this.state);
        }

        // Restore the state of the form

        private void LoadButton_Click(object sender, System.EventArgs e)
        {
            // Restore the state using the automator

            automator.Restore();
        }
    }
}

Great! But how will this happen? This is done by using the magic of reflection. The property names on the State are matched with the names (case-sensitive) of controls on the form whose state is to be saved. The Store() method on the Automator class will iterate over all the controls contained within the form matching the name of every control to the property on the State object. If a match is found, the value on the control is set on the matching property after applying type conversion (if necessary). The Restore() method on the Automator class will work in exactly the reverse order i.e. populating the control with the value from the matching property.

Control Name Control Type Property Name Property Type
Username System.Windows.Forms.TextBox Username string
Subscribe System.Windows.Forms.CheckBox Subscribe bool
Interests System.Windows.Forms.ListBox Interests string[]

Closing Note

This article demonstrates a technique to auto-populate a Windows Form. My next article will abstract some of the code out to make it more flexible and reusable. We will introduce a new Service tier in addition to the Presentation and Business tiers described in this article. The follow up article will demonstrate code reusability in three different types of applications viz. Windows, Web and Web Services.

References/Links

License

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