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

UIPAB Part 2 : MVC, UIPAB Essentails and Demo

0.00/5 (No votes)
3 May 2004 1  
Part 2 of 3 part series. Examines MVC pattern, UIPAB essentials and architecture; refactoring demo using UIPAB

Introduction

This article is the second in a series which sets out to demonstrate the vast benefits to be gained by utilizing the UIPAB to develop your .NET user interfaces. In the first article, I emphasized the importance of viewing the UIPAB within the context of Microsoft Patterns and Practices initiative. I pointed out that one of the corner-stones of PP was the Layered pattern. I spent most of the first article in building the Business and Data layers for a demo, and ended off by building the Presentation Layer by way of a WindowsForm application. The demo UI was build without the UIPAB. In this article, I will concentrate on the conceptual basis for UIPAB and more particularly the MVC pattern and how this pattern is implemented in UIPAB. I will then dwell on the most essential elements of the UIPAB and show how with this basic knowledge the WindowsForm application build in the previous article can be refactored using UIPAB. Finally, I will show how the refactored UI using UIPAB can be reused to create a Web UI as an ASP.NET application. The UIPAB UI�s I will be demonstrating use the bare minimum features of UIPAB. I think that if the essentials are mastered, it is much easier to begin using the more advanced features. The last articles in the series will refactor and extend my demonstration to make use of the rich-feature set provided by UIPAB.

MVC

The UIPAB is a version of the MVC pattern. A comfortable working knowledge of UIPAB is predicated upon an understanding and appreciation of MVC.

Martin Fowler introduces MVC as follows:

�Model View controller (MVC) is one of the most quoted (and most misquoted) patterns around. It started as a framework developed by Trygve Reenskaug for the Smalltalk platform in the late 1970s. Since then it has played an influential role in most UI frameworks and the thinking about UI design.� (Patterns of Enterprise Application Architecture � pg. 330)

Essentially, MVC splits the user interface into three distinct roles: Model, View and Controller � graphically presented as follows:

  • Model � an object that represents some information about the domain. The Model is a non-visual component that manages the UI processes, communications with other layers and UI data sub-systems.
  • View � represents the visual aspect of the UI. This is what the end-user sees. In the Windows world the View are the forms (Windows and Web) with their various controls. The view displays data to the end-user in an aesthetical, user-friendly way.
  • Controller � handles changes of information in the View. The controller receives user input from the View and passes it onto the Model. Alternatively, the Controller can update the View with data.

The dependencies and independencies of the MVC trinity are key to understanding MVC. The View (presentation) is separate from the Model. The View is also separate from the Controller. The model is independent of the View and the Controller. Both the View and the Controller is dependant on the Model. The Model is the dominant component and also the component that can be reused in changing UI scenarios. If a new UI is required, MVC mandates that we rebuild the Controller and most importantly the View. This being the case, it is imperative that both the View and the Controller be as light-weight as possible and contain as little UI logic as possible. Within the context of the Layered pattern is should be the Model and only the Model that interacts with the Business Layer.

Fowler summarizes the major advantage of MVC thus: �A key point in this separation is the direction of the dependencies: the presentation depends on the model but the model doesn�t depend on the presentation. People programming in the model should be entirely unaware of what presentation is being used, which both simplifies their task and makes it easier to add new presentations later on. It also means that presentation changes can be made freely without altering the model.� [Fowler � 331]

Fowler recommends that MVC be used in almost every UI scenario, saying that the only time it should not be used is in very simple systems where the model would have no real behaviour in it in any case. Whenever there is even a small amount of UI logic, MVC should be used. The clearest case I can think of when not to use MVC � is in a static HTML page which will never change i.e. the content is embedded into the page and the page does nothing but display a static image or text. In all other cases, MVC should be used.

MVC & PP

Predictably, MVC is at the heart of all UI patterns in .NET Enterprise Solution Patterns (ESP). There is extensive discussion of MVC in the PP literature.

In Enterprise Solution Patterns Using Microsoft .NET, MVC is introduced in Chapter 3 on Web Presentation Patterns (see especially pages 34 to 50). In Design and Implementation Guidelines for Web Clients MVC is discussed throughout; if you do not have time to read the entire guideline, read pages 22-25.

ESP defines the problem MVC is meant to address as: �How to modularize the user interface functionality of a Web application so that you can easily modify the individual parts?� ESP identifies two variations of MVC: a passive model and an active model. The passive model is when there is only one controller manipulating the model i.e. the controller modifies the model and then informs the view of the modification. In the passive scenario the model is completely independent of the view and thus the model cannot report changes of its state to be reflected in the view. In a Web situation � the browser (View) must be refreshed so as to retrieve the data from the Model. In the active scenario, the Model changes state independently of the Controller; here the Model notifies the View of any changes of state � a connected, real-time stock-ticker is an example of an active MVC scenario. The active MVC is a UI version of the Gang of 4 Observer Pattern.

