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

The User Interface Process Application Block : Part 1

0.00/5 (No votes)
28 Apr 2004 1  
Part 1 in a three part step-by-step series on UIPAB

Introduction

This is the first article in a series of three which will explain step-by-step how to implement UIPAB Version 2. The benefits of UIPAB will be demonstrated by firstly examining the non UIPAB approach. Also, UIPAB will be demonstrated within the broader context of Patterns and Practices and .NET application Blocks.

Background

An understanding of the ESP Layered pattern is advantageous. The code relies on various App Blocks, so an understanding of DAAB (Data Access App Block v2), CMAB (configuration Management App Block) and EMAB (Exception Management App Block) will be helpful.

Using the code

The code download consists of a SQL script to set up the database and four C# projects: UIPABBE (contains the Business Entity classes); UIPABData (contains the Data Access Logic Classes which utilize the DAAB); UIPABWin1 (which which the basic forms and stub code - this application does not work but is used as the basis for all subsequent working applications in this series); UIPABWin2 (a windows application that does not use UIPAB).

Overview

I recently delivered a talk for http://www.sadeveloper.net/ at Microsoft South Africa�s offices in Johannesburg. The audience comprised of Johannesburg�s more informed and talented .NET developers. I asked members of the audience if anyone had used the User Interface Process Application Block (UIPAB) and no one raised his or her (there were only three hers in the audience) hand. My talk was an hour and in that time I had to give a rushed explanation of UIPAB. My aim was to show the benefits of using the Patterns and Practices Application Blocks and my basic message was that the App Blocks takes .NET to an entirely new level of versatility, sophistication and, in my eyes, elegance. In the little time I had at my disposal, and judging from the responses I have subsequently received, I think I got my message across. I managed to convince my audience that App Blocks, and indeed the entire PP initiative, is crucial to serious, high-powered .NET development. Unfortunately, because of time constraints, I was unable to explain the delicate mechanics of UIPAB implementation in sufficient detail so as to allow my audience to go away and begin experimenting with the UIPAB. Some in the audience admitted that they had looked at the UIPAB and had tried to master it with the supplied literature, but found the task too daunting or the help file too complicated or too technical for quick practical implementation. Aside from the .PDF manual and the Help file supplied with the download, there is no other UIPAB literature. The documentation is thorough, but clearly too thorough for the novice or too dense for the time-constrained, dead-line-pressurized developer. The UIPAB, like the other App Blocks, comes with a wealth of examples, unfortunately, none of these examples are documented and you are forced to deconstruct the code on your own � for a new paradigm, a daunting task in and of itself. What is needed, therefore, is a step by step approach which will slowly introduce the potential UIPAB user to the benefits of using the App Block. These benefits can be shown using the minimum features of UIPAB, especially when we can show how an old style front end can be refactored using the UIPAB. While it is important to understand the UIPAB under the hood, such an in-depth understanding is not essential to start working with it. The documentation is by and large devoted to under-the-hood detail, and this is probably the reason for the general reticence I have encountered in using it. What I propose to do in this series of articles on the UIPAB is a step-by-step guidance to using the UIPAB. It is important to understand that the UIPAB is part of the PP initiative. I think it is wrong to study the UIPAB in isolation from the overall PP picture. This first article will therefore attempt to place UIPAB within the context of PP. I will briefly touch on the ESP .NET Layered Pattern and demonstrate what I think is the right approach in developing the Business and Data layers. I think that the true power of the App Blocks is unleashed when they work together in the same application, and so I will include the Data Access App Block (DAAB), the Configuration Management App Block (CMAB), and the Exception Management App Block (EMAB) in my Business and Data layers. I will then present the simple WindowsForm demo-example I will be using. I will show the various forms, controls and work-flow of the demo which will be common to all versions of the demo I will canvass in this series. Then I will present a non-UIPAB version of the demo where I will point out the drawbacks of using this old way of doing the UI.

In next article I will introduce the MVC pattern and show how this pattern attempts to solve the problems encountered in the old way of doing things. Also I want to emphasize that an appreciation of MVC is central to understanding the UIPAB. I will then examine the UIPAB using a step-by-step didactic approach instead of the detailed, technical approach found in the UIPAB documentation. I want to give an overview of the UIPAB objectives, its basic building blocks and a brief checklist on how to start developing simple UIPAB UI�s. I will then refactor the original WindowsForm application presented in the previous article this time using UIPAB. Lastly, I will show how the same refactored code can be reused in an ASP.NET application. In this first example I will be stripping UIPAB of all its detail and focusing on its core, basic features.