ESP lists two important benefits of MVC: support for multiple views and easy accommodation of changes. The also two liabilities in using MVC: complexity and the problem of frequent updates. If done correctly (as in UIPAB) the latter liability is eliminated. Also, I don�t think MVC is complex; it is an entirely different way of doing UI�s and it is this paradigm shift, more than anything else, which makes it seem complex. But once the coin drops � you will have an epiphany � you will have made the paradigm shift, and I don�t think you will ever go back to developing UI�s without using MVC.

MVC & UIPAB

The documentation states the UIPAB objectives as follows:

  • Abstract all navigation and workflow code from the user interface.
  • Enable the same programming for different types of application (Web and Windows).
  • Remove all state management from the user interface.
  • Persist snapshot state across processes.

Although it is not clearly stated in the documentation, UIPAB is a type of MVC. I find it easy to understand UIPAB if I map the three MVC components to appropriate UIPAB elements. Reducing a .NET MVC UI to its barest bones would go something like this:

  • The Form (Windows or Web) and the form controls represent the View.
  • The Code behind the Form is the Controller. The Controller should contain only control-event handlers and control-format code (look, feel, color, size, position etc�) In the new version of .NET � we will be able to create partial forms and so will be able to separate control-format code (which should strictly speaking be part of the View) from control-event handler code which should be the only code in the Controller.
  • Everything else is the Model.

From a UIPAB perspective the essential elements in �everything else� in the Model are:

  • State management.
  • Navigational management (Workflow).
  • Persistence mechanisms.
  • UI specific business logic.
  • Communication avenues to the Business Layer or to the Service Agents or Service Interfaces.

Before dealing with the UIPAB architecture, I want to briefly elaborate on UIPAB Views, State and Workflow Management and Persistence.

TASK

A Task is best defined as a specific Use-Case. UIPAB Tasks constitute a number of forms participating in a navigation sharing the same task and the same controller. The demo application in the first article in this series is an application fulfilling a single task. Applications normally carry out a number of tasks in sequence. When designing a UI therefore it is important that you first conceptualize in a very clearly defined manner the various Tasks your UI must perform. The PP documentation recommends modeling these tasks in flow diagrams. UIPAB allows for the linking of tasks. Furthermore, UIPAB allows you to persist your tasks. Practically, a Task is more or less congruent to a single Workflow. UIPAB assigns a unique ID to each Task in the UI application � this ID is used for persistence. (I will not be covering persistence in this article.)

VIEWS

UIPAB V2 supports only ASP.NET and Windows UI applications. All UI forms participating in UIPAB must implement the IView interface. Each IView implementation has the following properties:

  • Controller � each view is associated with a Controller. This property returns the Controller for the view. (Controller is an unfortunate nomenclature because the Controller in actual fact is the Model and not the Controller in the MVC trinity).
  • ViewName � the name of the view as defined in the UIPAB configuration.
  • Navigator � each IView form participates in a UIPAB Navigation. This property returns the associated Navigator.
  • TaskID �Each Task is assigned a unique ID which can be retrieved from every View participating in the Task.

The App Block supplies three UIPAB IView implementations:

  • WebFormView � these are views derived from the ASP.NET Page in System.Web.UI.
  • WindowsFormView � these are views derived from System.Windows.Forms.Form.
  • WindowsFormControlView � this is a new addition introduced by version 2. These views are derived from System.Windows.Forms.UserControl. (I will not be dealing with this view in this article.)

Developers can build custom IView implementations. For almost all scenarios, it is recommended to use the provided IView implementations. What this means is that your WebForms inherit from WebFormView and not Page and your Windows Forms inherit from WindowsFormView and not System.Windows.Form.

NAVIGATION

Navigation is a work-flow. Essentially, it is the map of Views as they are activated in a determined order for a specific Task. (Using the WindowsFormControlView the elements on the map can be controls on the same view and the workflow is from control to constituent control which are also called �nodes�.) The UIPAB has a Navigator class which co-ordinates the navigation between the views in the task. The UIPAB provides 4 different types of navigational modes. The point is, no matter the type of navigation used, each Task must be associated with a Navigator and each Task can only begin when it launches its navigator. In fact, the first act of any task in run-time is launching the appropriate navigator.

In this article I will only be examining one type of navigator (the principle is the same for the other navigators). At this stage, suffice to know what the navigator types are and how they can be initiated:

The four UIPAB navigators are:

  • Graph � this navigator uses a navigational graph which is predefined in the UIPAB configuration according to a fixed template structure. We will be examining this structure in this article. The Graph is characterized by a start and end node. This navigator is launched by a call to the StartNavigationGraph static method of the UIPManager.
  • Open �unlike the Graph the Open navigator is not confined to the predetermined nodes set out at design time in the configuration file. The Open navigator navigates to the view specified (if that view exists). To launch the open navigator you must call StartOpenNavigationTask method of the UIPManager.
  • User Control � as its name suggests, this navigator is used to manage workflow from control to control. The navigator is only applicable to Windows controls and will not work with Web controls. Navigation is launched by calling the StartUserControlsTask static method of the UIPManager.
  • Wizard � is a special type of Graph navigator and is actually derived from GraphNavigator. Thus, this navigator also has a graph which is defined in the UIPAB configuration; the Wizard navigator is also launched by a call to StartNavigationGraph Like User Control navigator, the Wizard navigator will only work with a Windows UI. The Wizard implements the familiar Windows forms wizard based tasks with the predictable Cancel, Back, Next and Finished commands.

STATE

State is important from a UI perspective in two ways. Firstly � there is some form of state that is derived from the Business Layer (or Data Layer) which must be maintained on the UI � this is true for both Windows and Web environments. Secondly, inevitably state must be maintained between views in the UI � in most cases each form is transferring state to the form that follows it in the navigational sequence.

In the traditional way of doing UIs (an example of which I gave in the first article of this series) � the UI state management process is awkward and cumbersome to say the least. State is either embedded into the UI views (the crude mode) or some sort of Mediator is used where state management is coordinated.

UIPAB manages state as it does navigation i.e. on per Task basis. Which is to say: the views that participate in the Task, share the same Navigator, Controller and state-management. In the old approach, each view more or less is responsible for managing its own State. With UIPAB State management is centralized.

The UIPAB has a generic State class which should fulfill most UI State management requirements. This class implements ISerializable and supports the StateChanged event which provides Observer type functionality to the UI using .NET delegates. The State class hosts an internal hash table into which most data types can be added. Essentially, these data-types are your state. All data types added to the State class�s inner hash-table must be marked as Serializable or implement ISerializable. The important State class methods are intuitive, given that the class contains a hash-table. The important methods are:

  • Add
  • Remove
  • Contains
  • Accept � this method is required when state is to be persisted.
  • Save � this method saves state to the persistence provider.
  • CopyTo

PERSISTENCE

The idea behind UI persistence is best explained by the �snapshot� metaphor. As the UI progresses through the Task there might be occasions where the User wishes to persist the current state. This might be required because the user has not the relevant information to complete the UI task, or he might want an audit of UI processes for later analysis. The point is � at the moment in time of persistence � a �snapshot� almost like a photograph is taken of the current UI sate and persisted.

The coding of UI state persistence can be a cumbersome and sometimes thankless task. This is all the more true when you want to build a flexible persistence architecture that can cater for a variety of persistence providers. Also, it might be necessary to retrieve persistence in one mode which was saved in another mode. For example: you might begin an online purchase scenario on the Web and resume it using an in-house windows intranet application.

Fortunately, UIPAB has a sophisticated, feature-rich, centralized and easy to use persistence management element. I will not be dealing with persistence in this article. Suffice to say at this stage that UIPAB supports six persistence out-of-the-box providers.

UIPAB ARCHTECTURE

The glue that holds the UIPAB together is the UIPMananger class. The UIPManager has been designed so as to have a simple, easy to learn API which consists of static methods that launch a particular navigator. It is the UIPManager that acts behind the scene to co-ordinate the various UIPAB elements and their association with the your front-end application.

Configuration is vital to setting up your UIPAB application. I have found that configuration is difficult to grasp in the abstract and makes more sense when applied to a specific example. Also, you will find that if there are errors or exceptions in your UIPAB applications, it is invariably because you have done something wrong in your UIPAB configuration. I will thus deal with configuration when I examine the UIPAB example.

HOW TO BUILD UIPAB UIs

At first glance the UIPAB might seem daunting in its complexity and its many layers and classes. In actual fact, the work required to build a UIPAB UI is minimal � the real hard work is the understanding.

Below is the UIPAB architectural diagram supplied with documentation. Basically, the only work you as a developer have to do is:

  1. Create your UI Forms.
  2. Create your UIPAB controller.
  3. Hook up the UI Form events with the controller.
  4. Configure your applications config file for UIPAB.