In the third article, I will build on the example we used to show UIPAB in more detail. Whereas the first example will deal with a single UI Process or Task (basically a Use Case), the second example will deal with two UI Processes and show how they can be linked. Also, the first example will not deal with snap-shot State persistence, the second example will.

The last article in the series will again refactor my example so as to demonstrate the various navigational abilities of UIPAB. The other examples use the Navigation Graph; the final example will show the use of all the UIPAB navigational capabilities. Also, in this final article, I will briefly discuss customizing the UIPAB.

I suggest at this stage that you download the UIPAB from here if you have not done so already and install it on your development machine. You will need an installation of .NET Framework 1.1 and Visual Studio .NET 2003.

THE ESP .NET LAYERED PATTERN

The first sentence of Chapter 1 of Fowler�s Patterns of Enterprise Architecture is a succinct description of what layering is:

�Layering is one of the most common techniques that software designers use to break apart a complicated software system�

It is important to stress what Layering it is not. A �layer� does not refer to a physical entity or separation. The term used for referring to such a physical entity is �tier�. A �multi-tiered� system is one which comprises a number of physical tiers. A �layer� therefore is a logical or conceptual software entity. A layer is deployed to one or more tiers. I have dealt with the .NET Enterprise Solutions Pattern adoption of the layered pattern in my article The Microsoft Patterns & Practices Initiative: Part 2 � The Layered Pattern and I refer interested readers to that article for more detail. (The article can be downloaded from http://www.sadeveloper.net/ or http://www.saarchitect.net/ or from http://www.pdev.co.za/ under Resources then Ayal�s Articles).

To summarize: There are three main layers each with its own sub-layers as follows:

  • Presentation Layer
    • User Interface Components
    • User Interface Process Components
  • Business Layer
    • Business Components
    • Business Workflows
    • Business Entities
    • Service Interfaces
  • Data Layer
    • Data Access Logic Components
    • Service Components

A Visual Studio Application Architecture Template Policy has been released that enforces the ESP layered pattern within the VS.NET 2004 IDE. The Template Policy can be downloaded from: http://www.pdev.co.za/ under Resource then Architecture (registration is required). I will be using this Template in all the examples. (I am unable to find the original URL at http://www.gotdotnet.com/ where this policy was originally released).

THE SAMPLE APPLICATION

For this first article I have deliberately chosen a simple example. All the sample application does is display a list of persons. The user can then add new persons to the list or update the details of an existing person. The person�s details are also simple: each person has a first name and last name; furthermore, each person can have one or more addresses or contact numbers associated with him. The sample application contemplates a future refactoring into a Service Oriented Application which will use the Offline Application Block, so I have tried to cater for this future development into the architecture from the start. When the application starts up, a list of all existing persons are retrieved from the data-store. This list is presented to the user where it is persisted. The user can now add new persons to the localized list; if the user updates an existing person already in the list, the application checks whether the selected person�s details have been retrieved from the data store; if the details have not been retrieved, these details are retrieved and presented to the user; if the details have already been retrieved, the person is presented to the user for editing. Only when the user has finished working with the list of persons, is the entire list sent back to the data-store. Newly inserted persons are registered with the data store; persons whose details have been updated are updated on the data-store.

The architecture therefore is one of an initial batch-down load; working locally on the UI and then a batch up-load to the data-store again. This is a disconnected paradigm which aims to keep data-access to the minimum. The example does not deal with concurrency or transactional issues.

The demo application will make use of a number of Application Blocks( Data Access, Configuration Management and Exception Management). I will not be discussing the mechanics of these other App Blocks but I thought it appropriate to include them in this demo. I repeat: It is my belief that the true power of the App Blocks is enhanced when you begin to use them together and it is best to start using this technique from the outset. (Hopefully � time and energy permitting � I will have occasion to examine these App Blocks in other articles.)

THE DATA STORAGE

The Data Schema is fairly straight forward. There are three tables: Person, Address and Contact � each with a Primary Key as an Identity Field.

There are three types of stored procedures defined for each table:

  • Get � Retrieves a single row from the table based on the supplied ID parameter.
  • Ins � Insert a single row into the table and returns the ID value for the newly registered row.
  • Up � Updates a single row in the table based on the supplied ID parameter.

The Person table has an extra stored procedure which retrieves a list of all rows in the table.

UIPAB STORED PROCEDURES
TABLE SP PARAMETERS DESCRIPTION
Person getPersons Returns all persons in the table.
getPerson ID Returns a single person.
insPerson Firstname, Surname Inserts new person and returns the ID.
upPerson ID, Firstname, Surname Updates an existing person
Address getAddressesByPerson ID Returns Addresses for specific Person
insAddress PersonID, Street, Province, Country Inserts an address for a specified person and returns the new address ID
upAddress ID, Street,Province, Country Updates an existing address.
Contact getPersonsByContact ID Returns Contacts for a specific Person
insContact PersonID, Street, Province, Country Inserts a specific Contact for a specific Person and returns new Contact ID.
upContact ID, Street, Province, Country Updates a specific contact.

The SQL Script to set up the data-base schema and the stored procedures is supplied in the download. The application assumes a SQL authentication mode for database access. The specific of the data-base access string will be configured using the Configuration Management App Block (CMAB) � so you don�t have to worry about it at this stage.

THE BUSINESS LAYER

Because the sample is a pretty simple application, there are no real business rules or processes involved. I am therefore only going to be developing the Business Entity sub-tier. There is a lot of discussion about what is the best way to develop .NET business entities within the context of the ESP Layered pattern. For a thorough treatment of the subject I refer you to the following:

My approach is derived from two overriding considerations: Firstly, like Martin Fowler, I am an object bigot and therefore tend to insist that that my business entities adhere to strict OO rules. Secondly, I am also a Layered Fundamentalist which means I try to stick to the ESP Layered Pattern as far as possible. Being an object bigot, there can be no place for ADO.NET classes and structures in my business entities i.e. no room for datasets, data tables, data rows or data relations. Being a Layered Fundamentalist, my business entities should not be doing what my other Business Layer sub-layers (Business Components and Business Workflows) should be doing. Practically speaking this means that my business entities are classes with attributes (properties) and nothing much more. Where operations (methods) are required, they are simple, straight-forward and serve merely to qualify a property. Business process, rules and workflows belong in the other sub-layers. This approach is conducive to reuse of my business entities; if the business rules were embedded into them, their reuse would be precluded. By being a Layered Fundamentalist I can reuse my business entities even when the business rules change. In the real world, this is what happens: the business entities are more-or-less static but the business rules are constantly changing.

All bigots are disadvantaged and object bigots are no exception. Because my business entities are pure they have no trace of ADO.NET components i.e. no DataSets, DataTables, DataRows and DataRelations, which also means no seamless ability to serialize into XML. Furthermore, whenever I access a relational data store to populate my business entity or update the data store with my business entities data, I will have to go through a long and cumbersome Object-Relational-Mapping (ORM) exercise. To add to my problems, at the end of the day, I want to display my business entities to the end-user, without ADO.NET the rich feature set of data-binding is not as readily available to me. So bigotry comes at a cost. The advantage of strict OO, however, far outweighs the disadvantages. The purist approach is aesthetically superior. When you mix models (relational with OO) the code tends to get messy and is inelegant. Also, the more complex the demands made on your business entities, the more you will come to realize that the short-term benefits of a hybrid can turn into a trap. If there is anything I have learned as a developer, it is that elegance and aesthetics is the best guarantee of longevity and performance.

All my business entities inherit from a base business entity class:

The BaseBE encapsulates properties that are common to all business entities. There are four properties and one protected method. The method, validate(), must be implemented by the specific business entity. In validate(), UI validation rules are coded.

The four properties are as follows:

PROPERTY DATA TYPE DESCRIPTION
ID string A property to uniquely identify the entity. If the entity is retrieved from the data storage, the ID should be assigned from the data storage. For all other entities, this value is a GUID.
Valid bool As the specific entity�s properties are populated, inserted or updated, the valid() method is triggered. When all the properties are validated, the Valid property is set to true otherwise it remains false.
isNew bool Entities that have not been retrieved from a persistence storage and are created in the new session are marked as new. When the batch of entities are sent to the persistence layer, all new objects are registered with the persistent layer.
isDirty bool All entities that are not new, are checked for being dirty when sent to the persistent object store. If an entity is dirty, this means that its properties have been changed from when they were first retrieved and therefore the entity must be updated on the data store.
The BaseBE presented here is a simplified version. The enterprise version is much more elaborate and allows for such things as granular dirty checks and deterministic serialization.
namespace UIPABBE
        {
          [Serializable]
        public abstract class BaseBE
  {
    private string id;    
    protected bool valid, dirty, isnew;
    public BaseBE():this(0){}
    public BaseBE(int id)
    {
      this.id = id.ToString();
      this.dirty = false;
    }

    public string ID
    {
      get{return id;}
      set{id = value;}
    }    
    public bool Valid{get{return valid;}}
    public bool isDirty
    {
      get{return dirty;}
      set{dirty = value;}
    }
    public bool isNew
    {
      get{return isnew;}
      set{isnew = value;}
    }
    protected abstract void validate();
  }
}

As you can notice the BaseBE is marked with the [Serializable] attribute. UIPAB maintains state; this state can be persisted and so all state objects must be serializable. As a rule of thumb, you should always serialize your business entity classes. (I have used declarative serialization in this example. If you are going to be using the advanced features of UIPAB, I recommend using deterministic serialization. This entails a fluent proficiency of .NET object serialization.)

The sample Business Entity layer consists of three simple classes, each representing a table in the data base: Person, Contact and Address. The classes are available in the source code of the UIPABBE project.

We will examine the Person business entity to get an understanding of the basic mechanics of what a business entity object does.

using System;
using System.Collections;

namespace UIPABBE
{
  [Serializable]
  public class Person: BaseBE
  {
    #region PRIVATE FIELDS
    private string first, last;
    private ArrayList contact = new ArrayList();
    private ArrayList ad = new ArrayList();
    #endregion

    #region CONSTRUCTORS
    public Person():this(0, "", ""){}  
    public Person(int id, string first, string last):base(id)
    {
      this.first = first;
      this.last = last;
      validate();
    }
    #endregion

    #region PROPERTIES
    public event EventHandler FirstChanged;
    public event EventHandler LastChanged;
    public string Firstname
    {
      get{return first;}
      set
      {
        if(this.first != value)
          this.dirty = true;
        first = value;
        if(FirstChanged != null)FirstChanged(
          this, EventArgs.Empty);
        validate();
      }
    }  
    public string Lastname
    {
      get{return last;}
      set
      {
        if(this.last != value)
          this.dirty = true;
        last = value;
        if(LastChanged != null)LastChanged(this,
          EventArgs.Empty);
        validate();
      }
    }
    public ArrayList Contacts
    {
      get{return contact;}
      set{contact = value;}
    }
    public ArrayList Addresses
    {
      get{return ad;}
      set{ad = value;}
    }
    #endregion

    #region OVERRIDES
    public override string ToString()
    {
      return this.Firstname + ", " + this.Lastname;
    }
    protected override void validate()
    {
      if(this.Firstname.Length > 0 &&
        this.Lastname.Length > 0)
        this.valid = true;
      else
        this.valid = false;
    }

    #endregion
  }
}

Pay attention to the following features:

  • The ToString() method is overridden. This is done to facilitate seamless data binding in the UI. If no specific property of the bound object is specified as the display, the control displays the ToString() output of the object.
  • Each entity property has an associated event. This event is triggered whenever the property value is set. Again, this technique is required for data-binding. When the value on an object bound to a control is changed on that object, the changed value will immediately be reflected in the control if the appropriate event is triggered.
  • Before setting each property value, the new value is compared to the old value. If these values are different, the object is marked as dirty. For this reason, a constructor is required which receives the values of all the objects parameters on initialization. If I had set the property values directly then every object would automatically be marked dirty and obviously this is undesirable. (I admit that in objects with many properties, sending initializing values in the constructor is cumbersome and inelegant. In my enterprise version of business entities, I have an elegant work-around which allows me to set property values directly on the property on initialization. For the purposes of the UIPAB the constructor initialization mode will suffice.).
  • After each property value has been set, the validate() method is called.
  • For lists of other objects aggregated in the current object I am using the ArrayList. This is a crude approach and should not be utilized in an enterprise business entity. (The full version of an enterprise business entity has a BaseBESet class which represents a list of business entities. Each business entity will therefore also inherit from the BaseBESet which is strongly typed. For the purposes of the UIPAB simple demo, the crude approach will suffice to avoid complications.)

THE DATA LAYER

Access to the data storage is strictly through the Data layer and the Data Access Logic Components (DALC) sub-layer to be more precise. The DALC layer utilizes the Data Access Application Block (DAAB v2) and so the DAAB should be referenced. Also, the DALC layer will be receiving and retrieving business entities, therefore the business entity project must also be referenced. (According to the Team Developer P&P guideline, best practice is to reference the project and not the specific project .DLL). Strictly speaking the DAAB is a DALC best-practices implementation. So the project I am going to create in the DALC layer is a project-specific fa�ade to the DAAB. (Again, the enterprise version of the DALC will be different from the version I am going to present here. By and large, the main difference is to centralize common behaviour in a base class and then make sure that all the DALC classes inherit for this base class.).

In the current version, I created a Data Access class for each business entity. The idea is to isolate data-access on a per-table, per-entity basis. Because the data store is relational, this sort of strict isolation is impossible; it is here where the relational world unavoidably encroaches on the OO world. (In a later series I intend to show how products like Matisse � which is a post relational data base with a .NET binding � can avoid this conundrum.) Where the data access is from a �join-table� i.e. a table which contains elements from two other tables, I place the data access code in the data access entity class which is predominant in the operation. For example: when I have to retrieve a person�s contact details, the predominant data that is retrieved is the contact information and so the getContactByPerson access will be placed in the Contact data access class.

Accordingly there are 3 Data Access classes: PersonData, AddressData and ContactData. The methods in each class correspond to a single stored procedure in the data base. The idea is to make the data-access as granular as possible. (Data Transaction and Concurrency issues are not handled in this version of the Data Access classes but the enterprise version should handle all data concurrency. Transaction handling should also be managed in the DALC layer.) Following the P&P recommendations, the DALC methods are all static. We will examine the AddressData class as an example:

using System;
using System.Collections;
using System.Data;
using System.Data.SqlClient;

using UIPABBE;
using Microsoft.ApplicationBlocks.Data;

namespace UIPABData
{
  public class AddressData
  {
   private static string con = DataConString.ConString();
   public static ArrayList getPersonAddresses(string id)
   {
     ArrayList list = new ArrayList();
     string proc = "getAddressesByPerson";
     SqlParameter[] sparams = new SqlParameter[]{
      new SqlParameter("@id", int.Parse(id))};
     DataSet ds = SqlHelper.ExecuteDataset(con, proc, sparams);
     foreach(DataRow dr in ds.Tables[0].Rows)
   list.Add(new Address((int)dr["ID"], dr["Street"].ToString(), 
     dr["Province"].ToString(), dr["Country"].ToString()));
     return list;
   }

   public static string insAddress(Address add, string id)
   {
     string proc = "insAddress";
     SqlParameter[] sparams = new SqlParameter[]{
      new SqlParameter("@personid", int.Parse(id)),
    new SqlParameter("@street", add.Street),
    new SqlParameter("@province", add.Province),
    new SqlParameter("@country", add.Country)};
      return Convert.ToString(SqlHelper.ExecuteScalar(
        con, proc, sparams));
    }

    public static void upAddress(Address add)
    {
  string proc = "upAddress";
  SqlParameter[] sparams = new SqlParameter[]{
   new SqlParameter("@id", int.Parse(add.ID)),
   new SqlParameter("@street", add.Street),
   new SqlParameter("@province", add.Province),
   new SqlParameter("@country", add.Country)};
  SqlHelper.ExecuteNonQuery(con, proc, sparams);
     }

  }
}

Pay attention to the following:

  • The DAAB and the business entity project is referenced. We also use the System.Data and the System.Data.SqlClient namespaces.
  • All the methods are public and static.
  • The data-access code in each method uses the SqlHelper class of the DAAB.
  • There a one method for each stored procedure. No methods call more than one stored procedure. (In more sophisticated DALC layers, a fa�ade or factories are built as wrappers around the DALC classes that directly call the DAAB. I normally create these factory classes or facades in separate projects in the DALC layer.)
  • The class has a private static string variable � this is the connection string and it is retrieved from a static method of a DataConString class.

You will notice two extra classes in the DALC layer: the ConstringSH and the DataConString. I have placed both these classes in the same .cs file i.e. ConStringSH.cs. . The reason I have done this is to demonstrate the use of the CMAB. (Initially I thought this was an overkill for a simple demo of the UIPAB, but on consideration, I am of the opinion that it important to begin using the App Blocks together right from the start.) The two extra classes are CMAB specific. The DataConString is a simple class whose properties represent the elements of the SQL authentication access string and whose ToString() method represents the SQL access string. The string values are read according to the CMAB configuration in the UI layer. (Note: never produce production code for data access strings the way I have - it is insecure and a recipe for disaster. I am doing it this way to give you a feel of CMAB without going into the security intricacies.)

THE PRESENTATION LAYER

Now that I have set up the Business and Data Layers, I can now concentrate on the Presentation Layer. The end user (and probably your managers and bosses) see and judge your application from the perspective of the Presentation Layer. From my experience, no matter how much I try talk myself and my team into saying that look and feel is not important, I have found that my projects are ultimately judged by the end-user experience, and they make their judgment on look, feel, intuitive work-flow and ease of use. As serious developers we might feel a bit cheated in having our work judged by what we think as the most inconsequential part of the application � the front end. But that is the way most of the world will judge us. The Layered Pattern can help us achieve our objective in this regard. It mandates that we develop our Business and Data layers independently from our front ends; it also envisages less change to these layers than to the Presentation layer. When we come to the Presentation Layer, we should not worry about business and data concerns; this will allow us to concentrate our skills, talent and time into developing pretty, intuitive and user-friendly user interfaces. Also, because the UI is independent of the other layers, we can easily change our UI�s without having to change our business and data access code.

The Presentation Layer is a sub-system in itself, with its own workflows, data requirements and business rules and processes in addition to look and feel. For example: Form A must launch Form B and From B must return us to the Main Form � this navigational route is workflow specific to the Presentation Layer. Without the UIPAB, we have to embed these UI specific business requirements into the UI code. As we will see, this is severely limiting and goes a long way to defeat the purpose of the Layered pattern, which we have so faithfully adopted in the Business and Data layers.

In the remainder of this article and the next article, I propose to do the following:

  • Present the required UI which will be a Windows Application.
  • Demonstrate how the Windows Application is developed without using the UIPAB i.e. embedding the UI business specific logic into the UI components.
  • Introduce the MVC (Model-View-Control) pattern which forms the conceptual foundation of the UIPAB.
  • Introduce the main objectives of the UIPAB, together with its most important components.
  • Refactor our Windows Application in a stage by stage approach using the UIPAB and showing how the new Windows application adheres to the Layered pattern.
  • Demonstrate how we can reuse our UI specific business code in a Web version of our Windows Version.

THE DEMO UI

The Demo UI elements can be found in the source code as UIPABWin1.proj - this application is not intended to work at this stage. The project comprises of 4 Windows forms: Form1, frmAddress, frmContact, frmList, frmPerson. Throughout all versions of the UI, we will be sticking to these 4 basic UI elements without changing their look or their constituent controls.

The UI work-flow is as follows:

  1. The application launches Form1 entitled UIPAB Win Demo. This form has a single button �Start Demo�. Clicking the button causes the application to retrieve a list of all persons from the persistent data store.
  2. The frmList file is now displayed. It has a ListBox control and two button controls: New Button and End. The list of persons retrieved from the data-store is displayed in the ListBox. If we want to add a new person to the list we click the New Person button, this will display frmPerson. If we want to edit a person already in the list, we double click that list item in the ListBox, this will also display the frmPerson. If we want to update our data store with the new or updated items in the list, we press the End button; this will initiate the data update process and will return the application to Form1.
  3. frmPerson has two TextBoxes for the Firstname and Surname of the specific Person. There are also two ListBoxes � the one displays a list of the Person�s addresses and the other a list of his contacts. There are three Buttons: Done, New Address and New Contact. To add a new address for the Person, we click the New Address Button and this displays the Address form. To update an existing address we double-click that address item in the address ListBox, this will display the Address form. The same procedure is used for adding or updating contacts. If we add or update the person�s Firstname and Surname, we do this in the appropriate TextBox. When we have finished working with the Person, we press the Done button; this will display the List form again with the updated or new person appearing in that form�s ListBox. Pressing Done in frmPerson does not update the persistent data source.
  4. The use of frmAddress and frmContact should be intuitive.

In UIPABWin1, I have hooked up all the UI events. These events will trigger and control the work-flow, and will be consistent throughout all the UI variations we will be examining.

FORM CONTROL EVENT DESCRIPTION
Form1 btnStart Click Retrieves list of persons from database. Causes frmList to display and form1 to hide.
frmList btnNew Click Displays the frmPerson to add a new person to the list.
btnEnd Click Sends the list to the database to be updated. Hides frmList and displays Form1.
lb DoubleClick Selects a specific person in the ListBox; displays frmPerson with the details of the selected person.
frmPerson btnAddress Click Displays frmAddress to add a new address to the person�s address list.
btnContact Click Displays frmContact to add a new contact to the person�s contact list.
lbAddress DoubleClick Selects a specific address from the person�s address list for update and displays frmAddress with the address�s details.
lbContact DoubleClick Selects a specific contact from the person�s contact list for update and displays frmContact with the contact�s details.
btnDone Click Adds the new or updated person back to the list of persons; returns control to frmList and dismisses frmPerson.
frmAddress btnDone Click Returns the new or updated address to the person�s address list; frmAdress is dismissed and control returns to frmPerson.
frmContact btnDone Click Returns the new or updated contact to the person�s contact list; frmContact is dismissed and control returns to frmPerson.

In summary UIPABWin1 does the following:

  • Defines the forms and their controls.
  • Defines the event-handlers for each control on each form.

As I move onto the demo version without the UIPAB, you will notice that I am forced to add a lot of code and extra procedures to the basic outline I have set up in UIPABWin1. When I do the version with UIPAB, you will notice that I return back to the basic skeleton code of UIPABWin1, and all I will be doing is adding a few lines of code in the event handlers.

THE UI WITHOUT UIPAB

UIPABWin2 is the demo version without the UIPAB. You will find it in the source code download as UIPABWin2.proj. UIPABWin2 is a fully working application. In fact, you will see when we demonstrate the UIPAB version of this application, that both versions work exactly the same and look exactly the same. The code and structure is vastly different.

Befoe you run UIPABWin2 make sure you configure the config. file with the correct SQL Data Access information.

In the config file locate the XMLConstring XML element. This element has a child element DataConString. Which in turn has the child elements: Server, UID, DB and PWD. You will have to enter the correct UID and PWD. The UID and PWD should reflect a valid SQL User name for SQL authentication to the UIPAB database.

UIPABWin2 has added references, code, new variables and methods to each form. The purpose of all these additions is to achieve the following:

  • Communicate with both the Business and Data Layers.
  • Manage the navigation and control flow from form to form.
  • Carry the necessary state required throughout the application.
  • Carry and create the necessary state that must be held by each specific form.
  • Manage the localized business rules, such as creating a new person and assigning the appropriate properties.

I don�t want to labour the point by examining all the code in UIPABWin2. For the purposes of illustration, I will deal with frmList only. You should examine the rest of the code in the other forms on your own, and you will understand the point I am trying to make.

First off note the project references:

  • Microsoft.ApplicationBlocks.ConfigurationManagement - this is required for data connection string handling. (I tried referenced the App Block where I thought it should be referenced i.e. in the DALC layer, but it didn�t work. It seems to me that the DALC layer is most appropriate location for referencing the App block and I don�t see anything in the documentation which says that an intermediate layer cannot have its own app.config file. All I can say is that the DALC did not find its app.config file and I was forced to reference CMAB in the UI. I am not sure if this because of my ignorance or a flaw in .NET � I would appreciate help in this regard.)
  • UIPABBE � the Business Layer is referenced directly in the UI.
  • UIPABData � the Data Layer is referenced directly in the UI.

The code in Form1�s Button click event handler is as follows:

private void btnStart_Click(object sender, EventArgs e)
{
   //Navigation Management embedded UI

   Form frm = new frmList(this);
   frm.Show();
   this.Hide();
}

This is navigational code. It is navigating the application from Form1 to frmList. I am passing Form1 to frmList; this is also for navigational purposes. When frmList is dismissed, it will now know to navigate back to Form1.

frmList has two class level variables:

//********************************

//State management in UI

private ArrayList list;
private Form1 frm;
//********************************

These are for state and navigational management.

frmList has two extra methods in addition to the event handlers.

private void bind()
{
   //Accesses DALC layer

   list = PersonData.getPersonsList();
   lb.DataSource = list;
}
//*********MODEL & WORKFLOW BEHAVIOUR*************    

internal void upPerson()
{
  CurrencyManager man = this.BindingContext[list] as CurrencyManager;
   if(man != null) man.Refresh();
}
//************************************************

The bind() method, makes a direct call to the Data Layer to retrieve the list of persons from the data store. The upPerson() method will be called from frmPerson when in the btnDone.Click event handler of frmPerson. In other words, frmPerson will have a reference of frmList passed to it.

The new Person button event handler in frmList looks like this:

private void btnNew_Click(object sender, EventArgs e)
{
//**********MODEL, WORK FLOW & STATE ********************

  Person person = new Person();
  person.ID = Guid.NewGuid().ToString();
  person.isNew = true;
  this.list.Add(person);
  Form frm = new frmPerson(this, person);
  frm.ShowDialog(this);
//******************************************************

}

In this code snippet, I am doing business processing, state management and navigation. I first create a new person object, assign it a new GUID and mark it as new � this is business processing embedded into the UI. I then add the new person to my class level ArrayList � this is state management embedded in the UI. Finally, I instantiate a new frmPerson and pass it a reference of the new person and frmList, and then display frmPerson � this is navigation and state management, again embedded in the UI. This method for a Layered fundamentalist is blasphemy.

When an existing person is selected in frmList�s ListBox, the event handler looks like this:

private void lb_DoubleClick(object sender, EventArgs e)
{
   if(lb.SelectedItems.Count == 0)return;
      Person person = (Person)lb.SelectedItems[0];
    //***************WORK FLOW & STATE

    if(!person.isNew)
    {
       if(person.Addresses.Count == 0)person.Addresses = 
         AddressData.getPersonAddresses(person.ID);
       if(person.Contacts.Count == 0)person.Contacts = 
         ContactData.getPersonContacts(person.ID);
    }
    Form frm = new frmPerson(this, person);
    frm.ShowDialog(this);
   //************************************

}

The first line checks whether we have selected an item in the list; if we have not we return from the method. The next line extracts the person object from the item selected. Both these lines of code belong in the UI. None of the rest of the code in this method should be in the UI.

I next check if the Person is not new; if he is not new, I check if I have retrieved his details from the data store; if I have not, I retrieve his contact and address details. This is business logic and data access logic code embedded into the UI.

I then instantiate a new frmPerson, pass it a reference of frmList and the extracted person object and navigate to frmPerson. This is state management and navigation embedded into the UI.

frmList�s btnEnd Click event handler is as follows:

private void btnEnd_Click(object sender, EventArgs e)
{
//***********USES DATA LAYER***************

  PersonData.upList(list);
  frm.Visible = true;
  this.Close();
//*****************************************      

}

The code first calls the Data Layer and initiates the crucial back end process of updating the data base. Next, I show the referenced Form1 form and close frmList. Business logic and navigation is here embedded in the UI.

CONCLUSION & WHAT�S NEXT

In this first article we have barely touched on the UIPAB. Instead, I have coded the Business and Data Layers which are going to be reused throughout. I have used a number of PP App Blocks in these layers, with the purpose of getting you used to using the App Blocks together from the start. I have given the basic UI elements and work-flow and the objective of the demo application. I then created a Windows Application Demo UI without using the UIPAB to demonstrate how domain logic and user process logic is embedded into the UI.

In the next article I will be examining the overall objective of the UIPAB and the basic UIPAB building blocks. The purpose will not be to give a thorough technical analysis (the App Block documentation does this better than I could) but, rather, to equip you with the basic knowledge and techniques to begin using the UIPAB. I will then refactor the Windows Demo from this article using UIPAB. Lastly, we will reuse the refactoring to build an identical Asp.Net Web UI.

In later articles I will delve into more features and details of the UIPAB.

I realize that this has been a particularly round-about, and long-winded way to introduce UIPAB. But, I think it should be introduced gradually. UIPAB is not for the feint hearted and if my goal is to make it the standard for all .NET UI development, I have to lay out may case carefully.

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