All the rest is taken care of for you by the Application Block.

REFACTORING THE WINDOWS EXAMPLE

I will now begin refactoring the Windows application I presented in the first article. Recall, that the application consisted of a single Task which was a person management console. I retrieved the list of person, disconnected, manipulated the list off line, and finally reconnected to the back-end making the necessary persistence changes.

The first thing I do is create a WindowsFroms project in the UI sub-layer of the Presentation Layer. This project appears in the source code as UIPABWin4.proj. In effect � the project should look exactly like the original Windows Application we used i.e. UIPABWin1.proj � which consisted of five forms and stub code: Form1, frmList, frmPerson, frmAddress, frmContact and their constituent controls and relevant event handlers. (Refer to article 1 for the set up instructions.) The project must reference the UIPAB Version 2 and CMAB (Config Management App Block). I will leave UIPABWin4 for now and will be returning to it after I have coded the UIPAB controller for this application.

UI PROCESS LAYER

Create a new class library in the UI Process layer of the Presentation Layer. I have called the project UIPABController.proj. (Note: despite the name controller, this controller is really the Model for your UI). I reference the UIPAB v2 Application Block and the EMAB (Exception Management Block) in my project. Also, my UI will interact with the Presentation and Data Layers through the intermediary of UI Process. I therefore reference UIPABBE (the Business Layer) and UIPABData (the Presentation Layer) (Article 1 in this series explained how to set up those layers).

I am now going to code a single class which I have called UIController. This class inherits from the UIPAB ControllerBase class. It is this class which is at the heart of my UIPAB UI. I have created the class in a layer all of its own. The class is independent of my UI (the UIPABWin4 project). As you will shortly see, my UIController, because it is independent, will allow for its reuse.

I want my UIController to take care of:

  • UI State Management.
  • UI workflow.
  • UI Domain logic.

Because the UIPAB does all the plumbing for workflow and state, all my UIController will do in these arenas are:

  • For State � add and present sate data. (The complicated mechanics of serializing, maintaining and persisting � is taken care of by UIPAB).
  • For workflow � indicating which navigational node to advance to in response to a UI action. (The actual work of finding the next view, launching and activating it and creating its state is done for you by UIPAB).

I find it convenient to divide my ControllerBase classes such as UIController into three main regions:

  • Constructor
  • Controller API
  • Domain Logic

I will now step through the UIController code in detail �as this the most important code block in the UIPAB example.

The class inherits from BaseController and uses the UIPAB, the EMAB, UIPABBE and UIPABData.

using System;
using System.Collections;

using Microsoft.ApplicationBlocks.UIProcess;
using Microsoft.ApplicationBlocks.ExceptionManagement;

using UIPABBE;
using UIPABData;

namespace UIPABController
{

  public class UIController: ControllerBase
}

The contractor takes a Navigator object or name as its parameter:

public UIController(Navigator nav):base(nav){}

The Controller API consists of a number of public methods and properties. I divide my Controller API into two regions: State (which consist of property getters only) and Navigation (which are public methods).

UIController has four properties corresponding to the four entities in my application (which also correspond to the four forms in my UI).

  • UIList � represents the list of persons and corresponds to UIPABWin4.frmList.
  • UIPerson - corresponds to UIPABBE.Person and UIPABWin4.frmPerson.
  • UIAddress � corresponds to UIPABBE.Address and UIPABWin4.frmAddress.
  • UIContact - corresponds to UIPABBE.Contact and UIPABWin4.Contact.

The state property code looks like this (note the use of the UIPAB State class).

#region STATE

public ArrayList UIList
{
  get
  {
    if(State["PersonList"] == null)
    State["PersonList"] = PersonData.getPersonsList();
    return State["PersonList"] as ArrayList;
  }        
}
public Person UIPerson
{get{return State["Person"] as Person;}}

public Address UIAddress
{get{return State["Address"] as Address;}}

public Contact UIContact
{get{return State["Contact"] as Contact;}}

#endregion

The navigation methods correspond to UI actions. I prefer a granular approach. That is to say: one navigational method for each UI navigational trigger. In large navigational schemas this might mean a plethora of navigational methods, but the methods remain small, clean and easy to maintain. Also, when a navigational event is triggered, the Controller class must be informed of the navigational value. I have seen this value passed from the UI to the Controller (indeed this is what the supplied examples do). This approach makes the controller dependent on the UI and so is something we have to avoid. What I do is associate navigational values with methods in the Controller itself � thereby guaranteeing complete independence to my Controller.

The following is a map of UI events to UIController Navigational methods (keep in mind the workflow diagram in Article 1 for reference):

VIEW CONTROL EVENT CONTROLLER NAV METHOD
frmList btnNew_Click toNewPerson()
lb_DoubleClick toExistingPerson(string id)
btnEnd_Click updateList()
frmPerson btnAddress_Click toNewAddress()
lbAddress_DoubleClick toExistingAddress(string id)
btnContact_Click toNewContact()
lbContact_DoubleClick toExistingContact(string id)
btnDone_Click updatePerson()
frmAddress btnDone_Click updateAddress()
frmContact btnDone_Click updateContact()

The navigational methods follow a pattern. There are three types of methods for each entity or view i.e. toNew, toExisting and update. Each navigational method calls a private domain logic method and then makes a call to the Nav method, which call the Navigate method of the base class and passes the navigation value.

public void Nav(string navValue){this.Navigate(navValue);}

I give the Person navigational methods here by way of example:

public void toNewPerson()
{
   newPerson();
   Nav("toPerson");
}
public void toExitingPerson(object obj)
{
  if(obj is Person)
    getPerson(((BaseBE)obj).ID);
  else
    getPerson(obj.ToString());
  Nav("toPerson");
}

public void upddatePerson()
{
  State["PersonList"] = this.UIList;
  State.Remove("Person");
  Nav("toList");
}

Pay attention to the following:

  • There is a clear separation between domain logic and navigational logic. Thus when I have to use domain logic in the navigational call (see toNewPerson() for example), I abstract that logic out into a new method (which is private) newPerson().
  • Navigation values are hard-coded into the navigational methods i.e. they are embedded into the UI Process and not UI.
  • State is set in the UIController and not by the UI. This is why all my UIController properties are only getters and not setters. State management must be centralized in the UI Process layer and NOT the UI Layer.

The remainder of the class consists of UI Process Domain logic. These are private methods which are basically helper methods to the UIController API methods and properties. In this particular application the UI Process Domain logic is divided into two distinct types:

  • Get methods - one method for Person, Contact and Address respectively. These methods take the entity�s ID and searches for that entity in the list maintained by the UIList property. These methods correspond to the toExisting() navigational method. They locate the existing entity in the list and then set the state for that entity. For example getPerson(string id) � retrieves the existing person from the list and sets the State[�Person�] to that person for use by the frmPerson. Pay attention also to the fact that when necessary the Data Layer is called from these private methods. In other words, the UI is isolated from and ignorant of the applications other layers.
    private void getPerson(string id)
    {
       Person person = null;
       foreach(Person p in UIList)
       {
        if(p.ID == id)
        {
        person = p;
        if(!p.isNew)
        {
         if(p.Addresses.Count == 0)p.Addresses = 
                  AddressData.getPersonAddresses(p.ID);
         if(p.Contacts.Count == 0)p.Contacts = 
                  ContactData.getPersonContacts(p.ID);
        }
        break;
         }
      }
      State["Person"] = person;
    }
    
  • New methods � again there is one new method for each entity i.e. newPerson, newContact and newAddress, each corresponding to the toNew() navigational method. Thus, when we have to navigate toNewPerson(), newPerson() is called. The method constructs the new Person entity and inserts this new entity into State where it will be available to the View which needs it (frmView).
    private void newPerson()
    {
      Person person = new Person();
      person.ID = Guid.NewGuid().ToString();
      person.isNew = true;
      UIList.Add(person);
      State["Person"] = person;            
    }
    
    

That is basically all there is to Controller. Compile it and return to the UI layer and UIPABWin4 project.

FINAL REFACTOR OF UI PROJECT

Back to UIPABWin4 and what remains to do is the following:

  • Make sure that all my views inherit from WindowsFormView.
  • Hook up navigation code with UIController.
  • Configure the config file.

Firstly, reference the UIPABController project. Our views need a handle to UIController. The way I propose to do this is different from the way demonstrated in the examples with the UIPAB � however I think this way is more elegant.

I create a new WindowsForm and call its baseForm which will inherit from WindowsFormView and will use the UIPAB and UIPABController:

using System;
using System.Drawing;
using System.ComponentModel;
using System.Windows.Forms;

using Microsoft.ApplicationBlocks.UIProcess;
using UIPABController;

namespace UIPABWin4
{
     public class baseForm : WindowsFormView
     {}
}

All implementations of the UIPAB IView interface had a Controller property which returns the controller associated with the View. I will modify the Controller property and cast it as UIController in a protected property called TaskController.

protected UIController TaskController{get{return 
  (UIController)Controller;}}

I now go to each view (WindowsForm) participating in the UIPAB task and make each WindowsForm inherit from baseForm. This way, my forms all inherit from WindowsFormView and have access to the TaskController property. In the UIPABWin4 � this is done to the following forms: frmList, frmPerson, frmAddress and frmPerson.

public class frmPerson : baseForm
{ �}

Each view has a form load event handler where the other event handlers are hooked up. The form load event handler also binds the necessary state to the controls in the form.

private void frmPerson_Load(object sender, EventArgs e)
{
  TaskController.State.StateChanged +=
    new Microsoft.ApplicationBlocks.UIProcess.State.
      StateChangedEventHandler(State_StateChanged);
  btnAddress.Click +=new EventHandler(btnAddress_Click);
  btnContact.Click +=new EventHandler(btnContact_Click);
  btnDone.Click +=new EventHandler(btnDone_Click);
  lbAddress.DoubleClick +=new EventHandler(lbAddress_DoubleClick);
  lbContact.DoubleClick +=new EventHandler(lbContact_DoubleClick);

  txtFirst.Text = TaskController.UIPerson.Firstname;
  txtLast.Text = TaskController.UIPerson.Lastname;
  lbAddress.DataSource = TaskController.UIPerson.Addresses;
  lbContact.DataSource = TaskController.UIPerson.Contacts;
}

Pay attention to the following:

  • I have subscribed to the StateChanged event of the State object in my UIController. I will be updating my controls as and when the relevant state changes.
  • I bind the controls on the form to the correct state in my UIController. Initially, I placed this code in the form�s constructor and the form crashed when it was activated. In other words, you can only access UI State on the View�s controller once the View (in this case the form) has loaded; hence the binding code is placed in the form load event.
  • Initially, I also bound my controls using data bindings i.e. DataBinding.Add() - this did not work with UIPAB. It seems that the currency manager is supposed to be serialized and so you can�t use DataBinding.Add() with State data attributes. (I might be making a mistake here and will welcome enlightenment.)

Now all that remains is to code the event handlers in each form. The code here is simple, short and intuitive. I give the frmPerson event handlers as an example:

private void btnAddress_Click(object sender, EventArgs e)
{
   TaskController.toNewAddress();
}
private void btnContact_Click(object sender, EventArgs e)
{
  TaskController.toNewContact();
}
private void btnDone_Click(object sender, EventArgs e)
{
  TaskController.UIPerson.Firstname = txtFirst.Text;
  TaskController.UIPerson.Lastname = txtLast.Text;
  TaskController.upddatePerson();
  this.Close();
}
private void lbAddress_DoubleClick(object sender, EventArgs e)
{
  if(lbAddress.SelectedItems.Count == 0)return;
  TaskController.toExistingAddress(lbAddress.SelectedItems[0]);
}
private void lbContact_DoubleClick(object sender, EventArgs e)
{
  if(lbContact.SelectedItems.Count == 0)return;
  TaskController.toExistingContact(lbContact.SelectedItems[0]);
}

Pay attention to the following:

  • The Even handlers� code is a simple method call to the TaskController property of the view.
  • I dismiss the form in the btnDone_Click event handler. If I could have used data-binding, I would not have to re-read the values from the form into the State in my UIController.

The last event handler handles the state changed event of the State object. This handler updates my various bound list-boxes when the appropriate state changes i.e. when I change or add a new address or contact in this example. What we have here is a type of Observer pattern implementation using delegates.

private void State_StateChanged(object sender, 
   StateChangedEventArgs e)
{
    if(TaskController.UIPerson != null)
    {
    CurrencyManager man1 = BindingContext[
        TaskController.UIPerson.Addresses] as CurrencyManager;
    if(man1!=null)man1.Refresh();
    CurrencyManager man2 = BindingContext[
        TaskController.UIPerson.Contacts] as CurrencyManager;
    if(man2!=null)man2.Refresh();
    }
}

Pay attention to the following:

  • The State Changed event will not work in this way in a disconnected environment such as the Web.
  • In my UIController class � when I retrieve a new entity or an existing one, I explicitly change the state object. The last line in getPerson(string id) and newPerson() on the UIController class is:
    State["Person"] = person;
    

This is to guarantee that the state changed event is triggered.

What is left is to launch the UIPAB and I do this in the btn_Click event handler of Form1. Note that Form1 does not participate in the Task and when you see the configuration, you will notice it is also not configured. Because I am using the Graph navigator the UIPAB initialization is as follows:

UIPManager.StartNavigationTask("Nav");

CONFIGURING THE APP.CONFIG FOR UIPAB

All I have to do now is configure the App.config file of UIPABWin4.proj. A word of warning, and I say this from bitter experience: if something is not working in your UIPAB app, it is probably because you have incorrectly configured the UIPAB app.config.

I will take this opportunity to issue a challenge to all of my readers (and to Microsoft): the App Blocks rely heavily on configurations to the app.config. The more App Blocks you use, the more your app.config will become unwieldy, the more you are guaranteed to make a mistake which sometimes takes hours to detect. The App Block configurations follow a fixed schemas with pre-defined lists of possible attributes and elements. The challenge is to write an application that will allow you � wizard like � to create app.config files for each App Block. (I am working on this at the moment � just to help me from continually mistyping or forgetting a quotation mark or an attribute template in my App Block app.configs.)

The first configuration is authoring a <section> element in the app.config�s <configSections> element. This <section> element is mandatory and is standard � you can cut paste it in every UIPAB configuration you ever author. The name of the section is �uipConfiguration�.

<configuration>
   <configSections>
     <section name="uipConfiguration"                 
      type="Microsoft.ApplicationBlocks.UIProcess.UIPConfigHandler,
      Microsoft.ApplicationBlocks.UIProcess,
      Version=1.0.1.0,Culture=neutral,PublicKeyToken=null"/>
    </configuration>
</configSections>
        

The rest of the configuration occurs in the <uipConfiguration> element which is a child element of <configuration>. <uipConfiguration> in its simplest form has three elements: <objectTypes>. <views>, <navigationGraph>.

<configuration>
   <configSections>
   <section name="uipConfiguration"                
 type="Microsoft.ApplicationBlocks.UIProcess.UIPConfigHandler,
Microsoft.ApplicationBlocks.UIProcess,
 Version=1.0.1.0,Culture=neutral,PublicKeyToken=null"/>
   </configuration>

  <uipConfiguration>        
      <objectTypes>
            
      </objectTypes>

      <views>
        
      </views>

      <navigationGraph>

      </navigationGraph>
  </uipConfiguration>

</configSections>

        

<objectTypes> must define four types each with a name and a type attribute:

  • <iViewManager> - either WindowsFormView or WebFormsView.
  • <State> - in most cases the State object of the UIPAB.
  • <Controller> - This is the only object which is not used from the UIPAB assembly. The namespace and assembly must be that of the BaseController class implementation. In my example: UIPABController.UIController,UIPABController.
  • <statePersistenceProvider> - the persistence provider utilized. (I will not be using it in the example in this article).

The <views> element list each view in the Task within its own <view> element. Each view is assigned a name; the type is the full-namespace and assembly of the actual windows form. Also, each <view> must be associated with a controller as defined in <objectTypes>. Notice that on my List <view> and Person <view> I have used the optional �stayOpen� attribute which I have set to true. (This is not the time to get into the details � but the UIPAB behaves unpredictably when StateChanged event is called in forms which do not have �stayOpen� set as true. (I admit � it might a misunderstanding on my part � and I would welcome assistance.)

The <navigationGraph> consists of a number of attributes and a number of <node> elements. These <nodes> are navigational nodes and essentially map the workflow scenarios for each view. The <navigationGraph> must be assigned a name; it is this name that is referenced when the application launches the UIPAB for this specific Task. The other <navigationGraph> attributes are:

  • startView � this is the first node (view or form) in the workflow.
  • State � this is the state object associated with this Task; the state object must be declared in<objectTypes>.
  • statePersist � the persistence provider � which must also be predefined in<objectTypes>.
  • <iViewManager> - again must predefined in <objectTypes>.

Each <node> is associated with a <view> as defined in <views>. Each navigational route in the view (corresponds to the event handlers in my example) must have a value - �navigateValue� - which is called in my UIController navigational methods; and a destination (�view�) � signifying which view must be activated when the navigational method is called.

REUSING CONTROL FOR ASP.NET APP

I will very quickly show you how to reuse your UI Process Layer i.e. the UIController in a Web version of the my UIPABWin4. The source code for the web version is in the download as UIPABWeb.proj which is an ASP.NET application.

I am not going to dwell on the code of the web application because it is exactly the same as the code for the windows application � with a slight modification to cater for the Web controls. Indeed, this is an extreme example where not only is the UI Process code reused, but the actual UI code is the same.

UIPABWeb has the same references: UIPAB, MCAB and UIPABController.

UIPABWeb has the exact same number of views and I have even named them same: Form1, frmList, frmPerson, frmAddress and frmContact. There is also a baseForm which here inherits from WebFormView and not WindowsFormView. frmList, frmPerson, frmAddress and frmContact all inherit for baseForm and all have the same event handlers as the windows application. In fact, if you examine the code in the event handlers, even the code is the same as in the windows application with slight variations to cater for the web controls. The frmPerson is given here as an example:

private void Page_Load(object sender, System.EventArgs e)
{
   btnContact.Click +=new EventHandler(btnContact_Click);
   btnAddress.Click +=new EventHandler(btnAddress_Click);
   btnDone.Click += new EventHandler(btnDone_Click);
   grdContact.ItemCommand +=new DataGridCommandEventHandler(
     grdContact_ItemCommand);
   grdAddress.ItemCommand +=new DataGridCommandEventHandler(
     grdAddress_ItemCommand);

    if(!Page.IsPostBack)
    {
      txtFirst.Text = TaskController.UIPerson.Firstname;
      txtLast.Text = TaskController.UIPerson.Lastname;
      grdContact.DataSource = TaskController.UIPerson.Contacts;
      grdAddress.DataSource = TaskController.UIPerson.Addresses;
      this.DataBind();
    }
}
private void btnContact_Click(object sender, EventArgs e)
{
   TaskController.toNewContact();
}
private void btnAddress_Click(object sender, EventArgs e)
{
  TaskController.toNewAddress();
}
private void btnDone_Click(object sender, EventArgs e)
{
  TaskController.UIPerson.Firstname = txtFirst.Text;
  TaskController.UIPerson.Lastname = txtLast.Text;
  TaskController.upddatePerson();
}
private void grdContact_ItemCommand(object sender, 
  DataGridCommandEventArgs e)
{
  DataGridItem item = (DataGridItem)e.Item;
  TaskController.toExistingContact((item.Cells[0].Text));    
}
private void grdAddress_ItemCommand(object sender, 
  DataGridCommandEventArgs e)
{
  DataGridItem item = (DataGridItem)e.Item;
  TaskController.toExistingAddress((item.Cells[0].Text));    
}

Form1 initiates the UIPAB the same way as the corresponding form in the windows application. Certain modification must be made to the configuration:

  • The <iViewManager> node of the <objectTypes> has a attribute of type WebFormViewManager (not WindowsFormViewManager).
  • The various view types in the <views> node are referenced by their .aspx identity (unlike in the windows application where we actually have to make full reference including Namespace, assembly name, version etc�).
    <views>
    <view name="Address"
          type="frmAddress.aspx"
          controller = "Demo"/>
    <view name="Contact"
          type="frmContact.aspx"
          controller = "Demo"/>
    <view name="Person"
          type="frmPerson.aspx"
          controller = "Demo"/>
    <view name="List"
          type="frmList.aspx"
          controller = "Demo"/>            
    </views>
    

Now I dare you to tell me that that is not code-reuse in the truest sense of the term.

CONCLUSION & WHAT�S NEXT

In this article I have shown how the UIPAB is a type of MVC pattern and why the MVC should be the standard architecture for all UIs. I then took you through a whirlwind tour of UIPAB, highlighting the essential elements and general architecture, trying to show how UIPAB corresponds to MVC. (The App Block comes with a detailed under-the-hood documentation which is not necessary to begin using UIPAB but should, nevertheless, be studied to get a full understanding of the mechanics and the features.)

Using the Presentation and Data Layers, and general UI elements and work flows I designed in the first article, I then refactored that application to use UIPAB. I tried to demonstrate a structured approach to designing UIPAB. Admittedly, the example was simple � using only a single Task and no State persistence. However, I believe the example supplies you with the conceptual and practical understanding which will make mastering the advanced features easy. The hard part is getting the fundamentals right.

Lastly, I showed you how easy it is to reuse UIPAB code in a completely different environment, converting my Windows Application to an ASP.NET application, reusing or duplicating most of the code.

In the last and final article in this series, I will expand on our example by including more tasks, transitions, persistence and different navigational scenarios. I will also briefly touch on UIPAB customization.

Reference

I am have tried to upload my new credentials to CodeProject but am battling. If you want to contact me - have a look at my website www.pdev.co.za or email me at ayal@pdev.co.za>

  • Article 1 in the series is here.
  • My first three articles on Patterns and Practices are all available here. In them, I try to summarize PP in the broader context and try to explain the Layered pattern. There is also a section of the site devoted for patterns with urld references for all the app blocks.
  • UIPAB VB2 can be downloaded here.
  • CMAB can be downloaded here.
  • DAAB can be downloaded here.
  • EMAB can be downloaded here.
  • The gotdotnet Visual Studio .NET PP Layered Policy Template can be downloaded here. I can't seem to source the original URL for this one.

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